@te-river/opencode-team-mode 1.5.11 → 1.5.13
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 +154 -57
- package/README.zh-CN.md +126 -47
- package/dist/agents.d.ts +5 -1
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +37 -11
- package/dist/agents.js.map +1 -1
- package/dist/approval-gate.d.ts +54 -16
- package/dist/approval-gate.d.ts.map +1 -1
- package/dist/approval-gate.js +163 -42
- package/dist/approval-gate.js.map +1 -1
- package/dist/host-hooks.d.ts +115 -0
- package/dist/host-hooks.d.ts.map +1 -0
- package/dist/host-hooks.js +270 -0
- package/dist/host-hooks.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -4
- package/dist/index.js.map +1 -1
- package/dist/prompts/lead.d.ts +1 -1
- package/dist/prompts/lead.d.ts.map +1 -1
- package/dist/prompts/lead.js +88 -6
- package/dist/prompts/lead.js.map +1 -1
- package/dist/prompts/shared.d.ts +1 -1
- package/dist/prompts/shared.d.ts.map +1 -1
- package/dist/prompts/shared.js +203 -119
- package/dist/prompts/shared.js.map +1 -1
- package/dist/prompts/specialists.d.ts +2 -2
- package/dist/prompts/specialists.d.ts.map +1 -1
- package/dist/prompts/specialists.js +73 -23
- package/dist/prompts/specialists.js.map +1 -1
- package/dist/tm/args-schema.d.ts +31 -1
- package/dist/tm/args-schema.d.ts.map +1 -1
- package/dist/tm/args-schema.js +155 -39
- package/dist/tm/args-schema.js.map +1 -1
- package/dist/tm/bash-timeout.d.ts +72 -0
- package/dist/tm/bash-timeout.d.ts.map +1 -0
- package/dist/tm/bash-timeout.js +100 -0
- package/dist/tm/bash-timeout.js.map +1 -0
- package/dist/tm/browser.d.ts +250 -20
- package/dist/tm/browser.d.ts.map +1 -1
- package/dist/tm/browser.js +1200 -143
- package/dist/tm/browser.js.map +1 -1
- package/dist/tm/config.d.ts +129 -0
- package/dist/tm/config.d.ts.map +1 -1
- package/dist/tm/config.js +117 -1
- package/dist/tm/config.js.map +1 -1
- package/dist/tm/dispatch.d.ts +95 -0
- package/dist/tm/dispatch.d.ts.map +1 -0
- package/dist/tm/dispatch.js +369 -0
- package/dist/tm/dispatch.js.map +1 -0
- package/dist/tm/index.d.ts +32 -7
- package/dist/tm/index.d.ts.map +1 -1
- package/dist/tm/index.js +74 -5
- package/dist/tm/index.js.map +1 -1
- package/dist/tm/memory.d.ts +88 -10
- package/dist/tm/memory.d.ts.map +1 -1
- package/dist/tm/memory.js +881 -99
- package/dist/tm/memory.js.map +1 -1
- package/dist/tm/pipelines.d.ts +26 -3
- package/dist/tm/pipelines.d.ts.map +1 -1
- package/dist/tm/pipelines.js +167 -11
- package/dist/tm/pipelines.js.map +1 -1
- package/dist/tm/ptc/budgets.d.ts +22 -1
- package/dist/tm/ptc/budgets.d.ts.map +1 -1
- package/dist/tm/ptc/budgets.js +87 -8
- package/dist/tm/ptc/budgets.js.map +1 -1
- package/dist/tm/ptc/contract.d.ts +49 -9
- package/dist/tm/ptc/contract.d.ts.map +1 -1
- package/dist/tm/ptc/contract.js +41 -3
- package/dist/tm/ptc/contract.js.map +1 -1
- package/dist/tm/ptc/driver.d.ts +5 -2
- package/dist/tm/ptc/driver.d.ts.map +1 -1
- package/dist/tm/ptc/driver.js +47 -15
- package/dist/tm/ptc/driver.js.map +1 -1
- package/dist/tm/ptc/engines.d.ts +15 -4
- package/dist/tm/ptc/engines.d.ts.map +1 -1
- package/dist/tm/ptc/engines.js +281 -28
- package/dist/tm/ptc/engines.js.map +1 -1
- package/dist/tm/ptc/index.d.ts +4 -4
- package/dist/tm/ptc/index.d.ts.map +1 -1
- package/dist/tm/ptc/index.js +2 -2
- package/dist/tm/ptc/index.js.map +1 -1
- package/dist/tm/ptc/pscan.d.ts +20 -1
- package/dist/tm/ptc/pscan.d.ts.map +1 -1
- package/dist/tm/ptc/pscan.js +328 -14
- package/dist/tm/ptc/pscan.js.map +1 -1
- package/dist/tm/ptc/summary.d.ts.map +1 -1
- package/dist/tm/ptc/summary.js +23 -1
- package/dist/tm/ptc/summary.js.map +1 -1
- package/dist/tm/ptc/tool.d.ts +34 -3
- package/dist/tm/ptc/tool.d.ts.map +1 -1
- package/dist/tm/ptc/tool.js +117 -20
- package/dist/tm/ptc/tool.js.map +1 -1
- package/dist/tm/pty.d.ts +70 -0
- package/dist/tm/pty.d.ts.map +1 -0
- package/dist/tm/pty.js +242 -0
- package/dist/tm/pty.js.map +1 -0
- package/dist/tm/search.d.ts +128 -15
- package/dist/tm/search.d.ts.map +1 -1
- package/dist/tm/search.js +556 -47
- package/dist/tm/search.js.map +1 -1
- package/dist/tm/shell-bridge.d.ts +14 -1
- package/dist/tm/shell-bridge.d.ts.map +1 -1
- package/dist/tm/shell-bridge.js +57 -10
- package/dist/tm/shell-bridge.js.map +1 -1
- package/dist/tm/webfetch.d.ts +29 -7
- package/dist/tm/webfetch.d.ts.map +1 -1
- package/dist/tm/webfetch.js +117 -15
- package/dist/tm/webfetch.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/installation.md +216 -17
- package/package.json +5 -2
package/dist/tm/ptc/tool.js
CHANGED
|
@@ -8,47 +8,134 @@
|
|
|
8
8
|
import { estimateTokens, shouldOffload, shorten } from "../config.js";
|
|
9
9
|
import { detectContentType } from "../preview.js";
|
|
10
10
|
import { tmError } from "../result.js";
|
|
11
|
+
import { askUserForTarget } from "../perm-ask.js";
|
|
11
12
|
import { BRIDGE_ALLOW } from "./contract.js";
|
|
12
13
|
import { parsePtcArgs } from "./budgets.js";
|
|
13
14
|
import { staticPscan } from "./pscan.js";
|
|
14
15
|
import { runPtc } from "./driver.js";
|
|
15
16
|
import { renderPtcSummary } from "./summary.js";
|
|
16
|
-
// ---------- bridge over the real governed pipelines --------------------------
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Unwrap a web tool's ToolResult back into the PTC `{ok,data}`/`{ok:false,
|
|
19
|
+
* error}` shape. The governed tools return `{output}` strings; a structured
|
|
20
|
+
* error renders with result.ts's pinned `[<tool> 失败 · phase=<phase>]`
|
|
21
|
+
* (optionally ` · line=<n>`) header line — the only machine-readable marker
|
|
22
|
+
* available at this boundary, so it is the split. Anything else (inline
|
|
23
|
+
* text, an offload handle block) is an ok result; a handle's ref is already
|
|
24
|
+
* in the text for the program to feed back through tm.fetch.
|
|
22
25
|
*/
|
|
23
|
-
|
|
26
|
+
function unwrapToolOutput(tool, output) {
|
|
27
|
+
const m = /^\[(\S+) 失败 · phase=([a-z]+)(?: · line=(-?\d+))?\]\n/.exec(output);
|
|
28
|
+
if (m && m[1] === tool) {
|
|
29
|
+
const phase = m[2];
|
|
30
|
+
const body = { tool, phase, message: output.slice(m[0].length) };
|
|
31
|
+
if (m[3] !== undefined)
|
|
32
|
+
Object.assign(body, { line: Number(m[3]) });
|
|
33
|
+
return { ok: false, error: body };
|
|
34
|
+
}
|
|
35
|
+
return { ok: true, data: output };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the OFFICIAL permission request the bridge raises for a bridged web
|
|
39
|
+
* call. The permission name is the tool itself (`tm_search` / `tm_webfetch`),
|
|
40
|
+
* so the host evaluates the CALLER'S own ruleset — exactly as a direct tool
|
|
41
|
+
* call would: `deny` for a non-web role, and for a web-granted role
|
|
42
|
+
* (lead/researcher) the default ruleset is `ask`, so each bridged web call
|
|
43
|
+
* pops a confirmation dialog (store an `allow` rule to skip it) — NOT a
|
|
44
|
+
* silent pass. The patterns carry the target host (webfetch) / engine
|
|
45
|
+
* (search) so any dialog that DOES pop is informative and no-secret.
|
|
46
|
+
*/
|
|
47
|
+
export function webBridgeAsk(tool, args) {
|
|
48
|
+
if (tool === "tm_webfetch") {
|
|
49
|
+
const url = typeof args.url === "string" ? args.url : "";
|
|
50
|
+
let host = "";
|
|
51
|
+
try {
|
|
52
|
+
host = url ? new URL(url).hostname : "";
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
host = "";
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
permission: "tm_webfetch",
|
|
59
|
+
patterns: host ? [host, url] : ["*"],
|
|
60
|
+
metadata: { tool, via: "tm_ptc_run", host },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const engine = typeof args.engine === "string" ? args.engine : "auto";
|
|
64
|
+
return {
|
|
65
|
+
permission: "tm_search",
|
|
66
|
+
patterns: [`engine:${engine}`],
|
|
67
|
+
metadata: { tool, via: "tm_ptc_run", engine },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function pipelineBridge(pipelines, ctx, web) {
|
|
24
71
|
const pick = (tool) => tool === "tm_read"
|
|
25
72
|
? pipelines.tmRead
|
|
26
73
|
: tool === "tm_grep"
|
|
27
74
|
? pipelines.tmGrep
|
|
28
75
|
: tool === "tm_bash"
|
|
29
76
|
? pipelines.tmBash
|
|
30
|
-
:
|
|
77
|
+
: tool === "tm_fetch"
|
|
78
|
+
? pipelines.tmFetch
|
|
79
|
+
: tool === "tm_search"
|
|
80
|
+
? web?.tm_search
|
|
81
|
+
: web?.tm_webfetch;
|
|
31
82
|
return {
|
|
32
83
|
async call(tool, args) {
|
|
33
84
|
if (!BRIDGE_ALLOW.includes(tool)) {
|
|
34
85
|
return { ok: false, error: tmError(tool, "args", "工具不在桥接白名单内").error };
|
|
35
86
|
}
|
|
36
|
-
const
|
|
87
|
+
const target = pick(tool);
|
|
88
|
+
if (!target) {
|
|
89
|
+
// a web bridge the caller's role / config never enabled
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: tmError(tool, "args", "web 桥未启用(TM_PTC_WEB_BRIDGE=off 或未装配)").error,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// C2: an ACTIVE role gate for bridged web — BEFORE execute. The bridge
|
|
96
|
+
// invokes the web tool's execute() directly, BYPASSING the host tool
|
|
97
|
+
// dispatch where agents.ts's `tm_search`/`tm_webfetch` = deny would
|
|
98
|
+
// otherwise block a non-web role, and the tools' own ctx.ask only pops
|
|
99
|
+
// for OUT-of-allowlist hosts (every default engine host is seeded). So
|
|
100
|
+
// without this the web bridge was a privilege-escalation path. Raise the
|
|
101
|
+
// SAME ruleset a direct call resolves: ctx.ask with the web permission
|
|
102
|
+
// name + target host/engine. A web-granted role hits the default `ask`
|
|
103
|
+
// rule → a confirmation dialog per call (an `allow` rule skips it); a
|
|
104
|
+
// non-web role's deny rule throws → a permission error step; a
|
|
105
|
+
// rejection or a missing ask bridge is fail-CLOSED.
|
|
106
|
+
if (tool === "tm_search" || tool === "tm_webfetch") {
|
|
107
|
+
const outcome = await askUserForTarget(ctx, webBridgeAsk(tool, args ?? {}));
|
|
108
|
+
if (outcome !== "approved") {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
error: tmError(tool, "permission", outcome === "rejected"
|
|
112
|
+
? `角色无权通过 PTC 桥接使用 ${tool}(ruleset deny / 用户拒绝);web 桥仅授予 lead/researcher。`
|
|
113
|
+
: `无法通过桥接校验 ${tool} 权限(ctx.ask 不可用)——已按最小权限拒绝。`).error,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const res = typeof target === "function"
|
|
118
|
+
? await target(args ?? {}, ctx)
|
|
119
|
+
: await target.execute(args ?? {}, ctx);
|
|
37
120
|
if (res && typeof res === "object" && "error" in res) {
|
|
38
121
|
return { ok: false, error: res.error };
|
|
39
122
|
}
|
|
123
|
+
if (tool === "tm_search" || tool === "tm_webfetch") {
|
|
124
|
+
return unwrapToolOutput(tool, String(res?.output ?? res ?? ""));
|
|
125
|
+
}
|
|
40
126
|
return { ok: true, data: res };
|
|
41
127
|
},
|
|
42
128
|
};
|
|
43
129
|
}
|
|
44
130
|
// ---------- the tool definition ----------------------------------------------
|
|
45
|
-
const PTC_RUN_DESCRIPTION = `Batch orchestration: ONE async program makes N governed tm_* calls with ZERO LLM round-trips — use it INSTEAD of chaining ≥3 tm_read / tm_grep / tm_bash calls toward the same goal (multi-file recon, bulk grep+read aggregation, cross-referencing search results). Only a char-pinned aggregation summary returns to context, so ALWAYS \`return\` the aggregated value at the end of the program: unreturned inline results are discarded (offload handles stay retrievable via tm.fetch).
|
|
131
|
+
const PTC_RUN_DESCRIPTION = `Batch orchestration: ONE async program makes N governed tm_* calls with ZERO LLM round-trips — use it INSTEAD of chaining ≥3 tm_read / tm_grep / tm_bash calls toward the same goal (multi-file recon, bulk grep+read aggregation, cross-referencing search results). Best for READ-ONLY fan-out: reading many files, aggregating many searches, or any parallel read that would otherwise cost one LLM turn per call. Only a char-pinned aggregation summary returns to context, so ALWAYS \`return\` the aggregated value at the end of the program: unreturned inline results are discarded (offload handles stay retrievable via tm.fetch).
|
|
46
132
|
|
|
47
133
|
- Example: \`const out = []; for (const p of ["a.ts", "b.ts", "c.ts"]) { const f = await tm.read({ path: p }); if (f.ok) out.push({ p, head: String(f.data).slice(0, 400) }); } return out;\`
|
|
48
|
-
- program: an async function body. Available: \`tm.read(args)\`, \`tm.grep(args)\`, \`tm.bash(args)\`, \`tm.fetch(args)\` — same args as the tm_* four. Each returns \`{ok:true, data}\` (data is already governed: inline text, or an offload handle you can tm.fetch again) or \`{ok:false, error:{tool,phase,line?,message}}\`. \`return\` a value; it is JSON-serialized into the summary (capped 2000 chars, oversized → handle).
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
134
|
+
- program: an async function body. Available: \`tm.read(args)\`, \`tm.grep(args)\`, \`tm.bash(args)\`, \`tm.fetch(args)\` — same args as the tm_* four — plus the WEB bridges \`await tm.search({ query, engine? })\` and \`await tm.webfetch({ url, fields? })\` (identical args to tm_search / tm_webfetch). Each returns \`{ok:true, data}\` (data is already governed: inline text, or an offload handle you can tm.fetch again) or \`{ok:false, error:{tool,phase,line?,message}}\`. \`return\` a value; it is JSON-serialized into the summary (capped 2000 chars, oversized → handle).
|
|
135
|
+
- Web bridge gating: tm.search / tm.webfetch run the REAL web tools under YOUR calling session's ctx, so the host evaluates them against YOUR agent's ruleset — a role without a web grant gets a \`permission\` error per call (bridged web is not a way around your tool whitelist), and \`TM_PTC_WEB_BRIDGE=off\` disables both. GETs are idempotent, so a transient client/execute/store failure retries once; a permission/args error never does.
|
|
136
|
+
- budgets (optional, tighten-only; clamped to TM_PTC_* ceilings): max_calls, max_errors, timeout_ms. Hitting any budget stops the whole run (produced output is NOT lost). Floors: max_calls 1 / max_errors 1 / timeout_ms 5000 ms; the summary echoes the EFFECTIVE budgets and flags every CLAMPED field (so a silently-narrowed budget is visible, not just the final number). Errors are retried at most once and only on idempotent phases (client/execute/store).
|
|
137
|
+
- Governance is NOT bypassed: every bridged call runs the full tm_* pipeline (P2 path scope, P3 allowlist, R6, threshold offload + handles, TTL). Status is one of ok | stopped-error-budget | stopped-call-budget | timeout | engine-error | program-error (program-error = the program itself threw — no auto-degrade re-run; engine-error = engine fault — auto mode re-runs the WHOLE program on the inline engine).
|
|
138
|
+
- Programs whose EXECUTABLE structure contains require/import/process/globalThis/Deno/Bun/fs/net/child_process are rejected before execution (static pre-scan, an auxiliary guard — NOT a security boundary). The pre-scan STRIPS string / template-literal / comment bodies first, so a grep pattern or log message that merely MENTIONS one of those words is fine — you do NOT need to obfuscate them; only a real \`require(...)\` / \`process.x\` call site is caught.
|
|
52
139
|
- Engine: TM_PTC_ENGINE=auto|worker|inline (auto tries worker first; on engine failure it re-runs the WHOLE program on inline with a degraded-engine mark — bridged calls are read-only, so a re-run is safe, and the fallback starts with fresh budgets under the same wall-clock deadline).`;
|
|
53
140
|
/** Build the tm_ptc_run ToolDefinition. Registered in the tool segment
|
|
54
141
|
* when the agent's whitelist grants tm_ptc_run (M3). `deps.args` is the
|
|
@@ -56,7 +143,14 @@ const PTC_RUN_DESCRIPTION = `Batch orchestration: ONE async program makes N gove
|
|
|
56
143
|
* descriptor fallback below keeps the tool usable without zod. */
|
|
57
144
|
export function buildPtcRunTool(deps) {
|
|
58
145
|
const { cfg, store, nextStepId, ctx, accessToken } = deps;
|
|
59
|
-
|
|
146
|
+
// Per-call bridge factory. The bridge MUST bind the CALLER's execute ctx,
|
|
147
|
+
// not the assembly-time `{ directory }`: tm_search / tm_webfetch run their
|
|
148
|
+
// `ctx.ask` permission probe against the calling agent's session ruleset, so
|
|
149
|
+
// a non-web role's bridged web call is denied by the host exactly as if that
|
|
150
|
+
// agent had called the tool directly. A test-provided `deps.bridge` still
|
|
151
|
+
// wins (the §9 suite injects its own mock).
|
|
152
|
+
const makeBridge = (callCtx) => deps.bridge ??
|
|
153
|
+
(deps.pipelines ? pipelineBridge(deps.pipelines, callCtx ?? ctx, deps.webTools) : undefined);
|
|
60
154
|
const expireAt = () => Date.now() + cfg.blackboardTtlDays * 24 * 60 * 60 * 1000;
|
|
61
155
|
return {
|
|
62
156
|
description: PTC_RUN_DESCRIPTION,
|
|
@@ -65,20 +159,23 @@ export function buildPtcRunTool(deps) {
|
|
|
65
159
|
label: { descriptor: "label: string (optional, ≤80 chars)" },
|
|
66
160
|
budgets: { descriptor: "budgets: { max_calls?, max_errors?, timeout_ms? } (optional, tighten-only)" },
|
|
67
161
|
},
|
|
68
|
-
execute: async (rawArgs) => {
|
|
162
|
+
execute: async (rawArgs, callCtx) => {
|
|
69
163
|
const parsed = parsePtcArgs(rawArgs ?? {}, cfg);
|
|
70
164
|
if (!parsed.ok) {
|
|
71
165
|
return { output: `[${parsed.error.error.tool} 失败 · phase=${parsed.error.error.phase}]\n${parsed.error.error.message}` };
|
|
72
166
|
}
|
|
73
|
-
// static pre-scan — reject programs with banned tokens
|
|
74
|
-
// engine runs
|
|
167
|
+
// static pre-scan — reject programs with banned tokens in EXECUTABLE
|
|
168
|
+
// structure before any engine runs (string/comment literals are stripped
|
|
169
|
+
// first, so a grep pattern that merely MENTIONS `require` is not caught).
|
|
75
170
|
const pscan = staticPscan(parsed.args.program);
|
|
76
171
|
if (pscan.rejected) {
|
|
77
172
|
return {
|
|
78
|
-
output: `[tm_ptc_run 失败 · phase=args]\n程序包含禁止标识符(${pscan.tokens.join(", ")}),已拒绝执行。PTC 桥接仅暴露 tm.read/tm.grep/tm.bash/tm.fetch
|
|
173
|
+
output: `[tm_ptc_run 失败 · phase=args]\n程序包含禁止标识符(${pscan.tokens.join(", ")}),已拒绝执行。PTC 桥接仅暴露 tm.read/tm.grep/tm.bash/tm.fetch/tm.search/tm.webfetch(web 两项受 TM_PTC_WEB_BRIDGE 与角色权限门控);require/import/process 等不在桥接白名单内。`,
|
|
79
174
|
};
|
|
80
175
|
}
|
|
81
|
-
|
|
176
|
+
// Re-bind the bridge to THIS call's ctx (role-gated web permission).
|
|
177
|
+
const bridge = makeBridge(callCtx);
|
|
178
|
+
if (!bridge) {
|
|
82
179
|
return {
|
|
83
180
|
output: "[tm_ptc_run 失败 · phase=client]\n桥接管线未提供。",
|
|
84
181
|
};
|
|
@@ -117,7 +214,7 @@ export function buildPtcRunTool(deps) {
|
|
|
117
214
|
parentStepId,
|
|
118
215
|
cfg,
|
|
119
216
|
store,
|
|
120
|
-
bridge
|
|
217
|
+
bridge,
|
|
121
218
|
...(deps.engine ? { engine: deps.engine } : {}),
|
|
122
219
|
});
|
|
123
220
|
const content = renderPtcSummary(outcome, offloadBlock);
|
package/dist/tm/ptc/tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/tm/ptc/tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAiB,MAAM,cAAc,CAAA;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGjD,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/tm/ptc/tool.ts"],"names":[],"mappings":"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"}
|
package/dist/tm/pty.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tm_pty — ASYNC command execution on a host PTY (issue #6's second half).
|
|
3
|
+
*
|
|
4
|
+
* The problem it removes: a three-suite serial `npm test` chain where every
|
|
5
|
+
* step carries a 120 s timeout is minutes of dead air in ONE blocked tool
|
|
6
|
+
* call. A PTY session is the host's own long-lived process — created and
|
|
7
|
+
* polled in milliseconds — so the agent can start the work, keep doing
|
|
8
|
+
* something else, and check back.
|
|
9
|
+
*
|
|
10
|
+
* What the surface actually offers (verified against the shipped SDK):
|
|
11
|
+
* `pty.create/list/get/update/remove/connect` with
|
|
12
|
+
* `Pty = {id,title,command,args,cwd,status:"running"|"exited",pid}` — i.e.
|
|
13
|
+
* NO stdin and NO output endpoint on the REST surface (terminal I/O rides the
|
|
14
|
+
* `connect` websocket, which this plugin deliberately does not speak). So
|
|
15
|
+
* tm_pty starts, watches and stops sessions; it never pretends to capture
|
|
16
|
+
* output, and the command writes its own log which the governed readers pick
|
|
17
|
+
* up. An agent that needs the transcript reads the file, not this tool.
|
|
18
|
+
*
|
|
19
|
+
* Governance — this is the important part. A tool that could spawn an
|
|
20
|
+
* arbitrary process would be a bypass around the R2 danger face (delete /
|
|
21
|
+
* git push / npm publish / package install are dialog-gated built-in bash).
|
|
22
|
+
* So every start is gated TWICE before anything is created:
|
|
23
|
+
* 1. the R6 classifier runs on the command (a blocked shape is refused with
|
|
24
|
+
* the standard privacy-preserving error, same source as tm_bash);
|
|
25
|
+
* 2. the OFFICIAL confirmation dialog decides (`permission:"tm_pty"` is
|
|
26
|
+
* mapped to {"*":"ask"} in agents.ts so the explicit rule beats the
|
|
27
|
+
* `tm_*` allow — without that map the ask would resolve silently and the
|
|
28
|
+
* user would never see it). No ask bridge on the host => refuse; a
|
|
29
|
+
* rejection => refuse. The plugin never self-allows.
|
|
30
|
+
* Concurrent sessions are capped (`TM_PTY_MAX`), and an id we did not start
|
|
31
|
+
* is not ours to kill.
|
|
32
|
+
*/
|
|
33
|
+
import type { EnvProtectMode } from "../envprotect.js";
|
|
34
|
+
import type { ToolDefinition } from "../types.js";
|
|
35
|
+
import type { TmPipelines } from "./pipelines.js";
|
|
36
|
+
export interface PtyRecord {
|
|
37
|
+
id: string;
|
|
38
|
+
command: string;
|
|
39
|
+
cwd: string;
|
|
40
|
+
title: string;
|
|
41
|
+
startedAt: number;
|
|
42
|
+
status: "running" | "exited";
|
|
43
|
+
pid?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface PtyDeps {
|
|
46
|
+
client: unknown;
|
|
47
|
+
pipelines: TmPipelines;
|
|
48
|
+
mode?: EnvProtectMode;
|
|
49
|
+
max?: number;
|
|
50
|
+
now?: () => number;
|
|
51
|
+
/** Called when the tool wants a fresh look at live state. */
|
|
52
|
+
onTracked?: (records: PtyRecord[]) => void;
|
|
53
|
+
}
|
|
54
|
+
/** The command line a human (and the dialog) reads: command + args, no env. */
|
|
55
|
+
export declare function ptyCommandLine(command: string, args: readonly string[]): string;
|
|
56
|
+
/** Does `line` hit one of the host's escalate-to-dialog bash globs (the R2
|
|
57
|
+
* danger face: rm/del/Remove-Item, git push|commit, npm publish|install,
|
|
58
|
+
* kill/systemctl, …)? Same pattern table the built-in bash config uses, so
|
|
59
|
+
* tm_pty refuses exactly the shapes that would otherwise need a human. */
|
|
60
|
+
export declare function matchesAskGlob(line: string, patterns: readonly string[]): string | null;
|
|
61
|
+
/** Red line / deny check — the SAME classifiers as tm_bash and built-in
|
|
62
|
+
* bash (R6 env face via classifyBashCommand, R2 danger face via the ask
|
|
63
|
+
* globs), so tm_pty cannot become a channel for what the other two refuse. */
|
|
64
|
+
export declare function ptyCommandBlocked(line: string, mode: EnvProtectMode): string | null;
|
|
65
|
+
/** Verdict table for a bounded poll: what the agent may act on. */
|
|
66
|
+
export declare function ptyVerdictLine(r: PtyRecord, now: number): string;
|
|
67
|
+
export declare function buildTmPtyTool(deps: PtyDeps): ToolDefinition & {
|
|
68
|
+
tracked: () => PtyRecord[];
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=pty.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty.d.ts","sourceRoot":"","sources":["../../src/tm/pty.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,aAAa,CAAA;AAI7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,WAAW,CAAA;IACtB,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;CAC3C;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAG/E;AAED;;;2EAG2E;AAC3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAcvF;AAED;;+EAE+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CAQnF;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,GAAG;IAAE,OAAO,EAAE,MAAM,SAAS,EAAE,CAAA;CAAE,CA+K7F"}
|
package/dist/tm/pty.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tm_pty — ASYNC command execution on a host PTY (issue #6's second half).
|
|
3
|
+
*
|
|
4
|
+
* The problem it removes: a three-suite serial `npm test` chain where every
|
|
5
|
+
* step carries a 120 s timeout is minutes of dead air in ONE blocked tool
|
|
6
|
+
* call. A PTY session is the host's own long-lived process — created and
|
|
7
|
+
* polled in milliseconds — so the agent can start the work, keep doing
|
|
8
|
+
* something else, and check back.
|
|
9
|
+
*
|
|
10
|
+
* What the surface actually offers (verified against the shipped SDK):
|
|
11
|
+
* `pty.create/list/get/update/remove/connect` with
|
|
12
|
+
* `Pty = {id,title,command,args,cwd,status:"running"|"exited",pid}` — i.e.
|
|
13
|
+
* NO stdin and NO output endpoint on the REST surface (terminal I/O rides the
|
|
14
|
+
* `connect` websocket, which this plugin deliberately does not speak). So
|
|
15
|
+
* tm_pty starts, watches and stops sessions; it never pretends to capture
|
|
16
|
+
* output, and the command writes its own log which the governed readers pick
|
|
17
|
+
* up. An agent that needs the transcript reads the file, not this tool.
|
|
18
|
+
*
|
|
19
|
+
* Governance — this is the important part. A tool that could spawn an
|
|
20
|
+
* arbitrary process would be a bypass around the R2 danger face (delete /
|
|
21
|
+
* git push / npm publish / package install are dialog-gated built-in bash).
|
|
22
|
+
* So every start is gated TWICE before anything is created:
|
|
23
|
+
* 1. the R6 classifier runs on the command (a blocked shape is refused with
|
|
24
|
+
* the standard privacy-preserving error, same source as tm_bash);
|
|
25
|
+
* 2. the OFFICIAL confirmation dialog decides (`permission:"tm_pty"` is
|
|
26
|
+
* mapped to {"*":"ask"} in agents.ts so the explicit rule beats the
|
|
27
|
+
* `tm_*` allow — without that map the ask would resolve silently and the
|
|
28
|
+
* user would never see it). No ask bridge on the host => refuse; a
|
|
29
|
+
* rejection => refuse. The plugin never self-allows.
|
|
30
|
+
* Concurrent sessions are capped (`TM_PTY_MAX`), and an id we did not start
|
|
31
|
+
* is not ours to kill.
|
|
32
|
+
*/
|
|
33
|
+
import { classifyBashCommand, R2_DANGER_BASH_ASK_PATTERNS } from "../envprotect.js";
|
|
34
|
+
import { askUserForTarget } from "./perm-ask.js";
|
|
35
|
+
import { tmError, toToolResult } from "./result.js";
|
|
36
|
+
import { unwrapClientResult } from "./client-unwrap.js";
|
|
37
|
+
import { shorten } from "./config.js";
|
|
38
|
+
/** The command line a human (and the dialog) reads: command + args, no env. */
|
|
39
|
+
export function ptyCommandLine(command, args) {
|
|
40
|
+
const parts = [String(command ?? "").trim(), ...args.map((a) => String(a ?? "").trim()).filter(Boolean)].filter(Boolean);
|
|
41
|
+
return parts.join(" ");
|
|
42
|
+
}
|
|
43
|
+
/** Does `line` hit one of the host's escalate-to-dialog bash globs (the R2
|
|
44
|
+
* danger face: rm/del/Remove-Item, git push|commit, npm publish|install,
|
|
45
|
+
* kill/systemctl, …)? Same pattern table the built-in bash config uses, so
|
|
46
|
+
* tm_pty refuses exactly the shapes that would otherwise need a human. */
|
|
47
|
+
export function matchesAskGlob(line, patterns) {
|
|
48
|
+
const norm = String(line ?? "").trim().replace(/\s+/g, " ");
|
|
49
|
+
if (!norm)
|
|
50
|
+
return null;
|
|
51
|
+
for (const raw of patterns) {
|
|
52
|
+
const pat = String(raw ?? "").trim().replace(/\s+/g, " ");
|
|
53
|
+
if (!pat || pat === "*")
|
|
54
|
+
continue;
|
|
55
|
+
if (pat.endsWith(" *")) {
|
|
56
|
+
const head = pat.slice(0, -2);
|
|
57
|
+
if (norm.startsWith(head + " "))
|
|
58
|
+
return pat;
|
|
59
|
+
}
|
|
60
|
+
else if (norm === pat) {
|
|
61
|
+
return pat;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/** Red line / deny check — the SAME classifiers as tm_bash and built-in
|
|
67
|
+
* bash (R6 env face via classifyBashCommand, R2 danger face via the ask
|
|
68
|
+
* globs), so tm_pty cannot become a channel for what the other two refuse. */
|
|
69
|
+
export function ptyCommandBlocked(line, mode) {
|
|
70
|
+
const text = line.trim();
|
|
71
|
+
if (!text)
|
|
72
|
+
return "empty-command";
|
|
73
|
+
if (/[$(`]/.test(text))
|
|
74
|
+
return "substitution";
|
|
75
|
+
if (/>\s*(?:\/dev\/sd|C:\\)/i.test(text) || /\brm\s+-rf\s+\/(\s|$)/.test(text))
|
|
76
|
+
return "device-write";
|
|
77
|
+
const danger = matchesAskGlob(text, R2_DANGER_BASH_ASK_PATTERNS);
|
|
78
|
+
if (danger)
|
|
79
|
+
return "danger-face";
|
|
80
|
+
return classifyBashCommand(text, mode, []);
|
|
81
|
+
}
|
|
82
|
+
/** Verdict table for a bounded poll: what the agent may act on. */
|
|
83
|
+
export function ptyVerdictLine(r, now) {
|
|
84
|
+
const secs = Math.max(0, Math.round(((r.startedAt ? now - r.startedAt : 0) / 1000)));
|
|
85
|
+
return `${r.id} · ${r.status === "running" ? `运行中 ${secs}s` : "已退出"} · pid ${r.pid ?? "?"} · ${shorten(r.command, 90)} · 日志:${r.title}`;
|
|
86
|
+
}
|
|
87
|
+
export function buildTmPtyTool(deps) {
|
|
88
|
+
const mode = deps.mode ?? "standard";
|
|
89
|
+
const max = Math.max(1, Number(deps.max) || 4);
|
|
90
|
+
const now = deps.now ?? (() => Date.now());
|
|
91
|
+
const store = deps.pipelines.store;
|
|
92
|
+
const log = (e) => store.appendTrajectory({ tool: "tm_pty", ...e });
|
|
93
|
+
const tracked = new Map();
|
|
94
|
+
const api = () => {
|
|
95
|
+
const pty = deps.client?.pty;
|
|
96
|
+
if (!pty || typeof pty.create !== "function" || typeof pty.get !== "function")
|
|
97
|
+
return null;
|
|
98
|
+
return pty;
|
|
99
|
+
};
|
|
100
|
+
const tool = {
|
|
101
|
+
description: `Start and watch a long-running command in the host's OWN terminal sessions, without blocking the round (the built-in bash waits; this one returns at once).
|
|
102
|
+
- { action:"start", command, args?:string[], cwd?, log? } → R6-classified, then the OFFICIAL confirmation dialog decides, then a PTY session is created and its id returned in milliseconds. Independent slow steps (test suites, builds, watchers) each get their own session so they overlap instead of serialising.
|
|
103
|
+
- NO OUTPUT CAPTURE: this tool starts/watches/stops processes and never reads their transcript (terminal I/O is a websocket this plugin does not speak). Write a log yourself — append \`2>&1 | tee <log>\` to the command, pass the path as \`log\`, then read that file with tm_read / tm_fetch once the session exits.
|
|
104
|
+
- { action:"status", id } → running / exited (+ pid + elapsed), { action:"list" } → every session this session started, { action:"kill", id } → stop one of ours.
|
|
105
|
+
- A still-running session is not a failure: report it as an open item, or kill it — never leave a process the user did not approve running after you are done.
|
|
106
|
+
- Capped at ${Math.max(1, Number(deps.max) || 4)} concurrent sessions; every start re-asks the user. No dialog bridge → refused (this is not a bypass channel).`,
|
|
107
|
+
args: {
|
|
108
|
+
action: { descriptor: 'action: start|status|list|kill (default "start")' },
|
|
109
|
+
command: { descriptor: "command: program to run, e.g. npm (start)" },
|
|
110
|
+
args: { descriptor: "args: string[] argv for the program, e.g. [\"test\"] (start)" },
|
|
111
|
+
cwd: { descriptor: "cwd: working directory, defaults to the session directory (start)" },
|
|
112
|
+
log: { descriptor: "log: path where the command tees its output, so tm_read can read it later (start)" },
|
|
113
|
+
id: { descriptor: "id: PTY session id (status|kill)" },
|
|
114
|
+
},
|
|
115
|
+
execute: async (rawArgs, ctx) => {
|
|
116
|
+
const name = "tm_pty";
|
|
117
|
+
try {
|
|
118
|
+
const args = (rawArgs ?? {});
|
|
119
|
+
const action = String(args.action ?? "start").trim().toLowerCase();
|
|
120
|
+
const pty = api();
|
|
121
|
+
if (!pty) {
|
|
122
|
+
return toToolResult(tmError(name, "client", "此宿主未暴露 client.pty(终端会话接口),tm_pty 不可用——用内置 bash 分段执行,或 tm_ptc_run 批量。"));
|
|
123
|
+
}
|
|
124
|
+
const directory = typeof ctx?.directory === "string"
|
|
125
|
+
? String(ctx.directory)
|
|
126
|
+
: process.cwd();
|
|
127
|
+
const c = (ctx ?? {});
|
|
128
|
+
log({ step_id: "pty", event: "call", action, session: String(c.sessionID ?? "") });
|
|
129
|
+
if (action === "list") {
|
|
130
|
+
const rows = [...tracked.values()];
|
|
131
|
+
if (!rows.length)
|
|
132
|
+
return toToolResult("本会话未启动任何 tm_pty 进程。");
|
|
133
|
+
return toToolResult(rows.map((r) => ptyVerdictLine(r, now())).join("\n"));
|
|
134
|
+
}
|
|
135
|
+
if (action === "status" || action === "kill") {
|
|
136
|
+
const id = String(args.id ?? "").trim();
|
|
137
|
+
if (!id)
|
|
138
|
+
return toToolResult(tmError(name, "args", `缺少 id(${action} 需要 tm_pty start 返回的会话 id)`));
|
|
139
|
+
if (action === "kill") {
|
|
140
|
+
// an id we did not start is the user's terminal, not ours to stop
|
|
141
|
+
if (!tracked.has(id)) {
|
|
142
|
+
return toToolResult(tmError(name, "governance", `会话 ${id} 不是本插件启动的——不代用户关闭终端。`));
|
|
143
|
+
}
|
|
144
|
+
if (typeof pty.remove !== "function")
|
|
145
|
+
return toToolResult(tmError(name, "client", "宿主无 pty.remove,无法停止会话。"));
|
|
146
|
+
const un = unwrapClientResult(await pty.remove({ path: { id } }));
|
|
147
|
+
if (!un.ok)
|
|
148
|
+
return toToolResult(tmError(name, "client", `停止失败:${shorten(un.message ?? "", 120)}`));
|
|
149
|
+
const rec = tracked.get(id);
|
|
150
|
+
if (rec)
|
|
151
|
+
rec.status = "exited";
|
|
152
|
+
log({ step_id: "pty", event: "kill", id });
|
|
153
|
+
return toToolResult(`已停止会话 ${id}${rec ? `(命令:${shorten(rec.command, 70)})` : ""}。`);
|
|
154
|
+
}
|
|
155
|
+
const un = unwrapClientResult(await pty.get({ path: { id } }));
|
|
156
|
+
if (!un.ok)
|
|
157
|
+
return toToolResult(tmError(name, "client", `查询失败:${shorten(un.message ?? "", 120)}`));
|
|
158
|
+
const info = un.data;
|
|
159
|
+
const rec = tracked.get(id);
|
|
160
|
+
if (rec && String(info.status ?? "") === "exited")
|
|
161
|
+
rec.status = "exited";
|
|
162
|
+
const status = String(info.status ?? "unknown");
|
|
163
|
+
return toToolResult([
|
|
164
|
+
`会话 ${id}:${status}${info.pid != null ? ` · pid ${info.pid}` : ""}`,
|
|
165
|
+
rec ? `已运行 ${Math.max(0, Math.round((now() - rec.startedAt) / 1000))}s · 命令 ${shorten(rec.command, 90)}` : "",
|
|
166
|
+
status === "running"
|
|
167
|
+
? "仍在运行:继续别的工作,稍后再 status;需要输出就读它的日志文件(tm_read)。"
|
|
168
|
+
: "已退出:用 tm_read 读日志文件确认结果,再报 EVIDENCE。",
|
|
169
|
+
]
|
|
170
|
+
.filter(Boolean)
|
|
171
|
+
.join("\n"));
|
|
172
|
+
}
|
|
173
|
+
if (action !== "start")
|
|
174
|
+
return toToolResult(tmError(name, "args", `未知 action "${shorten(action, 20)}"——可用: start|status|list|kill`));
|
|
175
|
+
const command = String(args.command ?? "").trim();
|
|
176
|
+
if (!command)
|
|
177
|
+
return toToolResult(tmError(name, "args", "缺少 command(要启动的程序,argv 用 args 传)"));
|
|
178
|
+
if (/[\n\r]/.test(command))
|
|
179
|
+
return toToolResult(tmError(name, "args", "command 必须是单条程序名,不要内嵌换行/多命令"));
|
|
180
|
+
const argv = Array.isArray(args.args) ? args.args.map((a) => String(a ?? "")) : [];
|
|
181
|
+
const line = ptyCommandLine(command, argv);
|
|
182
|
+
if (line.length > 4000)
|
|
183
|
+
return toToolResult(tmError(name, "args", "命令行过长"));
|
|
184
|
+
const blocked = ptyCommandBlocked(line, mode);
|
|
185
|
+
if (blocked) {
|
|
186
|
+
log({ step_id: "pty", event: "refused", category: blocked });
|
|
187
|
+
return toToolResult(tmError(name, "permission", `命令被 R6/R2 治理面拒绝(category=${blocked})——tm_pty 不是绕过确认窗的后门。改走内置 bash(会开官方弹窗)或缩小命令。`));
|
|
188
|
+
}
|
|
189
|
+
const running = [...tracked.values()].filter((r) => r.status === "running").length;
|
|
190
|
+
if (running >= max) {
|
|
191
|
+
return toToolResult(tmError(name, "governance", `并发上限 ${max} 已占满(${running} 个在跑)——先 tm_pty kill 或 status 收干净。`));
|
|
192
|
+
}
|
|
193
|
+
const cwd = typeof args.cwd === "string" && args.cwd.trim() ? args.cwd.trim() : directory;
|
|
194
|
+
const outcome = await askUserForTarget(ctx, {
|
|
195
|
+
permission: name,
|
|
196
|
+
patterns: [line],
|
|
197
|
+
metadata: { tool: name, command: shorten(line, 200), cwd: shorten(cwd, 120) },
|
|
198
|
+
});
|
|
199
|
+
if (outcome !== "approved") {
|
|
200
|
+
log({ step_id: "pty", event: outcome === "rejected" ? "dialog-rejected" : "no-dialog" });
|
|
201
|
+
return toToolResult(tmError(name, "permission", `后台执行需要用户批准:${outcome === "rejected" ? "用户未批准(或已超时自动拒绝)。" : "宿主无法弹出确认窗口。"}`));
|
|
202
|
+
}
|
|
203
|
+
const created = unwrapClientResult(await pty.create({
|
|
204
|
+
body: { command, ...(argv.length ? { args: argv } : {}), cwd, title: `tm_pty: ${shorten(line, 60)}` },
|
|
205
|
+
}));
|
|
206
|
+
if (!created.ok)
|
|
207
|
+
return toToolResult(tmError(name, "client", `创建终端会话失败:${shorten(created.message ?? "", 120)}`));
|
|
208
|
+
const info = created.data;
|
|
209
|
+
const id = String(info.id ?? "").trim();
|
|
210
|
+
if (!id)
|
|
211
|
+
return toToolResult(tmError(name, "client", "宿主未返回会话 id"));
|
|
212
|
+
const logPath = typeof args.log === "string" && args.log.trim() ? args.log.trim() : "";
|
|
213
|
+
const rec = {
|
|
214
|
+
id,
|
|
215
|
+
command: line,
|
|
216
|
+
cwd,
|
|
217
|
+
title: logPath || "(未指定日志文件)",
|
|
218
|
+
startedAt: now(),
|
|
219
|
+
status: "running",
|
|
220
|
+
pid: typeof info.pid === "number" ? info.pid : undefined,
|
|
221
|
+
};
|
|
222
|
+
tracked.set(id, rec);
|
|
223
|
+
deps.onTracked?.([...tracked.values()]);
|
|
224
|
+
log({ step_id: "pty", event: "start", id, cwd: shorten(cwd, 120), command: shorten(line, 200) });
|
|
225
|
+
return toToolResult([
|
|
226
|
+
`已启动(非阻塞):${id} · pid ${rec.pid ?? "?"} · ${shorten(line, 120)}`,
|
|
227
|
+
`它不会把输出送回给你——现在去做别的(派发、规划、读代码),稍后 tm_pty { action:"status", id:"${id}" }。`,
|
|
228
|
+
logPath
|
|
229
|
+
? `退出后用 tm_read 读 ${logPath} 取结果。`
|
|
230
|
+
: "下次启动同类任务时带 log 参数(例:cmd 2>&1 | tee 该路径),否则你无法取证它的输出。",
|
|
231
|
+
"用完请 tm_pty kill(或让用户确认留下)。",
|
|
232
|
+
].join("\n"));
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
const e = err;
|
|
236
|
+
return toToolResult(tmError("tm_pty", "execute", `tm_pty 失败:${shorten(e?.message ?? err, 160)}`));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
return Object.assign(tool, { tracked: () => [...tracked.values()] });
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=pty.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty.js","sourceRoot":"","sources":["../../src/tm/pty.ts"],"names":[],"mappings":"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"}
|