@wrongstack/cli 1.1.4 → 1.1.6
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-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
- package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
- package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
- package/dist/acp-leader/acp-leader-host.d.ts +9 -0
- package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
- package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
- package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
- package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
- package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
- package/dist/boot/launch-menu-arrow.d.ts +20 -0
- package/dist/boot/launch-menu-types.d.ts +36 -0
- package/dist/boot/launch-menu.d.ts +1 -57
- package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
- package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
- package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
- package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
- package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
- package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
- package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
- package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
- package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
- package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
- package/dist/chunk-LYWOV4OX.js +252 -0
- package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
- package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
- package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
- package/dist/chunk-PGC44T7N.js +255 -0
- package/dist/chunk-QLYPIJ7X.js +258 -0
- package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
- package/dist/chunk-RN5CFAVQ.js +292 -0
- package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
- package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
- package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
- package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
- package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
- package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
- package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
- package/dist/cli-main-boot.d.ts +1 -1
- package/dist/config-doctor-known-keys.d.ts +33 -0
- package/dist/config-doctor.d.ts +2 -47
- package/dist/config-history-support.d.ts +36 -0
- package/dist/config-history.d.ts +1 -1
- package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
- package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
- package/dist/execution-tui.d.ts +30 -0
- package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
- package/dist/fleet/host-acp.d.ts +3 -0
- package/dist/fleet/wrongforge-client.d.ts +116 -0
- package/dist/fleet/wrongforge-gateway.d.ts +20 -0
- package/dist/fleet/wrongforge-info.d.ts +32 -0
- package/dist/fleet/wrongforge-quota.d.ts +10 -0
- package/dist/fleet/wrongforge-runner.d.ts +37 -0
- package/dist/fleet/wrongforge-runtime.d.ts +10 -0
- package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
- package/dist/hq-server/login-attempt-store.d.ts +2 -0
- package/dist/hq-server/sweeps.d.ts +28 -0
- package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/mcp-22CFBX4H.js +236 -0
- package/dist/mcp-serve.d.ts +0 -63
- package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
- package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
- package/dist/plugin-llm-command.d.ts +9 -0
- package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
- package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
- package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
- package/dist/services/sdd/state.d.ts +2 -0
- package/dist/session-event-wiring-types.d.ts +62 -0
- package/dist/session-event-wiring.d.ts +2 -52
- package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
- package/dist/short-circuit-flags-276OLPIN.js +11 -0
- package/dist/slash-commands/command-context-fleet.d.ts +187 -0
- package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
- package/dist/slash-commands/command-context.d.ts +3 -282
- package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
- package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
- package/dist/subcommands/handlers/hq-token.d.ts +3 -0
- package/dist/subcommands/handlers/hq.d.ts +0 -21
- package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
- package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
- package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
- package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
- package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
- package/dist/tool-gateway-http.d.ts +30 -0
- package/dist/tool-gateway.d.ts +185 -0
- package/dist/tools-cli-44MLIUQO.js +509 -0
- package/dist/tools-serve-TRCET2M7.js +102 -0
- package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
- package/dist/tuneup-runtime-checks.d.ts +7 -0
- package/dist/tuneup-types.d.ts +118 -0
- package/dist/tuneup.d.ts +3 -122
- package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
- package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
- package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
- package/dist/wiring/provider-runtime-setup.d.ts +3 -1
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
- package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
- package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
- package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
- package/package.json +1 -1
- package/dist/mcp-W7A5HUXV.js +0 -560
- package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveWrongForgeEndpoint,
|
|
3
|
+
wrongForgeCall
|
|
4
|
+
} from "./chunk-PGC44T7N.js";
|
|
1
5
|
|
|
2
6
|
// src/fleet/acp-wrongstack-bridge.ts
|
|
3
7
|
import { statSync } from "node:fs";
|
|
@@ -17,6 +21,43 @@ function wstackLaunch(args, entry = process.argv[1]) {
|
|
|
17
21
|
}
|
|
18
22
|
return { command: "wstack", args };
|
|
19
23
|
}
|
|
24
|
+
function wstackToolsCommand(entry = process.argv[1], platform = process.platform) {
|
|
25
|
+
const launch = wstackLaunch(["tools"], entry);
|
|
26
|
+
return [launch.command, ...launch.args].map((word) => shellWord(word, platform)).join(" ");
|
|
27
|
+
}
|
|
28
|
+
function shellWord(word, platform) {
|
|
29
|
+
const w = platform === "win32" ? word.replace(/\\/g, "/") : word;
|
|
30
|
+
return /^[A-Za-z0-9_./:@=+-]+$/.test(w) ? w : `"${w.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
31
|
+
}
|
|
32
|
+
function cliIndexNote(cmd) {
|
|
33
|
+
return [
|
|
34
|
+
`- \`${cmd} run <tool> -- --<field> <value>\` (run through your shell): a prebuilt, ranked`,
|
|
35
|
+
" symbol index of this repository, caught up with the disk on every call. Prefer it over",
|
|
36
|
+
' grep/glob and whole-file reads for code navigation: codebase-context --query "<task>"',
|
|
37
|
+
" (files relevant to a task), codebase-search --query <name> (where something is defined),",
|
|
38
|
+
" codebase-incoming-calls / codebase-outgoing-calls --symbol <name> (callers, callees),",
|
|
39
|
+
" codebase-read-symbol --file <path> --symbol <name> (one symbol's source),",
|
|
40
|
+
" codebase-skeleton --path <file-or-dir> (an outline), codebase-impact-analysis --symbol",
|
|
41
|
+
` <name> (what a change affects). \`${cmd} describe <tool>\` lists a tool's fields;`,
|
|
42
|
+
` \`${cmd} batch\` runs several JSONL calls ({"tool":\u2026,"input":\u2026}) in one process.`,
|
|
43
|
+
" Use grep for literal text such as strings, comments and config values."
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
function serviceNotes(servers, delegated, indexCli) {
|
|
47
|
+
const viaCli = indexCli !== void 0 && servers.includes("codebase-index");
|
|
48
|
+
const mcp = viaCli ? servers.filter((s) => s !== "codebase-index") : servers;
|
|
49
|
+
return [
|
|
50
|
+
...mcp.length > 0 ? [
|
|
51
|
+
"WrongStack's project services are available as MCP servers:",
|
|
52
|
+
...mcp.map((s) => SERVER_NOTES[s](delegated))
|
|
53
|
+
] : [],
|
|
54
|
+
...viaCli ? [
|
|
55
|
+
...mcp.length > 0 ? [""] : [],
|
|
56
|
+
"WrongStack's codebase index is available through its CLI:",
|
|
57
|
+
...cliIndexNote(indexCli)
|
|
58
|
+
] : []
|
|
59
|
+
];
|
|
60
|
+
}
|
|
20
61
|
var SERVER_NAMES = {
|
|
21
62
|
sage: "wrongstack-sage",
|
|
22
63
|
mailbox: "wrongstack-mailbox",
|
|
@@ -31,9 +72,15 @@ var SERVER_NOTES = {
|
|
|
31
72
|
"codebase-index": () => "- wrongstack-index: a prebuilt, ranked symbol index of this repository, kept fresh by WrongStack. Prefer it over grep/glob and whole-file reads for code navigation: codebase_search (where something is defined), codebase_incoming_calls / codebase_outgoing_calls (callers, callees), codebase_read_symbol (one symbol's source), codebase_skeleton (a file or folder outline), codebase_impact_analysis (what a change affects), codebase_context (files relevant to a task), codebase_repo_map (overview). Use grep for literal text such as strings, comments and config values.",
|
|
32
73
|
"requirement-intake": () => "- wrongstack-intake: the project's requirements intake. List open requests; file a new one when you are handed an unrecorded software request."
|
|
33
74
|
};
|
|
34
|
-
function serviceWorkflow(servers) {
|
|
75
|
+
function serviceWorkflow(servers, indexCli) {
|
|
35
76
|
return [
|
|
36
|
-
...servers.includes("codebase-index") ? [
|
|
77
|
+
...servers.includes("codebase-index") ? indexCli !== void 0 ? [
|
|
78
|
+
"Use the WrongStack index first for code navigation; use your file tools for edits,",
|
|
79
|
+
"exact literal searches, and verification. Start a code task with",
|
|
80
|
+
`${indexCli} run codebase-context -- --query "<the task in a few words>"; for a named`,
|
|
81
|
+
`symbol use ${indexCli} run codebase-search -- --query <name>. If the index lacks a`,
|
|
82
|
+
"result, fall back to your tools."
|
|
83
|
+
] : [
|
|
37
84
|
"Use wrongstack-index first for code navigation; use your file tools for edits, exact",
|
|
38
85
|
"literal searches, and verification. Start a code task with",
|
|
39
86
|
"mcp__wrongstack-index__codebase_context (task description); for a named symbol use",
|
|
@@ -57,10 +104,12 @@ function serviceWorkflow(servers) {
|
|
|
57
104
|
"If a project service is unavailable, continue with available tools and report the gap."
|
|
58
105
|
];
|
|
59
106
|
}
|
|
60
|
-
function bridgeTaskKickoff(servers) {
|
|
107
|
+
function bridgeTaskKickoff(servers, indexCli) {
|
|
61
108
|
return [
|
|
62
109
|
"Project-service kickoff for this task:",
|
|
63
|
-
...servers.includes("codebase-index") ? [
|
|
110
|
+
...servers.includes("codebase-index") ? [
|
|
111
|
+
indexCli !== void 0 ? `First run ${indexCli} run codebase-context -- --query "<the task>" to locate relevant code.` : "First use mcp__wrongstack-index__codebase_context to locate relevant code."
|
|
112
|
+
] : [],
|
|
64
113
|
...servers.includes("sage") ? ["Use mcp__wrongstack-sage__memory_search for prior project guidance."] : [],
|
|
65
114
|
...servers.includes("mailbox") ? ['Check mcp__wrongstack-mailbox__mailbox_read with action "inbox".'] : [],
|
|
66
115
|
...servers.includes("kanban") ? ["If the task names a board card, inspect it with mcp__wrongstack-kanban__kanban_read."] : [],
|
|
@@ -93,7 +142,7 @@ function serverArgs(server, projectRoot, ctx) {
|
|
|
93
142
|
return [...base, "--writable"];
|
|
94
143
|
}
|
|
95
144
|
}
|
|
96
|
-
function bridgeNote(servers, delegated = true) {
|
|
145
|
+
function bridgeNote(servers, delegated = true, indexCli) {
|
|
97
146
|
return [
|
|
98
147
|
"You are working as a delegated worker inside a WrongStack project.",
|
|
99
148
|
"WrongStack is the orchestrator the user runs: it sent you this task and receives your",
|
|
@@ -104,10 +153,9 @@ function bridgeNote(servers, delegated = true) {
|
|
|
104
153
|
"from the user who delegated the task, and answer what it asks."
|
|
105
154
|
] : [],
|
|
106
155
|
"",
|
|
107
|
-
|
|
108
|
-
...servers.map((s) => SERVER_NOTES[s](delegated)),
|
|
156
|
+
...serviceNotes(servers, delegated, indexCli),
|
|
109
157
|
"",
|
|
110
|
-
...serviceWorkflow(servers),
|
|
158
|
+
...serviceWorkflow(servers, indexCli),
|
|
111
159
|
"",
|
|
112
160
|
"WrongStack applies the user's rules to your edits and commands. A refused permission",
|
|
113
161
|
"comes from them (a deny rule the user wrote, a history-rewriting git command, a",
|
|
@@ -115,15 +163,14 @@ function bridgeNote(servers, delegated = true) {
|
|
|
115
163
|
"was refused in your final reply."
|
|
116
164
|
].join("\n");
|
|
117
165
|
}
|
|
118
|
-
function leaderBridgeNote(servers) {
|
|
166
|
+
function leaderBridgeNote(servers, indexCli) {
|
|
119
167
|
return [
|
|
120
168
|
"You are the leader of a WrongStack session. The user talks to you through WrongStack",
|
|
121
169
|
"(its terminal UI or web UI), which shows your replies, your tool calls and their results.",
|
|
122
170
|
"",
|
|
123
|
-
|
|
124
|
-
...servers.map((s) => SERVER_NOTES[s](false)),
|
|
171
|
+
...serviceNotes(servers, false, indexCli),
|
|
125
172
|
"",
|
|
126
|
-
...serviceWorkflow(servers),
|
|
173
|
+
...serviceWorkflow(servers, indexCli),
|
|
127
174
|
"",
|
|
128
175
|
"WrongStack applies the user's rules to your edits and commands and asks the user when one",
|
|
129
176
|
"needs approval. A refused permission is the user's decision or rule: do not retry it or",
|
|
@@ -167,21 +214,24 @@ function installedBridgeAgents(acp, isOnPath = (c) => onPath(c)) {
|
|
|
167
214
|
}
|
|
168
215
|
function wrongstackBridgeSetup(agentId, projectRoot, config, role = "worker") {
|
|
169
216
|
const agents = config?.agents ?? DEFAULT_BRIDGE_AGENTS;
|
|
170
|
-
|
|
217
|
+
const listed = (a) => a === agentId || REGISTRY_ID_ALIASES[a] === agentId || REGISTRY_ID_ALIASES[agentId] === a;
|
|
218
|
+
if (!agents.some(listed)) return void 0;
|
|
171
219
|
const servers = [...new Set(config?.servers ?? DEFAULT_BRIDGE_SERVERS)].filter(
|
|
172
220
|
(s) => Object.hasOwn(SERVER_NAMES, s)
|
|
173
221
|
);
|
|
174
222
|
if (servers.length === 0) return void 0;
|
|
223
|
+
const indexCli = config?.indexSurface === "cli" && servers.includes("codebase-index") ? wstackToolsCommand() : void 0;
|
|
224
|
+
const attached = indexCli ? servers.filter((s) => s !== "codebase-index") : servers;
|
|
175
225
|
return (_task, ctx) => ({
|
|
176
|
-
promptPrefix: bridgeTaskKickoff(servers),
|
|
177
|
-
mcpServers:
|
|
226
|
+
promptPrefix: bridgeTaskKickoff(servers, indexCli),
|
|
227
|
+
mcpServers: attached.map((server) => {
|
|
178
228
|
const launch = wstackLaunch(serverArgs(server, projectRoot, ctx));
|
|
179
229
|
return { name: SERVER_NAMES[server], ...launch, env: [] };
|
|
180
230
|
}),
|
|
181
231
|
meta: {
|
|
182
232
|
systemPrompt: {
|
|
183
233
|
// `@session` only reaches someone when a WrongStack session delegated the task.
|
|
184
|
-
append: role === "leader" ? leaderBridgeNote(servers) : bridgeNote(servers, ctx.sessionId !== void 0)
|
|
234
|
+
append: role === "leader" ? leaderBridgeNote(servers, indexCli) : bridgeNote(servers, ctx.sessionId !== void 0, indexCli)
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
237
|
});
|
|
@@ -517,12 +567,186 @@ ${command}
|
|
|
517
567
|
}
|
|
518
568
|
}
|
|
519
569
|
|
|
570
|
+
// src/fleet/wrongforge-info.ts
|
|
571
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
572
|
+
import path from "node:path";
|
|
573
|
+
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
574
|
+
function wrongForgeInfoCacheFile() {
|
|
575
|
+
return path.join(wstackGlobalRoot(), "cache", "wrongforge-info.json");
|
|
576
|
+
}
|
|
577
|
+
function parseWrongForgeInfo(body) {
|
|
578
|
+
const o = body && typeof body === "object" ? body : {};
|
|
579
|
+
const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.length > 0) : [];
|
|
580
|
+
const def = o["default_model"] ?? o["defaultModel"];
|
|
581
|
+
const details = o["model_details"] ?? o["modelDetails"];
|
|
582
|
+
const effort = o["effort"];
|
|
583
|
+
const levels = effort?.levels ?? o["effortLevels"];
|
|
584
|
+
return {
|
|
585
|
+
models: strings(o["models"]),
|
|
586
|
+
...typeof def === "string" && def ? { defaultModel: def } : {},
|
|
587
|
+
features: strings(o["features"]),
|
|
588
|
+
...Array.isArray(levels) ? { effortLevels: strings(levels) } : {},
|
|
589
|
+
...Array.isArray(details) ? {
|
|
590
|
+
modelDetails: details.flatMap((value) => {
|
|
591
|
+
if (!value || typeof value !== "object") return [];
|
|
592
|
+
const d = value;
|
|
593
|
+
if (typeof d["id"] !== "string" || !d["id"]) return [];
|
|
594
|
+
return [
|
|
595
|
+
{
|
|
596
|
+
id: d["id"],
|
|
597
|
+
description: typeof d["description"] === "string" ? d["description"] : "",
|
|
598
|
+
effortLevels: strings(d["effort_levels"] ?? d["effortLevels"])
|
|
599
|
+
}
|
|
600
|
+
];
|
|
601
|
+
})
|
|
602
|
+
} : {}
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function readCachedWrongForgeInfo(file = wrongForgeInfoCacheFile()) {
|
|
606
|
+
try {
|
|
607
|
+
const info = parseWrongForgeInfo(JSON.parse(readFileSync(file, "utf8").replace(/^\uFEFF/, "")));
|
|
608
|
+
return info.models.length > 0 ? info : void 0;
|
|
609
|
+
} catch {
|
|
610
|
+
return void 0;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function writeWrongForgeInfoCache(info, file = wrongForgeInfoCacheFile()) {
|
|
614
|
+
if (info.models.length === 0) return;
|
|
615
|
+
try {
|
|
616
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
617
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
618
|
+
writeFileSync(tmp, `${JSON.stringify(info)}
|
|
619
|
+
`);
|
|
620
|
+
renameSync(tmp, file);
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function fetchWrongForgeInfo(ep, opts = {}) {
|
|
625
|
+
const res = await (opts.fetchImpl ?? fetch)(`${ep.url}/v1/info`, {
|
|
626
|
+
headers: { authorization: `Bearer ${ep.apiKey}` },
|
|
627
|
+
...opts.signal ? { signal: opts.signal } : {}
|
|
628
|
+
});
|
|
629
|
+
if (!res.ok) throw new Error(`WrongForge /v1/info answered ${res.status}`);
|
|
630
|
+
return parseWrongForgeInfo(await res.json());
|
|
631
|
+
}
|
|
632
|
+
function refreshWrongForgeInfoCache(ep) {
|
|
633
|
+
if (!ep) return;
|
|
634
|
+
void fetchWrongForgeInfo(ep, { signal: AbortSignal.timeout(3e3) }).then((info) => writeWrongForgeInfoCache(info)).catch(() => void 0);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// src/fleet/wrongforge-quota.ts
|
|
638
|
+
import {
|
|
639
|
+
DEFAULT_QUOTA_METER,
|
|
640
|
+
recordProviderQuota
|
|
641
|
+
} from "@wrongstack/core/quota";
|
|
642
|
+
var reads = /* @__PURE__ */ new Map();
|
|
643
|
+
var REFRESH_MS = 2e4;
|
|
644
|
+
function object(value) {
|
|
645
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
646
|
+
}
|
|
647
|
+
function percent(value) {
|
|
648
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100;
|
|
649
|
+
}
|
|
650
|
+
async function reportWrongForgeQuota(opts = {}) {
|
|
651
|
+
const endpoint = opts.endpoint ?? resolveWrongForgeEndpoint();
|
|
652
|
+
if (!endpoint) return false;
|
|
653
|
+
const providerId = opts.providerId ?? "wrongforge";
|
|
654
|
+
const key = `${providerId}\0${endpoint.url}\0${endpoint.apiKey}`;
|
|
655
|
+
const prior = reads.get(key);
|
|
656
|
+
if (prior?.pending) {
|
|
657
|
+
if (opts.force && !prior.forced) {
|
|
658
|
+
await prior.pending;
|
|
659
|
+
return reportWrongForgeQuota(opts);
|
|
660
|
+
}
|
|
661
|
+
return prior.pending;
|
|
662
|
+
}
|
|
663
|
+
if (!opts.force && prior && Date.now() - prior.at < REFRESH_MS) return prior.ok;
|
|
664
|
+
const state = { at: Date.now(), ok: false, forced: opts.force === true };
|
|
665
|
+
reads.set(key, state);
|
|
666
|
+
if (reads.size > 128)
|
|
667
|
+
for (const [id, entry] of reads) {
|
|
668
|
+
if (!entry.pending && id !== key) {
|
|
669
|
+
reads.delete(id);
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
state.pending = (async () => {
|
|
674
|
+
try {
|
|
675
|
+
const signal = AbortSignal.timeout(opts.timeoutMs ?? 1e4);
|
|
676
|
+
const body = object(
|
|
677
|
+
await wrongForgeCall(endpoint, opts.fetchImpl)(
|
|
678
|
+
"GET",
|
|
679
|
+
`/v1/usage${opts.force ? "?force=1" : ""}`,
|
|
680
|
+
void 0,
|
|
681
|
+
signal
|
|
682
|
+
)
|
|
683
|
+
);
|
|
684
|
+
if (signal.aborted) return false;
|
|
685
|
+
const windows = [];
|
|
686
|
+
for (const spec of [
|
|
687
|
+
{ key: "five_hour", id: "primary", label: "5h", windowMinutes: 300 },
|
|
688
|
+
{ key: "weekly", id: "secondary", label: "weekly", windowMinutes: 10080 }
|
|
689
|
+
]) {
|
|
690
|
+
const raw = object(body[spec.key]);
|
|
691
|
+
if (!percent(raw.percent)) continue;
|
|
692
|
+
const reset = typeof raw.resetsAt === "string" ? Date.parse(raw.resetsAt) : Number.NaN;
|
|
693
|
+
windows.push({
|
|
694
|
+
id: spec.id,
|
|
695
|
+
label: spec.label,
|
|
696
|
+
windowMinutes: spec.windowMinutes,
|
|
697
|
+
usedPercent: raw.percent,
|
|
698
|
+
...Number.isFinite(reset) ? { resetsAt: Math.floor(reset / 1e3) } : {}
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
if (windows.length === 0) return false;
|
|
702
|
+
const extra = object(body.extra_usage);
|
|
703
|
+
const snapshot = {
|
|
704
|
+
providerId,
|
|
705
|
+
meterId: DEFAULT_QUOTA_METER,
|
|
706
|
+
meterLabel: "WrongForge \xB7 Claude subscription",
|
|
707
|
+
windows,
|
|
708
|
+
capturedAt: Date.now()
|
|
709
|
+
};
|
|
710
|
+
if (extra.enabled === false) snapshot.credits = { hasCredits: false, unlimited: false };
|
|
711
|
+
if (extra.enabled === true) {
|
|
712
|
+
snapshot.note = percent(extra.utilization) ? `Extra usage enabled \xB7 ${extra.utilization}% used` : "Extra usage enabled";
|
|
713
|
+
if (typeof extra.usedCredits === "number" && Number.isFinite(extra.usedCredits) && extra.usedCredits >= 0 && typeof extra.monthlyLimit === "number" && Number.isFinite(extra.monthlyLimit) && extra.monthlyLimit >= 0) {
|
|
714
|
+
const remaining = Math.max(0, extra.monthlyLimit - extra.usedCredits);
|
|
715
|
+
snapshot.credits = {
|
|
716
|
+
hasCredits: remaining > 0,
|
|
717
|
+
unlimited: false,
|
|
718
|
+
balance: `${remaining} credits remaining (${extra.usedCredits}/${extra.monthlyLimit})`
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
} else if (extra.enabled === false) snapshot.note = "Extra usage disabled";
|
|
722
|
+
const exhausted = windows.filter(
|
|
723
|
+
(window) => window.usedPercent >= 100 && (window.resetsAt === void 0 || window.resetsAt * 1e3 > snapshot.capturedAt)
|
|
724
|
+
);
|
|
725
|
+
exhausted.sort((a, b) => (b.resetsAt ?? Infinity) - (a.resetsAt ?? Infinity));
|
|
726
|
+
if (exhausted[0]) snapshot.reachedWindowId = exhausted[0].id;
|
|
727
|
+
recordProviderQuota(providerId, [snapshot]);
|
|
728
|
+
state.ok = true;
|
|
729
|
+
return true;
|
|
730
|
+
} catch {
|
|
731
|
+
return false;
|
|
732
|
+
} finally {
|
|
733
|
+
delete state.pending;
|
|
734
|
+
}
|
|
735
|
+
})();
|
|
736
|
+
return state.pending;
|
|
737
|
+
}
|
|
738
|
+
|
|
520
739
|
export {
|
|
521
740
|
DEFAULT_BRIDGE_SERVERS,
|
|
522
741
|
installedBridgeAgents,
|
|
523
742
|
wrongstackBridgeSetup,
|
|
524
743
|
createProjectKanbanTransitionHandler,
|
|
525
744
|
createProjectKanbanAssignHandler,
|
|
526
|
-
createHqCommandDispatcher
|
|
745
|
+
createHqCommandDispatcher,
|
|
746
|
+
parseWrongForgeInfo,
|
|
747
|
+
readCachedWrongForgeInfo,
|
|
748
|
+
writeWrongForgeInfoCache,
|
|
749
|
+
refreshWrongForgeInfoCache,
|
|
750
|
+
reportWrongForgeQuota
|
|
527
751
|
};
|
|
528
|
-
//# sourceMappingURL=chunk-
|
|
752
|
+
//# sourceMappingURL=chunk-QRWXHGLB.js.map
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withheldReason
|
|
3
|
+
} from "./chunk-LYWOV4OX.js";
|
|
4
|
+
|
|
5
|
+
// src/tool-gateway-http.ts
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
import { realpath } from "node:fs/promises";
|
|
8
|
+
import { createServer } from "node:http";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
11
|
+
import {
|
|
12
|
+
bearerTokenMatches,
|
|
13
|
+
hostHeaderIsAcceptable,
|
|
14
|
+
isJsonContentType,
|
|
15
|
+
isLoopbackHost,
|
|
16
|
+
originIsAcceptable
|
|
17
|
+
} from "@wrongstack/mcp";
|
|
18
|
+
var BODY_CAP_BYTES = 4 * 1024 * 1024;
|
|
19
|
+
var DEFAULT_MAX_CONCURRENT_CALLS = 16;
|
|
20
|
+
var CALLBACK_TIMEOUT_MS = 3e4;
|
|
21
|
+
function serveToolGatewayHttp(opts) {
|
|
22
|
+
const host = opts.host ?? "127.0.0.1";
|
|
23
|
+
if (!opts.token || opts.token.length < 16) {
|
|
24
|
+
return Promise.reject(
|
|
25
|
+
new Error("tools serve: a bearer token of at least 16 characters is required")
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
const maxConcurrent = opts.maxConcurrentCalls ?? DEFAULT_MAX_CONCURRENT_CALLS;
|
|
29
|
+
const pending = /* @__PURE__ */ new Set();
|
|
30
|
+
let inFlight = 0;
|
|
31
|
+
const server = createServer((req, res) => {
|
|
32
|
+
void handle(req, res).catch((err) => {
|
|
33
|
+
opts.log?.(`tools serve: request failed: ${toErrorMessage(err)}`);
|
|
34
|
+
if (!res.headersSent) send(res, 500, { ok: false, error: "internal_error" });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
async function handle(req, res) {
|
|
38
|
+
if (!originIsAcceptable(req, host))
|
|
39
|
+
return send(res, 403, { ok: false, error: "cross_origin_forbidden" });
|
|
40
|
+
if (!hostHeaderIsAcceptable(req, host))
|
|
41
|
+
return send(res, 403, { ok: false, error: "untrusted_host_header" });
|
|
42
|
+
const url = new URL(req.url ?? "/", "http://local");
|
|
43
|
+
if (req.method === "GET" && url.pathname === "/health") return send(res, 200, { ok: true });
|
|
44
|
+
if (!bearerTokenMatches(req.headers.authorization, opts.token)) {
|
|
45
|
+
return send(res, 401, { ok: false, error: "unauthorized" });
|
|
46
|
+
}
|
|
47
|
+
let parts;
|
|
48
|
+
try {
|
|
49
|
+
parts = url.pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
50
|
+
} catch {
|
|
51
|
+
return send(res, 400, { ok: false, error: "invalid_path_encoding" });
|
|
52
|
+
}
|
|
53
|
+
if (parts[0] !== "v1" || parts[1] !== "tools")
|
|
54
|
+
return send(res, 404, { ok: false, error: "not_found" });
|
|
55
|
+
const name = parts[2];
|
|
56
|
+
if (req.method === "GET" && parts.length === 2) {
|
|
57
|
+
return send(res, 200, {
|
|
58
|
+
mode: opts.gateway.mode,
|
|
59
|
+
capabilities: opts.gateway.capabilities,
|
|
60
|
+
tools: opts.gateway.tools.map(describe)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (req.method === "GET" && name && parts.length === 3) {
|
|
64
|
+
const tool = opts.gateway.get(name);
|
|
65
|
+
if (!tool) return send(res, 404, notExposedBody(name));
|
|
66
|
+
return send(res, 200, describe(tool));
|
|
67
|
+
}
|
|
68
|
+
if (req.method === "POST" && name && parts.length === 4 && parts[3] === "call") {
|
|
69
|
+
return call(req, res, name);
|
|
70
|
+
}
|
|
71
|
+
return send(res, req.method === "GET" || req.method === "POST" ? 404 : 405, {
|
|
72
|
+
ok: false,
|
|
73
|
+
error: req.method === "GET" || req.method === "POST" ? "not_found" : "method_not_allowed"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function call(req, res, name) {
|
|
77
|
+
if (!isJsonContentType(req.headers["content-type"])) {
|
|
78
|
+
return send(res, 415, { ok: false, error: "content_type_must_be_json" });
|
|
79
|
+
}
|
|
80
|
+
const body = await readBody(req);
|
|
81
|
+
if (body === "too_large") return send(res, 413, { ok: false, error: "payload_too_large" });
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = parseCallRequest(body, opts.allowRemoteCallbacks === true);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return send(res, 400, { ok: false, error: "bad_request", message: toErrorMessage(err) });
|
|
87
|
+
}
|
|
88
|
+
if (!opts.gateway.get(name)) {
|
|
89
|
+
return send(res, 404, { call_id: parsed.callId, ...notExposedBody(name) });
|
|
90
|
+
}
|
|
91
|
+
let cwd;
|
|
92
|
+
if (parsed.workdir !== void 0) {
|
|
93
|
+
const resolved = await resolveWorkdir(parsed.workdir, opts.gateway.projectRoot);
|
|
94
|
+
if ("error" in resolved) {
|
|
95
|
+
return send(res, 400, {
|
|
96
|
+
call_id: parsed.callId,
|
|
97
|
+
ok: false,
|
|
98
|
+
tool: name,
|
|
99
|
+
error: "bad_workdir",
|
|
100
|
+
message: resolved.error
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
cwd = resolved.cwd;
|
|
104
|
+
}
|
|
105
|
+
if (inFlight >= maxConcurrent) {
|
|
106
|
+
return send(res, 429, { call_id: parsed.callId, ok: false, error: "too_many_calls" });
|
|
107
|
+
}
|
|
108
|
+
inFlight++;
|
|
109
|
+
const settle = run(name, parsed, cwd).finally(() => {
|
|
110
|
+
inFlight--;
|
|
111
|
+
});
|
|
112
|
+
if (!parsed.callback) {
|
|
113
|
+
return send(res, 200, await settle);
|
|
114
|
+
}
|
|
115
|
+
send(res, 202, { call_id: parsed.callId, status: "accepted" });
|
|
116
|
+
const delivery = settle.then((result) => deliverCallback(parsed.callback, result)).catch(
|
|
117
|
+
(err) => opts.log?.(`tools serve: callback for ${parsed.callId} failed: ${toErrorMessage(err)}`)
|
|
118
|
+
);
|
|
119
|
+
pending.add(delivery);
|
|
120
|
+
void delivery.finally(() => pending.delete(delivery));
|
|
121
|
+
}
|
|
122
|
+
async function run(name, req, cwd) {
|
|
123
|
+
const base = { call_id: req.callId, tool: name };
|
|
124
|
+
try {
|
|
125
|
+
const outcome = await opts.gateway.call(name, req.input, { cwd });
|
|
126
|
+
if (outcome.kind === "not_exposed") return { ...base, ...notExposedBody(name) };
|
|
127
|
+
if (outcome.kind === "confirm_required") {
|
|
128
|
+
return {
|
|
129
|
+
...base,
|
|
130
|
+
ok: false,
|
|
131
|
+
error: "confirm_required",
|
|
132
|
+
message: `Tool "${name}" needs an interactive confirmation for this input, which this server cannot give.`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
...base,
|
|
137
|
+
ok: !outcome.isError && outcome.settlement === "completed",
|
|
138
|
+
settlement: outcome.settlement,
|
|
139
|
+
duration_ms: outcome.durationMs,
|
|
140
|
+
content: outcome.content
|
|
141
|
+
};
|
|
142
|
+
} catch (err) {
|
|
143
|
+
return { ...base, ok: false, error: "tool_threw", message: toErrorMessage(err) };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function deliverCallback(callback, result) {
|
|
147
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
148
|
+
const res = await doFetch(callback.url, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: {
|
|
151
|
+
"content-type": "application/json",
|
|
152
|
+
...callback.token ? { authorization: `Bearer ${callback.token}` } : {}
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify(result),
|
|
155
|
+
signal: AbortSignal.timeout(CALLBACK_TIMEOUT_MS),
|
|
156
|
+
redirect: "error"
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) throw new Error(`callback answered HTTP ${res.status}`);
|
|
159
|
+
}
|
|
160
|
+
function notExposedBody(name) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
tool: name,
|
|
164
|
+
error: "not_exposed",
|
|
165
|
+
message: withheldReason(opts.gateway, opts.registry, name)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return new Promise((resolve2, reject) => {
|
|
169
|
+
server.once("error", reject);
|
|
170
|
+
server.listen(opts.port ?? 0, host, () => {
|
|
171
|
+
server.removeListener("error", reject);
|
|
172
|
+
if (opts.unref) server.unref();
|
|
173
|
+
const port = server.address().port;
|
|
174
|
+
const shown = host === "::1" ? "[::1]" : host;
|
|
175
|
+
resolve2({
|
|
176
|
+
url: `http://${shown}:${port}/`,
|
|
177
|
+
port,
|
|
178
|
+
host,
|
|
179
|
+
close: async () => {
|
|
180
|
+
await new Promise((done) => server.close(() => done()));
|
|
181
|
+
await Promise.allSettled([...pending]);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function describe(tool) {
|
|
188
|
+
return {
|
|
189
|
+
name: tool.name,
|
|
190
|
+
description: tool.description,
|
|
191
|
+
input_schema: tool.inputSchema ?? { type: "object" },
|
|
192
|
+
capabilities: tool.capabilities ?? [],
|
|
193
|
+
mutating: tool.mutating
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function parseCallRequest(body, allowRemoteCallbacks) {
|
|
197
|
+
const text = body.trim();
|
|
198
|
+
const raw = text === "" ? {} : JSON.parse(text);
|
|
199
|
+
if (!isRecord(raw)) throw new Error("the body must be a JSON object");
|
|
200
|
+
const input = raw["input"] ?? {};
|
|
201
|
+
if (!isRecord(input)) throw new Error(`"input" must be a JSON object (the tool's input)`);
|
|
202
|
+
const callId = raw["call_id"] ?? randomUUID();
|
|
203
|
+
if (typeof callId !== "string" || callId.length === 0 || callId.length > 200) {
|
|
204
|
+
throw new Error('"call_id" must be a non-empty string of at most 200 characters');
|
|
205
|
+
}
|
|
206
|
+
const cb = raw["callback"];
|
|
207
|
+
const workdir = raw["workdir"];
|
|
208
|
+
if (workdir !== void 0 && workdir !== null && (typeof workdir !== "string" || workdir === "")) {
|
|
209
|
+
throw new Error('"workdir" must be a non-empty path');
|
|
210
|
+
}
|
|
211
|
+
const base = { input, callId, ...typeof workdir === "string" ? { workdir } : {} };
|
|
212
|
+
if (cb === void 0) return base;
|
|
213
|
+
if (!isRecord(cb) || typeof cb["url"] !== "string") {
|
|
214
|
+
throw new Error('"callback" must be { "url": string, "token"?: string }');
|
|
215
|
+
}
|
|
216
|
+
let target;
|
|
217
|
+
try {
|
|
218
|
+
target = new URL(cb["url"]);
|
|
219
|
+
} catch {
|
|
220
|
+
throw new Error('"callback.url" is not a valid URL');
|
|
221
|
+
}
|
|
222
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") {
|
|
223
|
+
throw new Error('"callback.url" must be http(s)');
|
|
224
|
+
}
|
|
225
|
+
if (!allowRemoteCallbacks && !isLoopbackHost(target.hostname.replace(/^\[(.*)\]$/, "$1"))) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
'"callback.url" must be a loopback address (start the server with --allow-remote-callbacks)'
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const token = cb["token"];
|
|
231
|
+
if (token !== void 0 && typeof token !== "string")
|
|
232
|
+
throw new Error('"callback.token" must be a string');
|
|
233
|
+
return { ...base, callback: { url: target.toString(), token } };
|
|
234
|
+
}
|
|
235
|
+
async function resolveWorkdir(workdir, projectRoot) {
|
|
236
|
+
let root;
|
|
237
|
+
let target;
|
|
238
|
+
try {
|
|
239
|
+
root = await realpath(projectRoot);
|
|
240
|
+
target = await realpath(path.resolve(projectRoot, workdir));
|
|
241
|
+
} catch {
|
|
242
|
+
return { error: `workdir "${workdir}" does not exist on the WrongStack side` };
|
|
243
|
+
}
|
|
244
|
+
const fold = (p) => process.platform === "win32" ? p.toLowerCase() : p;
|
|
245
|
+
const rel = path.relative(fold(root), fold(target));
|
|
246
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
247
|
+
return {
|
|
248
|
+
error: `workdir "${workdir}" is outside the project this server serves (${root}); start \`wstack tools serve\` in the task's directory`
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return { cwd: target };
|
|
252
|
+
}
|
|
253
|
+
function readBody(req) {
|
|
254
|
+
return new Promise((resolve2, reject) => {
|
|
255
|
+
const chunks = [];
|
|
256
|
+
let size = 0;
|
|
257
|
+
let tooLarge = false;
|
|
258
|
+
req.on("data", (chunk) => {
|
|
259
|
+
if (tooLarge) return;
|
|
260
|
+
size += chunk.byteLength;
|
|
261
|
+
if (size > BODY_CAP_BYTES) {
|
|
262
|
+
tooLarge = true;
|
|
263
|
+
resolve2("too_large");
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
chunks.push(chunk);
|
|
267
|
+
});
|
|
268
|
+
req.on("end", () => {
|
|
269
|
+
if (!tooLarge) resolve2(Buffer.concat(chunks).toString("utf8"));
|
|
270
|
+
});
|
|
271
|
+
req.on("error", reject);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function send(res, status, body) {
|
|
275
|
+
res.writeHead(status, {
|
|
276
|
+
"content-type": "application/json",
|
|
277
|
+
// This endpoint executes tools: nothing about it should be embedded,
|
|
278
|
+
// sniffed into another type, or leak its URL onward. No CORS headers.
|
|
279
|
+
"x-content-type-options": "nosniff",
|
|
280
|
+
"x-frame-options": "DENY",
|
|
281
|
+
"referrer-policy": "no-referrer"
|
|
282
|
+
});
|
|
283
|
+
res.end(JSON.stringify(body));
|
|
284
|
+
}
|
|
285
|
+
function isRecord(value) {
|
|
286
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export {
|
|
290
|
+
serveToolGatewayHttp
|
|
291
|
+
};
|
|
292
|
+
//# sourceMappingURL=chunk-RN5CFAVQ.js.map
|
|
@@ -61,6 +61,11 @@ var SDDState = class {
|
|
|
61
61
|
if (this.versioning === null) this.versioning = new SpecVersioning();
|
|
62
62
|
return this.versioning;
|
|
63
63
|
}
|
|
64
|
+
/** Drain fire-and-forget tracker writes before deleting or replacing the store. */
|
|
65
|
+
async flushTaskWrites() {
|
|
66
|
+
await this.taskTracker?.flushPersistence();
|
|
67
|
+
await this.taskStore?.flush?.();
|
|
68
|
+
}
|
|
64
69
|
clearTaskState() {
|
|
65
70
|
this.taskStore = null;
|
|
66
71
|
this.taskTracker = null;
|
|
@@ -406,4 +411,4 @@ export {
|
|
|
406
411
|
getTaskGraphId,
|
|
407
412
|
getTaskTrackerExport
|
|
408
413
|
};
|
|
409
|
-
//# sourceMappingURL=chunk-
|
|
414
|
+
//# sourceMappingURL=chunk-TDP7CZGE.js.map
|
|
@@ -63,6 +63,7 @@ var helpCmd = async (_args, deps) => {
|
|
|
63
63
|
["wstack sage sync", "Keep project SAGE memory synchronized through HQ"],
|
|
64
64
|
["wstack plugin", "Plugins: list, install, enable, disable, remove"],
|
|
65
65
|
["wstack tools | skills", "List registered tools / discovered skills"],
|
|
66
|
+
["wstack tools run <tool>", "Call a built-in tool from a script or external agent"],
|
|
66
67
|
["wstack typesafe", "TypeSafe account: status, login, test"],
|
|
67
68
|
["wstack update", "Self-update (--check-only)"]
|
|
68
69
|
]),
|
|
@@ -157,4 +158,4 @@ export {
|
|
|
157
158
|
versionCmd,
|
|
158
159
|
helpCmd
|
|
159
160
|
};
|
|
160
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-UROB7MK5.js.map
|