@wrongstack/cli 1.0.6 → 1.0.7
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/dist/{acp-GOQURNC2.js → acp-A5EQEXQO.js} +61 -6
- package/dist/{auth-WBXXIQUO.js → auth-3K6EN5JP.js} +3 -3
- package/dist/boot/container-wiring.d.ts +2 -0
- package/dist/{chunk-VUVOMXWP.js → chunk-3VXYY5JC.js} +8 -1
- package/dist/{chunk-NR7BD73C.js → chunk-4OLAZIIN.js} +2 -2
- package/dist/{chunk-5A5GCSJC.js → chunk-FSZ5QU3M.js} +2 -2
- package/dist/{chunk-KNMIBKLV.js → chunk-JY52HWL6.js} +47 -1
- package/dist/{chunk-QDQPF4PB.js → chunk-RLCKKLJA.js} +7 -7
- package/dist/{chunk-VPS2SJW4.js → chunk-RMQNCHGO.js} +5 -3
- package/dist/{chunk-PQZPHGGD.js → chunk-XIZZ2EXG.js} +34 -2
- package/dist/{cli-context-BYKJDAFF.js → cli-context-TIAP6YQK.js} +83 -12
- package/dist/cli-context.d.ts +2 -0
- package/dist/{cli-main-5WZBH3WA.js → cli-main-AKJHDSHX.js} +1006 -634
- package/dist/config-doctor.d.ts +1 -1
- package/dist/execute-deps.d.ts +3 -1
- package/dist/{execution-Z6A26EG6.js → execution-FJE27VQ6.js} +80 -3
- package/dist/{hq-V4PHEETW.js → hq-G5PCP325.js} +7 -5
- package/dist/hq-command-controller.d.ts +15 -0
- package/dist/{hq-server-MZ7DV6X7.js → hq-server-G3VVVMIG.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/input-reader.d.ts +2 -2
- package/dist/{modeldiag-46CMWUNM.js → modeldiag-CJSFHZ3J.js} +55 -31
- package/dist/permission-prompt-mirror.d.ts +70 -0
- package/dist/permission-prompt.d.ts +10 -1
- package/dist/{providers-models-6MOGAOVK.js → providers-models-LFNZ5WI7.js} +2 -2
- package/dist/{short-circuit-flags-KBTYSJTW.js → short-circuit-flags-RAT6UZVI.js} +2 -2
- package/dist/slash-commands/subagent-models.d.ts +4 -0
- package/dist/subagent-models/panel-service.d.ts +28 -0
- package/dist/subcommands/handlers/modeldiag-test.d.ts +7 -0
- package/dist/{subcommands-DEYSYHIM.js → subcommands-VFVUTYAV.js} +2 -2
- package/dist/webui-server/prefs-seeding.d.ts +8 -0
- package/dist/{webui-server-OBV5JYJV.js → webui-server-3NYXBZHX.js} +36 -12
- package/dist/wiring/hq-telemetry.d.ts +7 -0
- package/package.json +28 -28
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
refreshAcpRegistry,
|
|
6
6
|
setupProvider
|
|
7
7
|
} from "./chunk-KEPABQYT.js";
|
|
8
|
+
import {
|
|
9
|
+
createHqCommandDispatcher
|
|
10
|
+
} from "./chunk-JY52HWL6.js";
|
|
8
11
|
import {
|
|
9
12
|
startCliHqConnection
|
|
10
13
|
} from "./chunk-TE3Q32JP.js";
|
|
@@ -36,18 +39,50 @@ import { WebSocketServer } from "ws";
|
|
|
36
39
|
|
|
37
40
|
// src/acp-hq-telemetry.ts
|
|
38
41
|
import { AgentStatusTracker } from "@wrongstack/core/coordination";
|
|
39
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
createApprovalRegistry,
|
|
44
|
+
startApprovalTelemetryBridge,
|
|
45
|
+
startSessionTelemetryBridge
|
|
46
|
+
} from "@wrongstack/core/hq";
|
|
40
47
|
var NO_REGISTRY = { updateAgents: async () => void 0 };
|
|
41
48
|
function startAcpHqTelemetry(options) {
|
|
49
|
+
const entries = /* @__PURE__ */ new Map();
|
|
50
|
+
let stopped = false;
|
|
51
|
+
const resolveApproval = (toolUseId, decision, sessionId) => {
|
|
52
|
+
if (sessionId !== void 0) {
|
|
53
|
+
return entries.get(sessionId)?.approvals?.resolve(toolUseId, decision, sessionId) ?? false;
|
|
54
|
+
}
|
|
55
|
+
for (const entry of entries.values()) {
|
|
56
|
+
if (entry.approvals?.resolve(toolUseId, decision) === true) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
42
60
|
const connection = startCliHqConnection({
|
|
43
61
|
clientKind: "acp",
|
|
44
62
|
projectRoot: options.projectRoot,
|
|
45
63
|
...options.projectName !== void 0 ? { projectName: options.projectName } : {},
|
|
46
64
|
...options.appConfig !== void 0 ? { appConfig: options.appConfig } : {},
|
|
47
|
-
capabilities: [
|
|
65
|
+
capabilities: [
|
|
66
|
+
"telemetry.publish",
|
|
67
|
+
"mailbox.summary",
|
|
68
|
+
"fleet.summary",
|
|
69
|
+
"session.summary",
|
|
70
|
+
// Approvals are the ONLY control this host accepts. `control.receive` is
|
|
71
|
+
// the server's gate for any command at all, so it has to be declared;
|
|
72
|
+
// everything except `approve` falls through the dispatcher and is
|
|
73
|
+
// refused, because an ACP session has no mailbox to steer and no leader
|
|
74
|
+
// to abort.
|
|
75
|
+
"control.receive",
|
|
76
|
+
"control.approve"
|
|
77
|
+
],
|
|
78
|
+
onCommand: createHqCommandDispatcher({
|
|
79
|
+
interruptLeader: () => false,
|
|
80
|
+
sessionTag: () => "acp",
|
|
81
|
+
ownsSession: (sessionId) => entries.has(sessionId),
|
|
82
|
+
allowRunCommand: () => false,
|
|
83
|
+
resolveApproval
|
|
84
|
+
})
|
|
48
85
|
});
|
|
49
|
-
const entries = /* @__PURE__ */ new Map();
|
|
50
|
-
let stopped = false;
|
|
51
86
|
const attach = (sessionId, agent, cwd) => {
|
|
52
87
|
if (stopped || entries.has(sessionId)) return;
|
|
53
88
|
const publisher = connection.getPublisher();
|
|
@@ -73,7 +108,19 @@ function startAcpHqTelemetry(options) {
|
|
|
73
108
|
tracker.stop();
|
|
74
109
|
return;
|
|
75
110
|
}
|
|
76
|
-
|
|
111
|
+
const entry = { tracker, stopBridge };
|
|
112
|
+
try {
|
|
113
|
+
const approvals = createApprovalRegistry(events);
|
|
114
|
+
entry.approvals = approvals;
|
|
115
|
+
entry.stopApprovalBridge = startApprovalTelemetryBridge({
|
|
116
|
+
registry: approvals,
|
|
117
|
+
publisher,
|
|
118
|
+
projectRoot: cwd,
|
|
119
|
+
sessionId
|
|
120
|
+
});
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
entries.set(sessionId, entry);
|
|
77
124
|
};
|
|
78
125
|
const detach = (sessionId) => {
|
|
79
126
|
const entry = entries.get(sessionId);
|
|
@@ -83,6 +130,14 @@ function startAcpHqTelemetry(options) {
|
|
|
83
130
|
entry.stopBridge();
|
|
84
131
|
} catch {
|
|
85
132
|
}
|
|
133
|
+
try {
|
|
134
|
+
entry.stopApprovalBridge?.();
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
entry.approvals?.dispose();
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
86
141
|
try {
|
|
87
142
|
entry.tracker.stop();
|
|
88
143
|
} catch {
|
|
@@ -1102,4 +1157,4 @@ ${renderAcpBenchText(result)}
|
|
|
1102
1157
|
export {
|
|
1103
1158
|
acpCmd
|
|
1104
1159
|
};
|
|
1105
|
-
//# sourceMappingURL=acp-
|
|
1160
|
+
//# sourceMappingURL=acp-A5EQEXQO.js.map
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
runOAuthLoginKind,
|
|
15
15
|
runOAuthLoginMenu,
|
|
16
16
|
validateFamily
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-FSZ5QU3M.js";
|
|
18
18
|
import {
|
|
19
19
|
renderAuthLocalHelpToString,
|
|
20
20
|
wantsLocalHelp
|
|
@@ -23,7 +23,7 @@ import "./chunk-B4JLSXZB.js";
|
|
|
23
23
|
import {
|
|
24
24
|
restoreFlags
|
|
25
25
|
} from "./chunk-CSAPOCBP.js";
|
|
26
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-3VXYY5JC.js";
|
|
27
27
|
import {
|
|
28
28
|
activeProfileConfigPath
|
|
29
29
|
} from "./chunk-YMXXOOFN.js";
|
|
@@ -619,4 +619,4 @@ async function runAuthRemove(deps, providerId) {
|
|
|
619
619
|
export {
|
|
620
620
|
authCmd
|
|
621
621
|
};
|
|
622
|
-
//# sourceMappingURL=auth-
|
|
622
|
+
//# sourceMappingURL=auth-3K6EN5JP.js.map
|
|
@@ -4,6 +4,7 @@ import type { Config, Logger, ModelsRegistry, Renderer } from '@wrongstack/core/
|
|
|
4
4
|
import type { WstackPaths } from '@wrongstack/core/utils';
|
|
5
5
|
import { createDefaultContainer } from '@wrongstack/runtime';
|
|
6
6
|
import { makePromptDelegate } from '../permission-prompt.js';
|
|
7
|
+
import { type ApprovalMirrorRef } from '../permission-prompt-mirror.js';
|
|
7
8
|
interface WireContainerDeps {
|
|
8
9
|
config: Config;
|
|
9
10
|
wpaths: WstackPaths;
|
|
@@ -30,6 +31,7 @@ export declare function wireContainer(deps: WireContainerDeps): {
|
|
|
30
31
|
pathResolver: InstanceType<typeof DefaultPathResolver>;
|
|
31
32
|
events: EventBus;
|
|
32
33
|
container: ReturnType<typeof createDefaultContainer>;
|
|
34
|
+
approvalMirror: ApprovalMirrorRef;
|
|
33
35
|
};
|
|
34
36
|
export {};
|
|
35
37
|
//# sourceMappingURL=container-wiring.d.ts.map
|
|
@@ -102,6 +102,13 @@ function modelDetails(models) {
|
|
|
102
102
|
tools: model.tool_call,
|
|
103
103
|
vision: model.modalities?.input?.includes("image"),
|
|
104
104
|
reasoning: model.reasoning ?? model.reasoningConfig !== void 0,
|
|
105
|
+
// Documented effort vocabulary, mirroring the gate
|
|
106
|
+
// `getActiveModelReasoningEffortLevels` applies for the settings
|
|
107
|
+
// panel: `effortSupported === false` is a documented "no effort
|
|
108
|
+
// control", and the picker must not offer a strip for it. An absent
|
|
109
|
+
// field stays absent, which the strip reads as "undocumented →
|
|
110
|
+
// canonical set".
|
|
111
|
+
...model.reasoningConfig?.effortSupported && model.reasoningConfig.effortLevels?.length ? { effortLevels: [...model.reasoningConfig.effortLevels] } : {},
|
|
105
112
|
maxContext: model.limit?.context,
|
|
106
113
|
maxOutput: model.limit?.output,
|
|
107
114
|
inputCost: model.cost?.input,
|
|
@@ -120,4 +127,4 @@ export {
|
|
|
120
127
|
hasApiKey,
|
|
121
128
|
buildPickableProviders
|
|
122
129
|
};
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
130
|
+
//# sourceMappingURL=chunk-3VXYY5JC.js.map
|
|
@@ -22,7 +22,7 @@ async function handleHelpVersionShortCircuit(argv) {
|
|
|
22
22
|
}
|
|
23
23
|
if (earlyFlags["help"] === true) {
|
|
24
24
|
if (positional.length > 0) {
|
|
25
|
-
const { subcommandNames } = await import("./subcommands-
|
|
25
|
+
const { subcommandNames } = await import("./subcommands-VFVUTYAV.js");
|
|
26
26
|
if (subcommandNames.includes(positional[0])) {
|
|
27
27
|
const first = positional[0];
|
|
28
28
|
const deep = positional[1];
|
|
@@ -43,4 +43,4 @@ async function handleHelpVersionShortCircuit(argv) {
|
|
|
43
43
|
export {
|
|
44
44
|
handleHelpVersionShortCircuit
|
|
45
45
|
};
|
|
46
|
-
//# sourceMappingURL=chunk-
|
|
46
|
+
//# sourceMappingURL=chunk-4OLAZIIN.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
hasApiKey,
|
|
7
7
|
isKeylessLocalProvider,
|
|
8
8
|
visibleModelIds
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3VXYY5JC.js";
|
|
10
10
|
import {
|
|
11
11
|
openBrowser
|
|
12
12
|
} from "./chunk-DAKVBMD4.js";
|
|
@@ -1813,4 +1813,4 @@ export {
|
|
|
1813
1813
|
addCustomProvider,
|
|
1814
1814
|
addKeyForProvider
|
|
1815
1815
|
};
|
|
1816
|
-
//# sourceMappingURL=chunk-
|
|
1816
|
+
//# sourceMappingURL=chunk-FSZ5QU3M.js.map
|
|
@@ -235,6 +235,52 @@ async function dispatch(controller, commandId, type, payload) {
|
|
|
235
235
|
});
|
|
236
236
|
return { commandId, status: "completed", message };
|
|
237
237
|
}
|
|
238
|
+
case "approve": {
|
|
239
|
+
const resolveApproval = controller.resolveApproval;
|
|
240
|
+
if (resolveApproval === void 0) {
|
|
241
|
+
return {
|
|
242
|
+
commandId,
|
|
243
|
+
status: "rejected",
|
|
244
|
+
message: "this client does not mirror approvals"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const toolUseId = typeof payload["toolUseId"] === "string" ? payload["toolUseId"] : "";
|
|
248
|
+
const decision = payload["decision"];
|
|
249
|
+
if (toolUseId.length === 0 || decision !== "yes" && decision !== "no" && decision !== "always" && decision !== "deny") {
|
|
250
|
+
return { commandId, status: "rejected", message: "malformed approve command" };
|
|
251
|
+
}
|
|
252
|
+
const applied = resolveApproval(toolUseId, decision, sessionId);
|
|
253
|
+
if (!applied) {
|
|
254
|
+
return {
|
|
255
|
+
commandId,
|
|
256
|
+
status: "rejected",
|
|
257
|
+
// Says WHY, because "rejected" alone reads as a failure the operator
|
|
258
|
+
// should retry — and retrying a settled prompt does nothing.
|
|
259
|
+
message: `approval ${toolUseId} is no longer pending (already answered, aborted or timed out)`
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return { commandId, status: "completed", message: `approval ${decision}` };
|
|
263
|
+
}
|
|
264
|
+
case "answer-input": {
|
|
265
|
+
const resolver = controller.resolveUserInput;
|
|
266
|
+
if (!resolver)
|
|
267
|
+
return {
|
|
268
|
+
commandId,
|
|
269
|
+
status: "rejected",
|
|
270
|
+
message: "this client does not mirror user input forms"
|
|
271
|
+
};
|
|
272
|
+
const requestId = typeof payload["requestId"] === "string" ? payload["requestId"] : "";
|
|
273
|
+
const response = payload["response"];
|
|
274
|
+
if (!requestId || !response || !Array.isArray(response.answers))
|
|
275
|
+
return { commandId, status: "rejected", message: "malformed answer-input command" };
|
|
276
|
+
if (!resolver(requestId, response, sessionId))
|
|
277
|
+
return {
|
|
278
|
+
commandId,
|
|
279
|
+
status: "rejected",
|
|
280
|
+
message: `user input ${requestId} is no longer pending`
|
|
281
|
+
};
|
|
282
|
+
return { commandId, status: "completed", message: "user input submitted" };
|
|
283
|
+
}
|
|
238
284
|
case "run-command": {
|
|
239
285
|
if (!controller.allowRunCommand()) {
|
|
240
286
|
return {
|
|
@@ -274,4 +320,4 @@ export {
|
|
|
274
320
|
createProjectKanbanAssignHandler,
|
|
275
321
|
createHqCommandDispatcher
|
|
276
322
|
};
|
|
277
|
-
//# sourceMappingURL=chunk-
|
|
323
|
+
//# sourceMappingURL=chunk-JY52HWL6.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
// src/subcommands/index.ts
|
|
3
3
|
var loaders = {
|
|
4
|
-
acp: async () => (await import("./acp-
|
|
4
|
+
acp: async () => (await import("./acp-A5EQEXQO.js")).acpCmd,
|
|
5
5
|
init: async () => (await import("./init-DRMMFG2G.js")).initCmd,
|
|
6
|
-
auth: async () => (await import("./auth-
|
|
6
|
+
auth: async () => (await import("./auth-3K6EN5JP.js")).authCmd,
|
|
7
7
|
update: async () => (await import("./update-5DPB4YD4.js")).updateCmd,
|
|
8
8
|
sessions: async () => (await import("./sessions-config-UL45XKX7.js")).sessionsCmd,
|
|
9
9
|
config: async () => (await import("./sessions-config-UL45XKX7.js")).configCmd,
|
|
@@ -14,8 +14,8 @@ var loaders = {
|
|
|
14
14
|
audit: async () => (await import("./audit-46YK5DSC.js")).auditCmd,
|
|
15
15
|
tools: async () => (await import("./tools-skills-GH24UCBY.js")).toolsCmd,
|
|
16
16
|
skills: async () => (await import("./tools-skills-GH24UCBY.js")).skillsCmd,
|
|
17
|
-
providers: async () => (await import("./providers-models-
|
|
18
|
-
models: async () => (await import("./providers-models-
|
|
17
|
+
providers: async () => (await import("./providers-models-LFNZ5WI7.js")).providersCmd,
|
|
18
|
+
models: async () => (await import("./providers-models-LFNZ5WI7.js")).modelsCmd,
|
|
19
19
|
mcp: async () => (await import("./mcp-FX7TKPTV.js")).mcpCmd,
|
|
20
20
|
plugin: async () => (await import("./plugin-usage-M6KET4OZ.js")).pluginCmd,
|
|
21
21
|
plugins: async () => (await import("./plugin-usage-M6KET4OZ.js")).pluginCmd,
|
|
@@ -27,11 +27,11 @@ var loaders = {
|
|
|
27
27
|
version: async () => (await import("./version-help-WXI42PZ3.js")).versionCmd,
|
|
28
28
|
help: async () => (await import("./version-help-WXI42PZ3.js")).helpCmd,
|
|
29
29
|
projects: async () => (await import("./projects-LMNAGGEU.js")).projectsCmd,
|
|
30
|
-
modeldiag: async () => (await import("./modeldiag-
|
|
30
|
+
modeldiag: async () => (await import("./modeldiag-CJSFHZ3J.js")).modeldiagCmd,
|
|
31
31
|
quick: async () => (await import("./quick-CNB4Z3HS.js")).quickCmd,
|
|
32
32
|
bench: async () => (await import("./bench-YBU6BVQP.js")).benchCmd,
|
|
33
33
|
chronicle: async () => (await import("./chronicle-UTFETHP6.js")).chronicleCmd,
|
|
34
|
-
hq: async () => (await import("./hq-
|
|
34
|
+
hq: async () => (await import("./hq-G5PCP325.js")).hqCmd,
|
|
35
35
|
mailbox: async () => (await import("./mailbox-serve-X3CR6VFW.js")).mailboxServeCmd,
|
|
36
36
|
permissions: async () => (await import("./permissions-CFDLXETM.js")).permissionsCmd,
|
|
37
37
|
project: async () => (await import("./project-AOQI5XTK.js")).projectCmd,
|
|
@@ -51,4 +51,4 @@ export {
|
|
|
51
51
|
subcommands,
|
|
52
52
|
subcommandNames
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-RLCKKLJA.js.map
|
|
@@ -27,7 +27,8 @@ import {
|
|
|
27
27
|
writeOut
|
|
28
28
|
} from "@wrongstack/core/utils";
|
|
29
29
|
function makePromptDelegate(reader) {
|
|
30
|
-
return async (tool, input, suggestedPattern) => {
|
|
30
|
+
return async (tool, input, suggestedPattern, signal) => {
|
|
31
|
+
if (signal?.aborted) return "no";
|
|
31
32
|
writeOut("\x07");
|
|
32
33
|
writeOut(
|
|
33
34
|
`
|
|
@@ -54,7 +55,8 @@ ${theme.warn("\u26A0 APPROVAL REQUIRED")} ${theme.primary("\u2502")} ${theme.bol
|
|
|
54
55
|
const alwaysHint = noAlwaysReason ? "" : ` ${theme.bold("[a]")}lways allow (${sanitizeTerminalText2(suggestedPattern)})`;
|
|
55
56
|
const answer = await reader.readKey(
|
|
56
57
|
`${theme.bold("[y]")}es ${theme.bold("[n]")}o${alwaysHint} ${theme.bold("[d]")}eny: `,
|
|
57
|
-
options
|
|
58
|
+
options,
|
|
59
|
+
...signal ? [{ signal }] : []
|
|
58
60
|
);
|
|
59
61
|
return answer;
|
|
60
62
|
};
|
|
@@ -480,4 +482,4 @@ export {
|
|
|
480
482
|
detectProjectFacts,
|
|
481
483
|
renderAgentsTemplate
|
|
482
484
|
};
|
|
483
|
-
//# sourceMappingURL=chunk-
|
|
485
|
+
//# sourceMappingURL=chunk-RMQNCHGO.js.map
|
|
@@ -2118,7 +2118,11 @@ async function authorizeHqCommand(input) {
|
|
|
2118
2118
|
// bootstrap exchange, which is the same thing the dashboard does.
|
|
2119
2119
|
actor: { kind: authMethod === "session" ? "user" : "remote-client", id: enqueuedBy },
|
|
2120
2120
|
surface: "hq",
|
|
2121
|
-
capability: command.type === "run-command" ? "hq.control.execute" :
|
|
2121
|
+
capability: command.type === "run-command" ? "hq.control.execute" : (
|
|
2122
|
+
// Approvals get their own capability string so a policy can allow
|
|
2123
|
+
// steering while still refusing remote approval of tool calls.
|
|
2124
|
+
command.type === "approve" || command.type === "answer-input" ? "hq.control.approve" : "hq.control.enqueue"
|
|
2125
|
+
),
|
|
2122
2126
|
subject,
|
|
2123
2127
|
risk: command.type === "run-command" ? "critical" : "high",
|
|
2124
2128
|
scope: { projectId: target.projectId },
|
|
@@ -2481,6 +2485,34 @@ async function handleApiCommand(req, res, url, mutableAuth, sessions, clients, b
|
|
|
2481
2485
|
res.end(JSON.stringify({ error: "unrecognized or malformed command", type: body.type }));
|
|
2482
2486
|
return;
|
|
2483
2487
|
}
|
|
2488
|
+
if ((validated.type === "approve" || validated.type === "answer-input") && isCookieAuth(auth) && auth.capabilities !== void 0 && !auth.capabilities.includes("control.approve")) {
|
|
2489
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2490
|
+
res.end(
|
|
2491
|
+
JSON.stringify({
|
|
2492
|
+
error: "forbidden: browser session lacks control.approve capability"
|
|
2493
|
+
})
|
|
2494
|
+
);
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
if ((validated.type === "approve" || validated.type === "answer-input") && isTokenAuth(auth) && auth.capabilities !== void 0 && !auth.capabilities.includes("control.approve")) {
|
|
2498
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2499
|
+
res.end(
|
|
2500
|
+
JSON.stringify({
|
|
2501
|
+
error: "forbidden: token lacks control.approve capability"
|
|
2502
|
+
})
|
|
2503
|
+
);
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
if ((validated.type === "approve" || validated.type === "answer-input") && !target.capabilities.includes("control.approve")) {
|
|
2507
|
+
res.writeHead(409, { "Content-Type": "application/json" });
|
|
2508
|
+
res.end(
|
|
2509
|
+
JSON.stringify({
|
|
2510
|
+
error: "client does not mirror approvals",
|
|
2511
|
+
clientId: body.clientId
|
|
2512
|
+
})
|
|
2513
|
+
);
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2484
2516
|
if (validated.type === "run-command" && isCookieAuth(auth) && auth.capabilities !== void 0 && !auth.capabilities.includes("control.execute")) {
|
|
2485
2517
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2486
2518
|
res.end(
|
|
@@ -4899,4 +4931,4 @@ export {
|
|
|
4899
4931
|
startHqServer,
|
|
4900
4932
|
HqInsecureExposureError2 as HqInsecureExposureError
|
|
4901
4933
|
};
|
|
4902
|
-
//# sourceMappingURL=chunk-
|
|
4934
|
+
//# sourceMappingURL=chunk-XIZZ2EXG.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
makePromptDelegate,
|
|
6
6
|
renderAgentsTemplate,
|
|
7
7
|
renderDiff
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RMQNCHGO.js";
|
|
9
9
|
import {
|
|
10
10
|
theme as theme2
|
|
11
11
|
} from "./chunk-QD544J2B.js";
|
|
@@ -19,17 +19,17 @@ import {
|
|
|
19
19
|
} from "./chunk-GPPRNAKW.js";
|
|
20
20
|
import {
|
|
21
21
|
subcommands
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-RLCKKLJA.js";
|
|
23
23
|
import {
|
|
24
24
|
renderDeepHelp,
|
|
25
25
|
renderFocusedHelp
|
|
26
26
|
} from "./chunk-JX6KL7OF.js";
|
|
27
27
|
import {
|
|
28
28
|
handleHelpVersionShortCircuit
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-4OLAZIIN.js";
|
|
30
30
|
import {
|
|
31
31
|
DEFAULT_PORT
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-XIZZ2EXG.js";
|
|
33
33
|
import "./chunk-HXJYHAR4.js";
|
|
34
34
|
import {
|
|
35
35
|
resolveHqPasswordInput
|
|
@@ -63,13 +63,13 @@ import {
|
|
|
63
63
|
runPicker,
|
|
64
64
|
saveToGlobalConfig,
|
|
65
65
|
theme
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-FSZ5QU3M.js";
|
|
67
67
|
import "./chunk-2VMJ3E5Z.js";
|
|
68
68
|
import "./chunk-B4JLSXZB.js";
|
|
69
69
|
import {
|
|
70
70
|
isKeylessLocalProvider,
|
|
71
71
|
visibleModelIds
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-3VXYY5JC.js";
|
|
73
73
|
import {
|
|
74
74
|
activeProfileConfigPath
|
|
75
75
|
} from "./chunk-YMXXOOFN.js";
|
|
@@ -125,11 +125,64 @@ function resolveBundledSkillsDir() {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
// src/permission-prompt-mirror.ts
|
|
129
|
+
import { randomUUID } from "node:crypto";
|
|
130
|
+
import { describeWriteTargets } from "@wrongstack/core/security";
|
|
131
|
+
var REPL_MIRROR_TTL_MS = 6e4;
|
|
132
|
+
var REPL_MIRROR_HEARTBEAT_MS = 2e4;
|
|
133
|
+
function makeMirroredPromptDelegate(deps) {
|
|
134
|
+
const now = deps.now ?? Date.now;
|
|
135
|
+
return async (tool, input, suggestedPattern) => {
|
|
136
|
+
const registry = deps.getRegistry();
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
if (registry === void 0) {
|
|
139
|
+
return deps.inner(tool, input, suggestedPattern, controller.signal);
|
|
140
|
+
}
|
|
141
|
+
const toolUseId = `repl:${randomUUID()}`;
|
|
142
|
+
let settled = false;
|
|
143
|
+
let remoteDecision;
|
|
144
|
+
const finish = registry.register({
|
|
145
|
+
toolUseId,
|
|
146
|
+
toolName: tool.name,
|
|
147
|
+
sessionId: deps.getSessionId?.(),
|
|
148
|
+
deadlineAt: now() + REPL_MIRROR_TTL_MS,
|
|
149
|
+
input,
|
|
150
|
+
suggestedPattern,
|
|
151
|
+
...tool.riskTier ? { riskTier: tool.riskTier } : {},
|
|
152
|
+
decisionSource: "default",
|
|
153
|
+
writeTargets: describeWriteTargets(tool, input),
|
|
154
|
+
destructive: tool.riskTier === "destructive",
|
|
155
|
+
resolve: (decision) => {
|
|
156
|
+
if (settled) return;
|
|
157
|
+
settled = true;
|
|
158
|
+
remoteDecision = decision;
|
|
159
|
+
controller.abort();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const heartbeat = setInterval(() => {
|
|
163
|
+
registry.renew(toolUseId, now() + REPL_MIRROR_TTL_MS);
|
|
164
|
+
}, REPL_MIRROR_HEARTBEAT_MS);
|
|
165
|
+
heartbeat.unref?.();
|
|
166
|
+
try {
|
|
167
|
+
const typed = await deps.inner(tool, input, suggestedPattern, controller.signal);
|
|
168
|
+
if (remoteDecision !== void 0) return remoteDecision;
|
|
169
|
+
settled = true;
|
|
170
|
+
const decision = typed === "yes" || typed === "always" || typed === "deny" ? typed : "no";
|
|
171
|
+
finish(decision);
|
|
172
|
+
return decision;
|
|
173
|
+
} finally {
|
|
174
|
+
clearInterval(heartbeat);
|
|
175
|
+
finish("no");
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
128
180
|
// src/boot/container-wiring.ts
|
|
129
181
|
function wireContainer(deps) {
|
|
130
182
|
const pathResolver = new DefaultPathResolver(deps.cwd);
|
|
131
183
|
const events = new CoreEventBus();
|
|
132
184
|
events.setLogger(deps.logger);
|
|
185
|
+
const approvalMirror = { current: void 0 };
|
|
133
186
|
const container = createDefaultContainer({
|
|
134
187
|
config: deps.config,
|
|
135
188
|
wpaths: deps.wpaths,
|
|
@@ -138,7 +191,15 @@ function wireContainer(deps) {
|
|
|
138
191
|
events,
|
|
139
192
|
permission: {
|
|
140
193
|
yolo: deps.config.yolo,
|
|
141
|
-
|
|
194
|
+
// Wrapped so the REPL's terminal prompt also appears on the HQ
|
|
195
|
+
// dashboard and can be answered from there. The wrapper is a no-op
|
|
196
|
+
// until `approvalMirror.current` is populated (HQ telemetry boot), and
|
|
197
|
+
// the terminal prompt itself behaves exactly as it always has.
|
|
198
|
+
promptDelegate: makeMirroredPromptDelegate({
|
|
199
|
+
inner: makePromptDelegate(deps.reader),
|
|
200
|
+
getRegistry: () => approvalMirror.current,
|
|
201
|
+
getSessionId: () => approvalMirror.sessionId?.()
|
|
202
|
+
})
|
|
142
203
|
},
|
|
143
204
|
compactor: {
|
|
144
205
|
preserveK: deps.config.context.preserveK,
|
|
@@ -150,7 +211,7 @@ function wireContainer(deps) {
|
|
|
150
211
|
container.bind(TOKENS.PathResolver, () => pathResolver);
|
|
151
212
|
container.bind(TOKENS.Renderer, () => deps.renderer);
|
|
152
213
|
container.bind(TOKENS.InputReader, () => deps.reader);
|
|
153
|
-
return { pathResolver, events, container };
|
|
214
|
+
return { pathResolver, events, container, approvalMirror };
|
|
154
215
|
}
|
|
155
216
|
|
|
156
217
|
// src/boot/launch-menu.ts
|
|
@@ -805,7 +866,7 @@ async function isPortInUse(host, port) {
|
|
|
805
866
|
}
|
|
806
867
|
async function handleHqShortCircuit(flags) {
|
|
807
868
|
if (flags["hq"] !== true) return null;
|
|
808
|
-
const { startHqServer } = await import("./hq-server-
|
|
869
|
+
const { startHqServer } = await import("./hq-server-G3VVVMIG.js");
|
|
809
870
|
const tunnelRequested = flags["tunnel"] === true;
|
|
810
871
|
const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
|
|
811
872
|
let publicOrigin;
|
|
@@ -1827,8 +1888,10 @@ var ReadlineInputReader = class {
|
|
|
1827
1888
|
}
|
|
1828
1889
|
});
|
|
1829
1890
|
}
|
|
1830
|
-
async readKey(prompt, options) {
|
|
1891
|
+
async readKey(prompt, options, opts = {}) {
|
|
1831
1892
|
setOutputLineGuard(null);
|
|
1893
|
+
const signal = opts.signal;
|
|
1894
|
+
if (signal?.aborted) return "";
|
|
1832
1895
|
writeOut(prompt);
|
|
1833
1896
|
return new Promise((resolve2) => {
|
|
1834
1897
|
const stdin = process.stdin;
|
|
@@ -1858,14 +1921,21 @@ var ReadlineInputReader = class {
|
|
|
1858
1921
|
cleanup();
|
|
1859
1922
|
resolve2("");
|
|
1860
1923
|
};
|
|
1924
|
+
const onAbort = () => {
|
|
1925
|
+
cleanup();
|
|
1926
|
+
writeOut("\n");
|
|
1927
|
+
resolve2("");
|
|
1928
|
+
};
|
|
1861
1929
|
const cleanup = () => {
|
|
1862
1930
|
stdin.off("data", onData);
|
|
1863
1931
|
stdin.off("close", onClose);
|
|
1932
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1864
1933
|
setRawMode2(stdin, wasRaw);
|
|
1865
1934
|
if (wasPaused) stdin.pause();
|
|
1866
1935
|
};
|
|
1867
1936
|
stdin.on("data", onData);
|
|
1868
1937
|
stdin.on("close", onClose);
|
|
1938
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1869
1939
|
});
|
|
1870
1940
|
}
|
|
1871
1941
|
/**
|
|
@@ -3210,7 +3280,7 @@ async function initializeCli(argv) {
|
|
|
3210
3280
|
}).catch(() => {
|
|
3211
3281
|
});
|
|
3212
3282
|
});
|
|
3213
|
-
const { events, container } = wireContainer({
|
|
3283
|
+
const { events, container, approvalMirror } = wireContainer({
|
|
3214
3284
|
config,
|
|
3215
3285
|
wpaths,
|
|
3216
3286
|
cwd,
|
|
@@ -3225,6 +3295,7 @@ async function initializeCli(argv) {
|
|
|
3225
3295
|
updateInfo: refreshedUpdateInfo,
|
|
3226
3296
|
events,
|
|
3227
3297
|
container,
|
|
3298
|
+
approvalMirror,
|
|
3228
3299
|
configStore,
|
|
3229
3300
|
webuiSessionChild
|
|
3230
3301
|
};
|
|
@@ -3232,4 +3303,4 @@ async function initializeCli(argv) {
|
|
|
3232
3303
|
export {
|
|
3233
3304
|
initializeCli
|
|
3234
3305
|
};
|
|
3235
|
-
//# sourceMappingURL=cli-context-
|
|
3306
|
+
//# sourceMappingURL=cli-context-TIAP6YQK.js.map
|
package/dist/cli-context.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ import type { BootContext } from './boot.js';
|
|
|
22
22
|
export interface CliContext extends BootContext {
|
|
23
23
|
events: EventBus;
|
|
24
24
|
container: ReturnType<typeof wireContainer>['container'];
|
|
25
|
+
/** Late-bound handle the REPL prompt uses to mirror approvals to HQ. */
|
|
26
|
+
approvalMirror: ReturnType<typeof wireContainer>['approvalMirror'];
|
|
25
27
|
configStore: ConfigStore;
|
|
26
28
|
webuiSessionChild?: WebuiSessionChildOptions | undefined;
|
|
27
29
|
}
|