@spotpatch/next 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,8 @@
18
18
 
19
19
  The preview connects SpotPatch's element picker, source-aware context, bilingual multi-target requests, and optional review-gated AI Agent to a Next.js development server.
20
20
 
21
+ Component `dataFlow` is not part of the Next.js preview yet. `spotpatch-next init` intentionally does not write that option, and `withSpotPatch({ dataFlow: {} })` is rejected instead of displaying a non-functional or misleading panel. The currently implemented data-flow Beta remains Vite + React 18 only.
22
+
21
23
  ### Implemented preview path
22
24
 
23
25
  - `withSpotPatch()` composes an existing `next.config` without evaluating it twice.
@@ -68,6 +70,36 @@ import "@spotpatch/next/client";
68
70
 
69
71
  Start development through the package script, not a direct `next dev` command. A successful startup prints one line beginning with `[spotpatch:next] ready`; the page then mounts one `spotpatch-root` Shadow DOM host with the **Select element** / **选择元素** action.
70
72
 
73
+ ### External Agent handoff (local validation)
74
+
75
+ Enable the development-only handoff UI explicitly in the same wrapper options:
76
+
77
+ ```ts
78
+ export default withSpotPatch({ externalAgent: true })(nextConfig);
79
+ ```
80
+
81
+ Run setup and active commands from the exact canonical project root that owns the running `spotpatch-next dev` session. Setup is a dry run without `--write`:
82
+
83
+ ```bash
84
+ pnpm exec spotpatch-next bridge setup --client claude --scope project --mode active --write
85
+ MCP_PROTOCOL_NEGOTIATION=legacy claude --dangerously-load-development-channels server:spotpatch
86
+
87
+ pnpm exec spotpatch-next connect codex --allow-workspace-write
88
+
89
+ pnpm exec spotpatch-next bridge setup --client cursor --scope project --write
90
+ ```
91
+
92
+ The active command forms support an explicit session when `bridge sessions --json` reports multiple sessions for that exact root:
93
+
94
+ ```text
95
+ spotpatch-next bridge channel claude [--session <opaque-id>]
96
+ spotpatch-next connect codex --allow-workspace-write [--session <opaque-id>]
97
+ ```
98
+
99
+ The Claude legacy environment belongs on the Claude host process. Claude Channels are a Research Preview, work only while the Channel-enabled session is running, and provide no completion acknowledgement; completion depends on Claude calling the SpotPatch result tool. Codex active mode is zero-setup: the Connector injects the project-local SpotPatch MCP entry into its App Server thread and does not create or modify `.codex/config.toml`. Codex can still load other MCP servers already enabled by the user's normal Codex configuration. The Connector accepts exactly `codex-cli 0.149.0`. `bridge setup --client codex ...` remains available only for optional Inbox use; Cursor and all generic MCP hosts remain Inbox-only.
100
+
101
+ This path is `local-validation`, not part of the Next public-support promise. Automated fake-host and two-handoff tests exist, and a consecutive two-revision Codex flow has been manually validated on the recorded macOS/Next.js/Codex 0.149.0 environment. Real Claude Code consecutive delivery, repeatable real-host automation, and Windows process-tree cleanup remain `not-tested`; Cursor remains Inbox-only. See the [external Agent design and exact status](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E5%A4%96%E9%83%A8Agent%E8%BF%9E%E6%8E%A5/00-%E7%B4%A2%E5%BC%95%E4%B8%8E%E5%86%B3%E7%AD%96%E6%91%98%E8%A6%81.md).
102
+
71
103
  ### Current evidence and remaining gates
72
104
 
73
105
  The public preview has passed:
