@snack-kit/porygon 0.2.0 → 0.3.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
@@ -551,6 +551,16 @@ npm run playground # 启动 Playground
551
551
 
552
552
  ## Changelog
553
553
 
554
+ ### v0.3.0
555
+
556
+ #### Bug Fixes
557
+
558
+ - **Claude adapter**: 将 prompt 从 CLI 参数 (`-p <prompt>`) 改为通过 stdin 传递 (`--print` + stdin pipe),修复超长 prompt 导致的偶发 403 "Request not allowed" 错误
559
+
560
+ #### 改进
561
+
562
+ - `SpawnOptions` 新增 `stdinData` 字段,`EphemeralProcess.executeStreaming` 支持向子进程 stdin 写入数据
563
+
554
564
  ### v0.2.0
555
565
 
556
566
  #### 新特性
package/dist/index.cjs CHANGED
@@ -28,6 +28,7 @@ __export(index_exports, {
28
28
  AgentTimeoutError: () => AgentTimeoutError,
29
29
  ClaudeAdapter: () => ClaudeAdapter,
30
30
  ConfigValidationError: () => ConfigValidationError,
31
+ InteractiveSession: () => InteractiveSession,
31
32
  InterceptorRejectedError: () => InterceptorRejectedError,
32
33
  OpenCodeAdapter: () => OpenCodeAdapter,
33
34
  Porygon: () => Porygon,
@@ -350,12 +351,18 @@ var EphemeralProcess = class {
350
351
  throw new Error("Process aborted before start");
351
352
  }
352
353
  this.aborted = false;
354
+ const useStdin = options.stdinData !== void 0;
353
355
  const child = (0, import_node_child_process.spawn)(options.command, options.args, {
354
356
  cwd: options.cwd,
355
357
  env: options.env ?? void 0,
356
- stdio: ["ignore", "pipe", "pipe"]
358
+ stdio: [useStdin ? "pipe" : "ignore", "pipe", "pipe"]
357
359
  });
358
360
  this.childProcess = child;
361
+ if (useStdin && child.stdin) {
362
+ child.stdin.write(options.stdinData, () => {
363
+ child.stdin.end();
364
+ });
365
+ }
359
366
  let timeoutTimer;
360
367
  if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
361
368
  timeoutTimer = setTimeout(() => {
@@ -967,7 +974,8 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
967
974
  "tool-restriction",
968
975
  "mcp",
969
976
  "subagents",
970
- "worktree"
977
+ "worktree",
978
+ "interactive-session"
971
979
  ]),
972
980
  streamingMode: "chunked",
973
981
  outputFormats: ["text", "json", "stream-json"],
@@ -1000,12 +1008,14 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1000
1008
  if (v) cleanEnv[k] = v;
1001
1009
  }
1002
1010
  }
1011
+ const stdinData = this.buildStdinData(request);
1003
1012
  const streamOptions = {
1004
1013
  command: this.cliCommand,
1005
1014
  args,
1006
1015
  ...cwd ? { cwd } : {},
1007
1016
  env: cleanEnv,
1008
- timeoutMs: request.timeoutMs
1017
+ timeoutMs: request.timeoutMs,
1018
+ stdinData
1009
1019
  };
1010
1020
  const cmdStr = [this.cliCommand, ...args.map((a) => /[\s"']/.test(a) ? JSON.stringify(a) : a)].join(" ");
1011
1021
  const debugCmd = cwd ? `cd ${JSON.stringify(cwd)} && ${cmdStr}` : cmdStr;
@@ -1138,10 +1148,16 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
1138
1148
  env["NO_PROXY"] = noProxy;
1139
1149
  }
1140
1150
  }
1151
+ /**
1152
+ * 构建通过 stdin 传递给 Claude CLI 的数据。
1153
+ * 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
1154
+ */
1155
+ buildStdinData(request) {
1156
+ return request.prompt;
1157
+ }
1141
1158
  buildArgs(request) {
1142
1159
  const args = [
1143
- "-p",
1144
- request.prompt,
1160
+ "--print",
1145
1161
  "--output-format",
1146
1162
  "stream-json",
1147
1163
  "--verbose"
@@ -1753,6 +1769,54 @@ var OpenCodeAdapter = class extends AbstractAgentAdapter {
1753
1769
  }
1754
1770
  };
1755
1771
 
1772
+ // src/session/interactive-session.ts
1773
+ var InteractiveSession = class {
1774
+ initialSessionId;
1775
+ resolvedSessionId;
1776
+ adapter;
1777
+ baseRequest;
1778
+ firstSent = false;
1779
+ closed = false;
1780
+ constructor(initialSessionId, adapter, baseRequest) {
1781
+ this.initialSessionId = initialSessionId;
1782
+ this.adapter = adapter;
1783
+ this.baseRequest = baseRequest;
1784
+ }
1785
+ /** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
1786
+ get sessionId() {
1787
+ return this.resolvedSessionId ?? this.initialSessionId;
1788
+ }
1789
+ /** 会话是否仍然活跃 */
1790
+ get isActive() {
1791
+ return !this.closed;
1792
+ }
1793
+ /**
1794
+ * 发送一条消息,返回流式响应。
1795
+ * 首次调用使用 initialSessionId,后续自动附加 resume。
1796
+ */
1797
+ async *send(prompt) {
1798
+ if (this.closed) {
1799
+ throw new SessionNotFoundError(this.sessionId);
1800
+ }
1801
+ const request = {
1802
+ ...this.baseRequest,
1803
+ prompt,
1804
+ ...this.firstSent ? { resume: this.sessionId } : {}
1805
+ };
1806
+ for await (const msg of this.adapter.query(request)) {
1807
+ if (!this.firstSent && msg.sessionId) {
1808
+ this.resolvedSessionId = msg.sessionId;
1809
+ }
1810
+ yield msg;
1811
+ }
1812
+ this.firstSent = true;
1813
+ }
1814
+ /** 关闭会话(仅清理内部状态,无进程需要释放) */
1815
+ close() {
1816
+ this.closed = true;
1817
+ }
1818
+ };
1819
+
1756
1820
  // src/porygon.ts
1757
1821
  var Porygon = class extends import_node_events2.EventEmitter {
1758
1822
  config;
@@ -1852,6 +1916,21 @@ var Porygon = class extends import_node_events2.EventEmitter {
1852
1916
  }
1853
1917
  return resultText;
1854
1918
  }
1919
+ /**
1920
+ * 创建交互式多轮对话会话。
1921
+ * 自动管理 sessionId 和 resume,对调用方透明。
1922
+ */
1923
+ session(options) {
1924
+ const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
1925
+ const adapter = this.getAdapter(backend);
1926
+ const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
1927
+ const { prompt: _, ...baseRequest } = merged;
1928
+ return new InteractiveSession(
1929
+ crypto.randomUUID(),
1930
+ adapter,
1931
+ baseRequest
1932
+ );
1933
+ }
1855
1934
  /**
1856
1935
  * 注册拦截器
1857
1936
  * @param direction 拦截方向
@@ -2100,6 +2179,7 @@ function createOutputGuard(options) {
2100
2179
  AgentTimeoutError,
2101
2180
  ClaudeAdapter,
2102
2181
  ConfigValidationError,
2182
+ InteractiveSession,
2103
2183
  InterceptorRejectedError,
2104
2184
  OpenCodeAdapter,
2105
2185
  Porygon,