@snack-kit/porygon 0.2.0 → 0.4.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 +17 -0
- package/dist/index.cjs +89 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -2
- package/dist/index.d.ts +46 -2
- package/dist/index.js +88 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -128,7 +128,7 @@ interface AgentErrorMessage extends BaseAgentMessage {
|
|
|
128
128
|
* 适配器能力声明
|
|
129
129
|
*/
|
|
130
130
|
interface AdapterCapabilities {
|
|
131
|
-
features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode">;
|
|
131
|
+
features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode" | "interactive-session">;
|
|
132
132
|
/**
|
|
133
133
|
* 流式输出模式:
|
|
134
134
|
* - 'delta': 后端原生产生增量 stream_chunk 事件(如 OpenCode)
|
|
@@ -209,6 +209,38 @@ interface InterceptorContext {
|
|
|
209
209
|
*/
|
|
210
210
|
type InterceptorFn = (text: string, context: InterceptorContext) => string | boolean | undefined | Promise<string | boolean | undefined>;
|
|
211
211
|
|
|
212
|
+
/**
|
|
213
|
+
* 便捷的多轮对话封装。
|
|
214
|
+
*
|
|
215
|
+
* 底层仍是 per-turn spawn + --resume,但对调用方透明:
|
|
216
|
+
* ```ts
|
|
217
|
+
* const session = porygon.session({ systemPrompt: '...' });
|
|
218
|
+
* for await (const msg of session.send('Hello')) { ... }
|
|
219
|
+
* for await (const msg of session.send('继续')) { ... } // 自动 resume
|
|
220
|
+
* session.close();
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
declare class InteractiveSession {
|
|
224
|
+
readonly initialSessionId: string;
|
|
225
|
+
private resolvedSessionId?;
|
|
226
|
+
private adapter;
|
|
227
|
+
private baseRequest;
|
|
228
|
+
private firstSent;
|
|
229
|
+
private closed;
|
|
230
|
+
constructor(initialSessionId: string, adapter: IAgentAdapter, baseRequest: Omit<PromptRequest, "prompt">);
|
|
231
|
+
/** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
|
|
232
|
+
get sessionId(): string;
|
|
233
|
+
/** 会话是否仍然活跃 */
|
|
234
|
+
get isActive(): boolean;
|
|
235
|
+
/**
|
|
236
|
+
* 发送一条消息,返回流式响应。
|
|
237
|
+
* 首次调用使用 initialSessionId,后续自动附加 resume。
|
|
238
|
+
*/
|
|
239
|
+
send(prompt: string): AsyncGenerator<AgentMessage>;
|
|
240
|
+
/** 关闭会话(仅清理内部状态,无进程需要释放) */
|
|
241
|
+
close(): void;
|
|
242
|
+
}
|
|
243
|
+
|
|
212
244
|
/**
|
|
213
245
|
* 健康检查结果(扁平化结构)
|
|
214
246
|
*/
|
|
@@ -270,6 +302,11 @@ declare class Porygon extends EventEmitter {
|
|
|
270
302
|
* @returns 最终结果文本
|
|
271
303
|
*/
|
|
272
304
|
run(request: PromptRequest): Promise<string>;
|
|
305
|
+
/**
|
|
306
|
+
* 创建交互式多轮对话会话。
|
|
307
|
+
* 自动管理 sessionId 和 resume,对调用方透明。
|
|
308
|
+
*/
|
|
309
|
+
session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
|
|
273
310
|
/**
|
|
274
311
|
* 注册拦截器
|
|
275
312
|
* @param direction 拦截方向
|
|
@@ -458,6 +495,8 @@ interface SpawnOptions {
|
|
|
458
495
|
cwd?: string;
|
|
459
496
|
env?: Record<string, string>;
|
|
460
497
|
timeoutMs?: number;
|
|
498
|
+
/** 写入 stdin 后自动关闭的数据 */
|
|
499
|
+
stdinData?: string;
|
|
461
500
|
}
|
|
462
501
|
/**
|
|
463
502
|
* 进程执行结果
|
|
@@ -681,6 +720,11 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
|
|
|
681
720
|
* @param noProxy 不走代理的地址列表
|
|
682
721
|
*/
|
|
683
722
|
private applyProxyEnv;
|
|
723
|
+
/**
|
|
724
|
+
* 构建通过 stdin 传递给 Claude CLI 的数据。
|
|
725
|
+
* 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
|
|
726
|
+
*/
|
|
727
|
+
private buildStdinData;
|
|
684
728
|
private buildArgs;
|
|
685
729
|
}
|
|
686
730
|
|
|
@@ -765,4 +809,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
|
|
|
765
809
|
private getConfigPath;
|
|
766
810
|
}
|
|
767
811
|
|
|
768
|
-
export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
|
|
812
|
+
export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
|
package/dist/index.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ interface AgentErrorMessage extends BaseAgentMessage {
|
|
|
128
128
|
* 适配器能力声明
|
|
129
129
|
*/
|
|
130
130
|
interface AdapterCapabilities {
|
|
131
|
-
features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode">;
|
|
131
|
+
features: Set<"streaming" | "session-resume" | "system-prompt" | "tool-restriction" | "mcp" | "subagents" | "worktree" | "budget-limit" | "serve-mode" | "interactive-session">;
|
|
132
132
|
/**
|
|
133
133
|
* 流式输出模式:
|
|
134
134
|
* - 'delta': 后端原生产生增量 stream_chunk 事件(如 OpenCode)
|
|
@@ -209,6 +209,38 @@ interface InterceptorContext {
|
|
|
209
209
|
*/
|
|
210
210
|
type InterceptorFn = (text: string, context: InterceptorContext) => string | boolean | undefined | Promise<string | boolean | undefined>;
|
|
211
211
|
|
|
212
|
+
/**
|
|
213
|
+
* 便捷的多轮对话封装。
|
|
214
|
+
*
|
|
215
|
+
* 底层仍是 per-turn spawn + --resume,但对调用方透明:
|
|
216
|
+
* ```ts
|
|
217
|
+
* const session = porygon.session({ systemPrompt: '...' });
|
|
218
|
+
* for await (const msg of session.send('Hello')) { ... }
|
|
219
|
+
* for await (const msg of session.send('继续')) { ... } // 自动 resume
|
|
220
|
+
* session.close();
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
declare class InteractiveSession {
|
|
224
|
+
readonly initialSessionId: string;
|
|
225
|
+
private resolvedSessionId?;
|
|
226
|
+
private adapter;
|
|
227
|
+
private baseRequest;
|
|
228
|
+
private firstSent;
|
|
229
|
+
private closed;
|
|
230
|
+
constructor(initialSessionId: string, adapter: IAgentAdapter, baseRequest: Omit<PromptRequest, "prompt">);
|
|
231
|
+
/** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
|
|
232
|
+
get sessionId(): string;
|
|
233
|
+
/** 会话是否仍然活跃 */
|
|
234
|
+
get isActive(): boolean;
|
|
235
|
+
/**
|
|
236
|
+
* 发送一条消息,返回流式响应。
|
|
237
|
+
* 首次调用使用 initialSessionId,后续自动附加 resume。
|
|
238
|
+
*/
|
|
239
|
+
send(prompt: string): AsyncGenerator<AgentMessage>;
|
|
240
|
+
/** 关闭会话(仅清理内部状态,无进程需要释放) */
|
|
241
|
+
close(): void;
|
|
242
|
+
}
|
|
243
|
+
|
|
212
244
|
/**
|
|
213
245
|
* 健康检查结果(扁平化结构)
|
|
214
246
|
*/
|
|
@@ -270,6 +302,11 @@ declare class Porygon extends EventEmitter {
|
|
|
270
302
|
* @returns 最终结果文本
|
|
271
303
|
*/
|
|
272
304
|
run(request: PromptRequest): Promise<string>;
|
|
305
|
+
/**
|
|
306
|
+
* 创建交互式多轮对话会话。
|
|
307
|
+
* 自动管理 sessionId 和 resume,对调用方透明。
|
|
308
|
+
*/
|
|
309
|
+
session(options?: Omit<PromptRequest, "prompt">): InteractiveSession;
|
|
273
310
|
/**
|
|
274
311
|
* 注册拦截器
|
|
275
312
|
* @param direction 拦截方向
|
|
@@ -458,6 +495,8 @@ interface SpawnOptions {
|
|
|
458
495
|
cwd?: string;
|
|
459
496
|
env?: Record<string, string>;
|
|
460
497
|
timeoutMs?: number;
|
|
498
|
+
/** 写入 stdin 后自动关闭的数据 */
|
|
499
|
+
stdinData?: string;
|
|
461
500
|
}
|
|
462
501
|
/**
|
|
463
502
|
* 进程执行结果
|
|
@@ -681,6 +720,11 @@ declare class ClaudeAdapter extends AbstractAgentAdapter {
|
|
|
681
720
|
* @param noProxy 不走代理的地址列表
|
|
682
721
|
*/
|
|
683
722
|
private applyProxyEnv;
|
|
723
|
+
/**
|
|
724
|
+
* 构建通过 stdin 传递给 Claude CLI 的数据。
|
|
725
|
+
* 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
|
|
726
|
+
*/
|
|
727
|
+
private buildStdinData;
|
|
684
728
|
private buildArgs;
|
|
685
729
|
}
|
|
686
730
|
|
|
@@ -765,4 +809,4 @@ declare class OpenCodeAdapter extends AbstractAgentAdapter {
|
|
|
765
809
|
private getConfigPath;
|
|
766
810
|
}
|
|
767
811
|
|
|
768
|
-
export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
|
|
812
|
+
export { AbstractAgentAdapter, type AdapterCapabilities, AdapterIncompatibleError, AdapterNotAvailableError, AdapterNotFoundError, type AgentAssistantMessage, type AgentErrorMessage, AgentExecutionError, type AgentMessage, type AgentResultMessage, type AgentStreamChunkMessage, type AgentSystemMessage, AgentTimeoutError, type AgentToolUseMessage, type BackendConfig, ClaudeAdapter, type CompatibilityResult, ConfigValidationError, type GuardAction, type GuardOptions, type HealthCheckResult, type IAgentAdapter, InteractiveSession, type InterceptorContext, type InterceptorDirection, type InterceptorFn, InterceptorRejectedError, type McpServerConfig, type ModelInfo, OpenCodeAdapter, Porygon, type PorygonConfig, PorygonError, type PorygonEvents, type PromptRequest, type ProxyConfig, type SessionInfo, type SessionListOptions, SessionNotFoundError, createInputGuard, createOutputGuard, createPorygon };
|
package/dist/index.js
CHANGED
|
@@ -309,12 +309,18 @@ var EphemeralProcess = class {
|
|
|
309
309
|
throw new Error("Process aborted before start");
|
|
310
310
|
}
|
|
311
311
|
this.aborted = false;
|
|
312
|
+
const useStdin = options.stdinData !== void 0;
|
|
312
313
|
const child = spawn(options.command, options.args, {
|
|
313
314
|
cwd: options.cwd,
|
|
314
315
|
env: options.env ?? void 0,
|
|
315
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
316
|
+
stdio: [useStdin ? "pipe" : "ignore", "pipe", "pipe"]
|
|
316
317
|
});
|
|
317
318
|
this.childProcess = child;
|
|
319
|
+
if (useStdin && child.stdin) {
|
|
320
|
+
child.stdin.write(options.stdinData, () => {
|
|
321
|
+
child.stdin.end();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
318
324
|
let timeoutTimer;
|
|
319
325
|
if (options.timeoutMs !== void 0 && options.timeoutMs > 0) {
|
|
320
326
|
timeoutTimer = setTimeout(() => {
|
|
@@ -811,14 +817,15 @@ function mapClaudeEvent(event, sessionId) {
|
|
|
811
817
|
return [];
|
|
812
818
|
}
|
|
813
819
|
if (isResultEvent(event)) {
|
|
820
|
+
const usage = event.usage;
|
|
814
821
|
return [{
|
|
815
822
|
...baseFields,
|
|
816
823
|
type: "result",
|
|
817
824
|
text: event.result,
|
|
818
|
-
costUsd: event.cost_usd,
|
|
825
|
+
costUsd: event.total_cost_usd ?? event.cost_usd,
|
|
819
826
|
durationMs: event.duration_ms,
|
|
820
|
-
inputTokens: event.input_tokens,
|
|
821
|
-
outputTokens: event.output_tokens
|
|
827
|
+
inputTokens: usage?.input_tokens ?? event.input_tokens,
|
|
828
|
+
outputTokens: usage?.output_tokens ?? event.output_tokens
|
|
822
829
|
}];
|
|
823
830
|
}
|
|
824
831
|
return [];
|
|
@@ -926,7 +933,8 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
926
933
|
"tool-restriction",
|
|
927
934
|
"mcp",
|
|
928
935
|
"subagents",
|
|
929
|
-
"worktree"
|
|
936
|
+
"worktree",
|
|
937
|
+
"interactive-session"
|
|
930
938
|
]),
|
|
931
939
|
streamingMode: "chunked",
|
|
932
940
|
outputFormats: ["text", "json", "stream-json"],
|
|
@@ -959,12 +967,14 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
959
967
|
if (v) cleanEnv[k] = v;
|
|
960
968
|
}
|
|
961
969
|
}
|
|
970
|
+
const stdinData = this.buildStdinData(request);
|
|
962
971
|
const streamOptions = {
|
|
963
972
|
command: this.cliCommand,
|
|
964
973
|
args,
|
|
965
974
|
...cwd ? { cwd } : {},
|
|
966
975
|
env: cleanEnv,
|
|
967
|
-
timeoutMs: request.timeoutMs
|
|
976
|
+
timeoutMs: request.timeoutMs,
|
|
977
|
+
stdinData
|
|
968
978
|
};
|
|
969
979
|
const cmdStr = [this.cliCommand, ...args.map((a) => /[\s"']/.test(a) ? JSON.stringify(a) : a)].join(" ");
|
|
970
980
|
const debugCmd = cwd ? `cd ${JSON.stringify(cwd)} && ${cmdStr}` : cmdStr;
|
|
@@ -1097,10 +1107,16 @@ var ClaudeAdapter = class extends AbstractAgentAdapter {
|
|
|
1097
1107
|
env["NO_PROXY"] = noProxy;
|
|
1098
1108
|
}
|
|
1099
1109
|
}
|
|
1110
|
+
/**
|
|
1111
|
+
* 构建通过 stdin 传递给 Claude CLI 的数据。
|
|
1112
|
+
* 使用 stdin 而非 CLI 参数传递 prompt,避免超长参数导致的 403 错误。
|
|
1113
|
+
*/
|
|
1114
|
+
buildStdinData(request) {
|
|
1115
|
+
return request.prompt;
|
|
1116
|
+
}
|
|
1100
1117
|
buildArgs(request) {
|
|
1101
1118
|
const args = [
|
|
1102
|
-
"
|
|
1103
|
-
request.prompt,
|
|
1119
|
+
"--print",
|
|
1104
1120
|
"--output-format",
|
|
1105
1121
|
"stream-json",
|
|
1106
1122
|
"--verbose"
|
|
@@ -1712,6 +1728,54 @@ var OpenCodeAdapter = class extends AbstractAgentAdapter {
|
|
|
1712
1728
|
}
|
|
1713
1729
|
};
|
|
1714
1730
|
|
|
1731
|
+
// src/session/interactive-session.ts
|
|
1732
|
+
var InteractiveSession = class {
|
|
1733
|
+
initialSessionId;
|
|
1734
|
+
resolvedSessionId;
|
|
1735
|
+
adapter;
|
|
1736
|
+
baseRequest;
|
|
1737
|
+
firstSent = false;
|
|
1738
|
+
closed = false;
|
|
1739
|
+
constructor(initialSessionId, adapter, baseRequest) {
|
|
1740
|
+
this.initialSessionId = initialSessionId;
|
|
1741
|
+
this.adapter = adapter;
|
|
1742
|
+
this.baseRequest = baseRequest;
|
|
1743
|
+
}
|
|
1744
|
+
/** 当前生效的 sessionId(首次 send 后反映 CLI 返回的真实 ID) */
|
|
1745
|
+
get sessionId() {
|
|
1746
|
+
return this.resolvedSessionId ?? this.initialSessionId;
|
|
1747
|
+
}
|
|
1748
|
+
/** 会话是否仍然活跃 */
|
|
1749
|
+
get isActive() {
|
|
1750
|
+
return !this.closed;
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* 发送一条消息,返回流式响应。
|
|
1754
|
+
* 首次调用使用 initialSessionId,后续自动附加 resume。
|
|
1755
|
+
*/
|
|
1756
|
+
async *send(prompt) {
|
|
1757
|
+
if (this.closed) {
|
|
1758
|
+
throw new SessionNotFoundError(this.sessionId);
|
|
1759
|
+
}
|
|
1760
|
+
const request = {
|
|
1761
|
+
...this.baseRequest,
|
|
1762
|
+
prompt,
|
|
1763
|
+
...this.firstSent ? { resume: this.sessionId } : {}
|
|
1764
|
+
};
|
|
1765
|
+
for await (const msg of this.adapter.query(request)) {
|
|
1766
|
+
if (!this.firstSent && msg.sessionId) {
|
|
1767
|
+
this.resolvedSessionId = msg.sessionId;
|
|
1768
|
+
}
|
|
1769
|
+
yield msg;
|
|
1770
|
+
}
|
|
1771
|
+
this.firstSent = true;
|
|
1772
|
+
}
|
|
1773
|
+
/** 关闭会话(仅清理内部状态,无进程需要释放) */
|
|
1774
|
+
close() {
|
|
1775
|
+
this.closed = true;
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1715
1779
|
// src/porygon.ts
|
|
1716
1780
|
var Porygon = class extends EventEmitter2 {
|
|
1717
1781
|
config;
|
|
@@ -1811,6 +1875,21 @@ var Porygon = class extends EventEmitter2 {
|
|
|
1811
1875
|
}
|
|
1812
1876
|
return resultText;
|
|
1813
1877
|
}
|
|
1878
|
+
/**
|
|
1879
|
+
* 创建交互式多轮对话会话。
|
|
1880
|
+
* 自动管理 sessionId 和 resume,对调用方透明。
|
|
1881
|
+
*/
|
|
1882
|
+
session(options) {
|
|
1883
|
+
const backend = options?.backend ?? this.config.defaultBackend ?? "claude";
|
|
1884
|
+
const adapter = this.getAdapter(backend);
|
|
1885
|
+
const merged = this.mergeRequest({ ...options, prompt: "" }, backend);
|
|
1886
|
+
const { prompt: _, ...baseRequest } = merged;
|
|
1887
|
+
return new InteractiveSession(
|
|
1888
|
+
crypto.randomUUID(),
|
|
1889
|
+
adapter,
|
|
1890
|
+
baseRequest
|
|
1891
|
+
);
|
|
1892
|
+
}
|
|
1814
1893
|
/**
|
|
1815
1894
|
* 注册拦截器
|
|
1816
1895
|
* @param direction 拦截方向
|
|
@@ -2058,6 +2137,7 @@ export {
|
|
|
2058
2137
|
AgentTimeoutError,
|
|
2059
2138
|
ClaudeAdapter,
|
|
2060
2139
|
ConfigValidationError,
|
|
2140
|
+
InteractiveSession,
|
|
2061
2141
|
InterceptorRejectedError,
|
|
2062
2142
|
OpenCodeAdapter,
|
|
2063
2143
|
Porygon,
|