@@ -119,6 +151,8 @@ If React reports only a removed body attribute such as `cz-shortcut-listen="true
119
151
 
120
152
  当前预览把 SpotPatch 的元素选择、源码上下文、中英文多目标修改要求和可选审阅式 AI Agent 接入 Next.js 开发服务器。
121
153
 
154
+ 组件 `dataFlow` 尚未进入 Next.js 预览范围。`spotpatch-next init` 会有意不写入该选项;`withSpotPatch({ dataFlow: {} })` 会直接拒绝,而不是显示一个不能工作的误导性面板。当前已实现的数据链路 Beta 仍仅限 Vite + React 18。
155
+
122
156
  ### 已实现的预览链路
123
157
 
124
158
  - `withSpotPatch()` 组合现有 `next.config`,不会重复执行宿主配置。
@@ -169,6 +203,36 @@ import "@spotpatch/next/client";
169
203
 
170
204
  必须通过 package script 启动,而不是直接运行 `next dev`。成功启动时终端只打印一条以 `[spotpatch:next] ready` 开头的信息;页面随后挂载唯一 `spotpatch-root` Shadow DOM,并显示 **Select element** / **选择元素** 操作。
171
205
 
206
+ ### 外部 Agent 交接(本地验证)
207
+
208
+ 在同一包装器选项中显式启用仅开发期的交接 UI:
209
+
210
+ ```ts
211
+ export default withSpotPatch({ externalAgent: true })(nextConfig);
212
+ ```
213
+
214
+ 必须在当前 `spotpatch-next dev` Session 所属的精确 canonical 项目根执行 setup 和主动命令。setup 不带 `--write` 时只是 dry-run:
215
+
216
+ ```bash
217
+ pnpm exec spotpatch-next bridge setup --client claude --scope project --mode active --write
218
+ MCP_PROTOCOL_NEGOTIATION=legacy claude --dangerously-load-development-channels server:spotpatch
219
+
220
+ pnpm exec spotpatch-next connect codex --allow-workspace-write
221
+
222
+ pnpm exec spotpatch-next bridge setup --client cursor --scope project --write
223
+ ```
224
+
225
+ 若 `bridge sessions --json` 报告该精确项目根有多个 Session,主动子命令支持显式选择:
226
+
227
+ ```text
228
+ spotpatch-next bridge channel claude [--session <opaque-id>]
229
+ spotpatch-next connect codex --allow-workspace-write [--session <opaque-id>]
230
+ ```
231
+
232
+ Claude legacy 环境变量必须设置在 Claude 宿主进程。Claude Channels 仍是 Research Preview,只在已启用 Channel 的会话运行时工作,且没有 completion ACK;完成状态依赖 Claude 调用 SpotPatch 结果 tool。Codex 主动模式为零配置:Connector 只把当前项目的 SpotPatch MCP 配置注入它拥有的 App Server thread,不创建也不修改 `.codex/config.toml`;Codex 仍可能按用户既有配置启动其他已启用 MCP server。Connector 当前只接受 `codex-cli 0.149.0`。`bridge setup --client codex ...` 仅保留为可选 Inbox 配置;Cursor 和所有普通 MCP 宿主仍为 Inbox-only。
233
+
234
+ 该链路当前只是 `local-validation`,不属于 Next 正式支持承诺。仓库有假宿主和连续两 Handoff 自动化测试,并已在记录的 macOS/Next.js/Codex 0.149.0 环境人工验证连续两个 revision。真实 Claude Code 连续投递、可重复真实宿主自动化和 Windows 进程树清理仍为 `not-tested`;Cursor 保持 Inbox-only。准确边界见[外部 Agent 方案与实现状态](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E5%A4%96%E9%83%A8Agent%E8%BF%9E%E6%8E%A5/00-%E7%B4%A2%E5%BC%95%E4%B8%8E%E5%86%B3%E7%AD%96%E6%91%98%E8%A6%81.md)。
235
+
172
236
  ### 当前证据与剩余门禁
173
237
 
174
238
  公共预览已经通过:
package/dist/cli.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // src/cli.ts
4
4
  import { createRequire as createRequire2 } from "module";
5
5
  import path4 from "path";
6
+ import { runSpotPatchBridgeCli } from "@spotpatch/bridge";
6
7
 
7
8
  // src/cli-owner.ts
8
9
  import { spawn } from "child_process";
@@ -282,7 +283,9 @@ import {
282
283
  } from "http";
283
284
  import {
284
285
  createAgentJobManager,
286
+ createExternalHandoffService,
285
287
  createRuntimeAiConfig,
288
+ createRuntimeDataFlowConfig,
286
289
  createSession,
287
290
  createSourceRegistrationService,
288
291
  createSourceRegistry,
@@ -297,7 +300,7 @@ import {
297
300
  // package.json
298
301
  var package_default = {
299
302
  name: "@spotpatch/next",
300
- version: "0.4.0",
303
+ version: "0.6.0",
301
304
  description: "Development-only Next.js adapter and CLI for SpotPatch.",
302
305
  license: "MIT",
303
306
  repository: {
@@ -390,6 +393,7 @@ var package_default = {
390
393
  "verify:loader": "node scripts/verify-loader.cjs"
391
394
  },
392
395
  dependencies: {
396
+ "@spotpatch/bridge": "workspace:^",
393
397
  "@spotpatch/compiler": "workspace:^",
394
398
  "@spotpatch/dev-server": "workspace:^",
395
399
  "@spotpatch/runtime": "workspace:^",
@@ -563,6 +567,7 @@ async function createNextSidecar(sidecarOptions) {
563
567
  let closed = false;
564
568
  let registry;
565
569
  let agentManager;
570
+ let externalHandoffService;
566
571
  let handler = (_request, response) => {
567
572
  writeUnavailable(response, 503);
568
573
  };
@@ -621,9 +626,11 @@ async function createNextSidecar(sidecarOptions) {
621
626
  apiBase: SPOTPATCH_API_BASE,
622
627
  ai: createRuntimeAiConfig(input.options.ai),
623
628
  budget: input.options.budget,
629
+ dataFlow: createRuntimeDataFlowConfig(input.options.dataFlow),
624
630
  bundler: input.bundler,
625
631
  debug: input.options.debug,
626
632
  editor: input.options.editor,
633
+ externalAgent: input.options.externalAgent,
627
634
  framework: "next",
628
635
  frameworkVersion: input.nextVersion,
629
636
  locale: input.options.locale,
@@ -640,8 +647,24 @@ async function createNextSidecar(sidecarOptions) {
640
647
  environment: input.credentials,
641
648
  root: input.projectRoot
642
649
  });
650
+ const handoffService = input.options.externalAgent.enabled ? createExternalHandoffService({
651
+ framework: "next",
652
+ root: input.appRoot,
653
+ sessionId: session.id
654
+ }) : void 0;
655
+ if (handoffService !== void 0) {
656
+ try {
657
+ await handoffService.start();
658
+ } catch {
659
+ process.stderr.write(
660
+ "[spotpatch:next] External Agent handoff is unavailable; core tools remain active.\n"
661
+ );
662
+ }
663
+ }
664
+ externalHandoffService = handoffService;
643
665
  const middleware = createSpotPatchMiddleware({
644
666
  ...manager === void 0 ? {} : { agentManager: manager },
667
+ ...handoffService === void 0 ? {} : { externalHandoffService: handoffService },
645
668
  bootstrap: {
646
669
  expectedOrigin: input.publicOrigin,
647
670
  runtimeConfig
@@ -687,6 +710,8 @@ async function createNextSidecar(sidecarOptions) {
687
710
  writeUnavailable(response, 503);
688
711
  };
689
712
  registry?.clear();
713
+ await externalHandoffService?.close();
714
+ externalHandoffService = void 0;
690
715
  await agentManager?.close();
691
716
  server.closeIdleConnections();
692
717
  const closing = closeServer(server);
@@ -1037,9 +1062,7 @@ function importInsertionOffset(program) {
1037
1062
  function insertStaticImport(magicString, program, statement) {
1038
1063
  const offset = importInsertionOffset(program);
1039
1064
  if (offset === 0) {
1040
- magicString.prepend(`${statement}
1041
-
1042
- `);
1065
+ magicString.prepend(`${statement}${program.body.length === 0 ? "\n" : "\n\n"}`);
1043
1066
  return;
1044
1067
  }
1045
1068
  magicString.appendRight(offset, `
