From ec8fd5cc53db1fd5ac1aecc114e96bbaa9995785 Mon Sep 17 00:00:00 2001 From: S1 dev subagent Date: Mon, 28 Sep 2026 22:01:55 +0800 Subject: [PATCH] refactor(tui): expose adapter/stream/events/auth-storage subpath exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slice S1 of the webui runtime-first migration. Closes the only hard gap identified in the design: `@minimax/code` (the @mavis/tui projection) shipped only `./package.json` in `exports`, so the adapter surface that the future in-process runtime host needs was locked inside the package and webui had to go through ACP. Subpath exports now resolve to source under packages/tui/src via the existing workspace-source resolver hook (no file moves, per the source-sync discipline): ./runtime-adapter -> src/runtime/adapter.ts (TuiRuntimeAdapter + port types) ./runtime-stream -> src/runtime/stream-events.ts (projectTuiSessionStreamFrame + frames) ./runtime-events -> src/runtime/event-normalizer.ts (normalizeTuiRuntimeEvent) ./auth-storage -> src/auth/storage.ts (readCliAuthContext + projection helpers) TuiRunCoordinator is intentionally left for S4 (runtime-chat.js) — it is not in the §2.5 'A 推荐' table, only mentioned in §0 prose; S1 is the hard-gap slice, not the full surface. tsconfig.standalone.json paths and release/public-source.json are regenerated, not hand-edited. Verification: a new webui-side node:test (packages/webui/test/lib/tui-runtime-adapter-import.test.js) imports `@minimax/code/runtime-adapter`, constructs TuiRuntimeAdapter with a CliService stub, and asserts listSessions returns the normalised TuiSession array. Test fails with ERR_MODULE_NOT_FOUND when the subpath is removed (mutation-verified in the worktree). node_modules symlink created for the worktree is untracked and ignored by source-inventory. --- packages/tui/package.json | 6 +- .../lib/tui-runtime-adapter-import.test.js | 103 ++++++++++++++++++ release/public-source.json | 1 + tsconfig.standalone.json | 12 ++ 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 packages/webui/test/lib/tui-runtime-adapter-import.test.js diff --git a/packages/tui/package.json b/packages/tui/package.json index 5d6214cc8..3224a4335 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -5,7 +5,11 @@ "description": "Minimax Code CLI and TUI product entry.", "type": "module", "exports": { - "./package.json": "./package.json" + "./package.json": "./package.json", + "./runtime-adapter": "./src/runtime/adapter.ts", + "./runtime-stream": "./src/runtime/stream-events.ts", + "./runtime-events": "./src/runtime/event-normalizer.ts", + "./auth-storage": "./src/auth/storage.ts" }, "engines": { "node": ">=22.19 <23 || >=24.2 <27" diff --git a/packages/webui/test/lib/tui-runtime-adapter-import.test.js b/packages/webui/test/lib/tui-runtime-adapter-import.test.js new file mode 100644 index 000000000..e70f307ba --- /dev/null +++ b/packages/webui/test/lib/tui-runtime-adapter-import.test.js @@ -0,0 +1,103 @@ +// webui/test/lib/tui-runtime-adapter-import.test.js +// S1 webui-side smoke test: the webui can construct a `TuiRuntimeAdapter` +// with a stubbed `CliService` and call `listSessions` end-to-end, importing +// the adapter through the new `@minimax/code/runtime-adapter` subpath export +// (the public symbol was previously locked inside `@minimax/code` because +// the package only exported `./package.json`). +// +// This test is the tripwire for the runtime-first migration's first slice: +// webui must be able to reach the adapter surface without going through the +// ACP protocol layer. If the export goes missing again, the import resolution +// fails before the constructor even runs, so a "module exists" assertion +// would not be enough — we drive the call through and observe the +// normalisation result. + +import { test, describe } from "node:test"; +import assert from "node:assert/strict"; + +// Importing through the public subpath forces the resolver hook in +// packages/webui/server/lib/workspace-source-hooks.mjs to apply the +// exports-map rewrite; a missing export causes ERR_MODULE_NOT_FOUND here, +// which is the failure we want to see when the export is removed. +const adapterModule = await import("@minimax/code/runtime-adapter"); +const { TuiRuntimeAdapter } = adapterModule; + +// Minimal CliService stub: listSessions is the only method exercised by this +// test, and the adapter's `TuiSessionAccess` only forwards through that one +// call. Casting via `as unknown as CliService` keeps the cast local and +// obvious, matching the pattern used in packages/tui/test/unit/tui-runtime-adapter.test.ts. +function makeStubCliService(sessions) { + return { + listSessions: async (req) => { + return { + sessions: sessions.map((session) => ({ + sessionId: session.sessionId, + agentName: req.name ?? "mavis", + title: session.title ?? null, + workspaceDir: session.workspaceDir ?? null, + sessionType: session.sessionType === "branch" ? 0 : 1, + sessionKind: 1, + archived: false, + visibility: "visible", + status: { statusType: 0 }, + createdAt: session.createdAt ?? 1_700_000_000_000, + updatedAt: session.updatedAt ?? 1_700_000_000_000, + interactionMode: 0, + memoryPolicy: { + recallEnabled: false, + writeEnabled: false, + recallLocked: false, + }, + frameworkType: null, + isDefaultWorkspace: false, + })), + hasMore: false, + }; + }, + }; +} + +describe("webui can import the runtime adapter through @minimax/code/runtime-adapter", () => { + test("TuiRuntimeAdapter is exported as a constructable class", () => { + assert.equal( + typeof TuiRuntimeAdapter, + "function", + "@minimax/code/runtime-adapter must export TuiRuntimeAdapter as a constructable class", + ); + }); + + test("listSessions returns the normalised TuiSession array from the stub", async () => { + const stub = makeStubCliService([ + { + sessionId: "stub-1", + title: "first stub session", + workspaceDir: "/tmp/webui-stub-1", + sessionType: "root", + }, + { + sessionId: "stub-2", + title: "second stub session", + workspaceDir: "/tmp/webui-stub-2", + sessionType: "branch", + }, + ]); + const adapter = new TuiRuntimeAdapter( + /** @type {import('@mavis/local-runtime-v2/cli-service').CliService} */ ( + /** @type {unknown} */ (stub) + ), + ); + + const sessions = await adapter.listSessions(); + + assert.ok(Array.isArray(sessions), "listSessions must resolve to an array"); + assert.equal(sessions.length, 2); + assert.deepEqual( + sessions.map((session) => session.sessionId), + ["stub-1", "stub-2"], + ); + assert.equal(sessions[0].title, "first stub session"); + assert.equal(sessions[0].sessionType, "root"); + assert.equal(sessions[1].sessionType, "branch"); + assert.equal(sessions[0].workspaceDir, "/tmp/webui-stub-1"); + }); +}); \ No newline at end of file diff --git a/release/public-source.json b/release/public-source.json index 81efcc8dc..294a7a2b2 100644 --- a/release/public-source.json +++ b/release/public-source.json @@ -3579,6 +3579,7 @@ "packages/webui/test/lib/static.test.js", "packages/webui/test/lib/transcript-sync-wedge.check.mjs", "packages/webui/test/lib/transcript.test.js", + "packages/webui/test/lib/tui-runtime-adapter-import.test.js", "packages/webui/test/lib/upload.test.js", "packages/webui/test/lib/usage.check.mjs", "packages/webui/test/lib/workspace-containment.check.mjs", diff --git a/tsconfig.standalone.json b/tsconfig.standalone.json index 5af7034ee..ca8e0286d 100644 --- a/tsconfig.standalone.json +++ b/tsconfig.standalone.json @@ -376,9 +376,21 @@ "@mavis/system-reminder": [ "./packages/agent-modules/system-reminder/src/index.ts" ], + "@minimax/code/auth-storage": [ + "./packages/tui/src/auth/storage.ts" + ], "@minimax/code/package.json": [ "./packages/tui/package.json" ], + "@minimax/code/runtime-adapter": [ + "./packages/tui/src/runtime/adapter.ts" + ], + "@minimax/code/runtime-events": [ + "./packages/tui/src/runtime/event-normalizer.ts" + ], + "@minimax/code/runtime-stream": [ + "./packages/tui/src/runtime/stream-events.ts" + ], "@minimax/mcode-sandbox-runtime": [ "./third_party/sandbox-runtime/src/index.ts" ],