@@ -1120,6 +1143,27 @@ function wrappedFactoryCall(declaration, wrapperName) {
1120
1143
  const callee = unwrapParentheses(factoryCall.callee);
1121
1144
  return callee.type === "Identifier" && callee.name === wrapperName ? factoryCall : void 0;
1122
1145
  }
1146
+ function staticPropertyName(property) {
1147
+ if (property.computed) return void 0;
1148
+ if (property.key.type === "Identifier") return property.key.name;
1149
+ return property.key.type === "Literal" && typeof property.key.value === "string" ? property.key.value : void 0;
1150
+ }
1151
+ function assertSupportedDataFlowOption(factory) {
1152
+ const argument = factory.arguments[0];
1153
+ if (argument?.type !== "ObjectExpression") return;
1154
+ const properties = argument.properties.filter(
1155
+ (property2) => property2.type === "Property" && staticPropertyName(property2) === "dataFlow"
1156
+ );
1157
+ if (properties.length > 1) {
1158
+ throw new Error("SpotPatch init found duplicate dataFlow options.");
1159
+ }
1160
+ const property = properties[0];
1161
+ if (property !== void 0 && !(property.value.type === "Literal" && property.value.value === false)) {
1162
+ throw new Error(
1163
+ "SpotPatch Next does not support component dataFlow yet; remove dataFlow or set it to false."
1164
+ );
1165
+ }
1166
+ }
1123
1167
  function transformNextConfig(absolutePath, source, trustedFastModeAvailable) {
1124
1168
  if (absolutePath.endsWith(".cjs") || absolutePath.endsWith(".cts")) {
1125
1169
  throw new Error(
@@ -1138,6 +1182,7 @@ function transformNextConfig(absolutePath, source, trustedFastModeAvailable) {
1138
1182
  const wrapperName = existingWrapperName ?? chooseWrapperName(program);
1139
1183
  const existingFactory = existingWrapperName === void 0 ? void 0 : wrappedFactoryCall(defaultExport.declaration, existingWrapperName);
1140
1184
  if (existingFactory !== void 0) {
1185
+ assertSupportedDataFlowOption(existingFactory);
1141
1186
  if (trustedFastModeAvailable && existingFactory.arguments.length === 0) {
1142
1187
  magicString.overwrite(
1143
1188
  existingFactory.start,
@@ -1375,7 +1420,7 @@ async function checkNextIntegration(directory = process.cwd()) {
1375
1420
  // src/cli.ts
1376
1421
  function writeUsage() {
1377
1422
  process.stderr.write(
1378
- "Usage: spotpatch-next <dev|init|check>\n dev [next dev options] Start the local Next.js development server.\n init Preview and apply safe integration changes.\n check Verify the integration without writing files.\n"
1423
+ "Usage: spotpatch-next <dev|init|check|connect|bridge>\n dev [next dev options] Start the local Next.js development server.\n init Preview and apply safe integration changes.\n check Verify the integration without writing files.\n connect codex Start the zero-setup Codex Agent connector.\n bridge Run external-Agent MCP, CLI, or setup commands.\n"
1379
1424
  );
1380
1425
  }
1381
1426
  function verifyAdapterExports(appRoot) {
@@ -1467,6 +1512,12 @@ async function main(arguments_) {
1467
1512
  if (command === "check") {
1468
1513
  return runCheck(rest);
1469
1514
  }
1515
+ if (command === "bridge") {
1516
+ return runSpotPatchBridgeCli(rest, { adapter: "next" });
1517
+ }
1518
+ if (command === "connect") {
1519
+ return runSpotPatchBridgeCli(arguments_, { adapter: "next" });
1520
+ }
1470
1521
  writeUsage();
1471
1522
  return 1;
1472
1523
  }