@krapht/ork 1.0.0-alpha.3 → 1.0.0-alpha.5
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/{__build_info__-BwrKe6yz.mjs → __build_info__-Dl8yTEu-.mjs} +2 -2
- package/dist/bin.mjs +63 -225
- package/dist/{clean-hXSaN8BT.mjs → clean-DYVmLkd4.mjs} +1 -1
- package/dist/{completion-Cga01yQP.mjs → completion-BVx917Yr.mjs} +5 -5
- package/dist/{context-mCAwzFFM.mjs → context-CepHRoj9.mjs} +3 -3
- package/dist/{decide-BvZnKErW.mjs → decide-CK_-lI5V.mjs} +3 -3
- package/dist/{find-CbetS8I2.mjs → find-DWiDJ5Lc.mjs} +1 -1
- package/dist/{format-D87bY6Ud.mjs → format-yBEAXZhk.mjs} +223 -17
- package/dist/{in-a-run-Bs4Wv-Cz.mjs → in-a-run-CKFIeMpU.mjs} +1 -1
- package/dist/{limits-RzKQ_zPk.mjs → limits-CwzPe9wZ.mjs} +1 -1
- package/dist/{list-DX65Sbu7.mjs → list-BE02pCZ9.mjs} +1 -1
- package/dist/{mcp-DKrrxowo.mjs → mcp-DMuBbUHU.mjs} +6 -6
- package/dist/{output-Q3TZ-Ave.mjs → output-BhYkshYq.mjs} +9 -6
- package/dist/{pause-B5Z41lR3.mjs → pause-C_Q0W6iX.mjs} +4 -4
- package/dist/{relay-BLnjMdjT.mjs → relay-COCvG-0I.mjs} +2 -2
- package/dist/{remove-IbcZOemm.mjs → remove-RVcX1KqP.mjs} +3 -3
- package/dist/{send-CJKEhBL1.mjs → send-OVpRei_5.mjs} +3 -3
- package/dist/{src-LSxjnWDH.mjs → src-CxZr4Bp2.mjs} +16 -8
- package/dist/{src-DnETlJ1r.mjs → src-Do_MhwiV.mjs} +186 -30
- package/dist/{src-XDSRnAZV.mjs → src-HxtJOMRr.mjs} +172 -167
- package/dist/{src-BTDzibW3.mjs → src-QEycKJWk.mjs} +26 -12
- package/dist/ui/THIRD_PARTY_LICENSES.txt +594 -0
- package/dist/ui/assets/app-DzI77WV9.js +75 -0
- package/dist/ui/assets/app-pTYGrAbI.css +1 -0
- package/dist/ui/assets/{index-BkI68sr5.js → index-2G_6XIIv.js} +3 -3
- package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/{ui-fvrO7Gxd.mjs → ui-Dz_qfUOz.mjs} +4 -4
- package/dist/{usage-bNdcBcj9.mjs → usage-BcLUZ1xw.mjs} +5 -5
- package/dist/{watch-EhlO304A.mjs → watch-Buyn0Mhz.mjs} +6 -6
- package/dist/{workflows-C5hYAVcE.mjs → workflows-1sntXqSd.mjs} +3 -3
- package/dist/{worktrees-BQFcpYz1.mjs → worktrees-B_Go_A9w.mjs} +4 -4
- package/package.json +1 -1
- package/dist/ui/assets/app-BNElhp7Z.js +0 -67
- package/dist/ui/assets/app-BNT0SuIz.css +0 -1
- package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $n as
|
|
1
|
+
import { $n as isToolNode, $t as evaluate, At as runScope, Bt as serversIn, F as declaredProblem, Mt as envReadsOf, Ot as heldTimeout, Pt as declaredOutputOf, Rt as MCP_FILE, Wn as markValue, Zn as beforeOf, ar as integrationFor, en as parseTemplate, er as programsRunBy, ft as sessionDriver, ir as TOOL_PREFIX, jt as calledWorkflows, mt as reportSchemaFor, nr as timeoutMs, rr as INTEGRATIONS, s as planReason, tn as renderTemplate, ut as planNodeSession, zt as mcpNotText } from "./src-Do_MhwiV.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { accessSync, constants,
|
|
3
|
+
import { accessSync, constants, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -123,6 +123,156 @@ const resultOf = (outcome, declared) => {
|
|
|
123
123
|
if (!result.ok) result.error = report.summary;
|
|
124
124
|
return result;
|
|
125
125
|
};
|
|
126
|
+
// ../nodes/src/environ.ts
|
|
127
|
+
/** A variable of the user's. */
|
|
128
|
+
const readEnv = (environ, name) => Object.hasOwn(environ, name) ? environ[name] : void 0;
|
|
129
|
+
// ../nodes/src/mcp-servers.ts
|
|
130
|
+
/**
|
|
131
|
+
* @file Reading an MCP server's definition from the project's `.mcp.json`, the
|
|
132
|
+
* file Claude Code reads too, so a server is defined in one place (RFC-006).
|
|
133
|
+
* `${VAR}` and `${VAR:-default}` read the user's environment, which is where
|
|
134
|
+
* secrets stay: Ork stores none, and a run masks each value read this way. A
|
|
135
|
+
* server that needs an OAuth sign-in holds Claude Code's tokens, not Ork's,
|
|
136
|
+
* so it is refused.
|
|
137
|
+
*/
|
|
138
|
+
const VARIABLE = /\$\{([A-Za-z_]\w*)(?::-([^}]*))?\}/gu;
|
|
139
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
140
|
+
const stringsOf = (value) => isRecord(value) ? Object.fromEntries(Object.entries(value).flatMap(([key, item]) => typeof item === "string" ? [[key, item]] : [])) : {};
|
|
141
|
+
const listOf = (value) => Array.isArray(value) ? value.flatMap((item) => typeof item === "string" ? [item] : []) : [];
|
|
142
|
+
/** The server as written, or why Ork can't call it. */
|
|
143
|
+
const readMcpServer = (root, name) => {
|
|
144
|
+
const servers = serversIn(root);
|
|
145
|
+
if (!servers.ok) return servers;
|
|
146
|
+
const known = Object.keys(servers.value);
|
|
147
|
+
const server = Object.hasOwn(servers.value, name) ? servers.value[name] : void 0;
|
|
148
|
+
if (!isRecord(server)) return {
|
|
149
|
+
ok: false,
|
|
150
|
+
error: `no MCP server \`${name}\` in ${MCP_FILE}${known.length === 0 ? "" : `, which has ${known.join(", ")}`}`
|
|
151
|
+
};
|
|
152
|
+
if ("oauth" in server) return {
|
|
153
|
+
ok: false,
|
|
154
|
+
error: `MCP server \`${name}\` signs in with OAuth, whose tokens are Claude Code's: give it a token through headers and \`env:\`, or call it from a session`
|
|
155
|
+
};
|
|
156
|
+
const notText = mcpNotText(server);
|
|
157
|
+
if (notText !== void 0) return {
|
|
158
|
+
ok: false,
|
|
159
|
+
error: `MCP server \`${name}\` has a value in ${notText} that isn't a string: write it in quotes`
|
|
160
|
+
};
|
|
161
|
+
const { type, url, command } = server;
|
|
162
|
+
if (type === "http" && typeof url === "string") return {
|
|
163
|
+
ok: true,
|
|
164
|
+
value: {
|
|
165
|
+
kind: "http",
|
|
166
|
+
url,
|
|
167
|
+
headers: stringsOf(server["headers"])
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
if ((type === void 0 || type === "stdio") && typeof command === "string") return {
|
|
171
|
+
ok: true,
|
|
172
|
+
value: {
|
|
173
|
+
kind: "stdio",
|
|
174
|
+
command,
|
|
175
|
+
args: listOf(server["args"]),
|
|
176
|
+
env: stringsOf(server["env"])
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
error: `MCP server \`${name}\` is not one Ork can call: it takes stdio and http servers`
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* The named servers' entries as written, for a session: `${VAR}` stays text for
|
|
186
|
+
* the `claude` that loads them to expand. The compiler has checked the names.
|
|
187
|
+
*/
|
|
188
|
+
const namedMcpEntries = (root, names) => {
|
|
189
|
+
if (names.length === 0) return {
|
|
190
|
+
ok: true,
|
|
191
|
+
value: {}
|
|
192
|
+
};
|
|
193
|
+
const servers = serversIn(root);
|
|
194
|
+
if (!servers.ok) return servers;
|
|
195
|
+
const mcpServers = {};
|
|
196
|
+
for (const name of names) {
|
|
197
|
+
const server = Object.hasOwn(servers.value, name) ? servers.value[name] : void 0;
|
|
198
|
+
if (!isRecord(server)) return {
|
|
199
|
+
ok: false,
|
|
200
|
+
error: `no MCP server \`${name}\` in ${MCP_FILE}`
|
|
201
|
+
};
|
|
202
|
+
mcpServers[name] = server;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
value: { mcpServers }
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
/** Each variable a server reads, and whether it has a default. */
|
|
210
|
+
const readsOf = (server) => {
|
|
211
|
+
return (server.kind === "stdio" ? [
|
|
212
|
+
server.command,
|
|
213
|
+
...server.args,
|
|
214
|
+
...Object.values(server.env)
|
|
215
|
+
] : [server.url, ...Object.values(server.headers)]).flatMap((text) => [...text.matchAll(VARIABLE)].map((match) => ({
|
|
216
|
+
name: String(match[1]),
|
|
217
|
+
fallback: match[2] !== void 0
|
|
218
|
+
})));
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* The server with its variables filled in, and the values to mask: all it read
|
|
222
|
+
* but those `plain` names, which aren't secret.
|
|
223
|
+
*/
|
|
224
|
+
const expandServer = (server, environ, plain = []) => {
|
|
225
|
+
const unset = readsOf(server).find(({ name, fallback }) => !fallback && readEnv(environ, name) === void 0);
|
|
226
|
+
if (unset !== void 0) return {
|
|
227
|
+
ok: false,
|
|
228
|
+
error: `${MCP_FILE} reads ${unset.name}, which is not set`
|
|
229
|
+
};
|
|
230
|
+
const values = [];
|
|
231
|
+
const fill = (text) => text.replaceAll(VARIABLE, (_all, name, fallback) => {
|
|
232
|
+
const value = readEnv(environ, name);
|
|
233
|
+
if (value !== void 0 && !plain.includes(name)) values.push(value);
|
|
234
|
+
return value ?? fallback ?? "";
|
|
235
|
+
});
|
|
236
|
+
const fillAll = (record) => Object.fromEntries(Object.entries(record).map(([key, text]) => [key, fill(text)]));
|
|
237
|
+
return {
|
|
238
|
+
ok: true,
|
|
239
|
+
value: {
|
|
240
|
+
server: server.kind === "stdio" ? {
|
|
241
|
+
kind: "stdio",
|
|
242
|
+
command: fill(server.command),
|
|
243
|
+
args: server.args.map((arg) => fill(arg)),
|
|
244
|
+
env: fillAll(server.env)
|
|
245
|
+
} : {
|
|
246
|
+
kind: "http",
|
|
247
|
+
url: fill(server.url),
|
|
248
|
+
headers: fillAll(server.headers)
|
|
249
|
+
},
|
|
250
|
+
values
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
/** What a workflow's MCP servers read of the user's, unless called not secret. */
|
|
255
|
+
const mcpVariablesOf = (workflow, root) => Object.values(workflow.nodes).flatMap((node) => {
|
|
256
|
+
if (node.type !== "mcp.call") return [];
|
|
257
|
+
const read = readMcpServer(root, node.server);
|
|
258
|
+
return read.ok ? readsOf(read.value).map(({ name }) => name).filter((name) => node.plainEnv?.includes(name) !== true) : [];
|
|
259
|
+
});
|
|
260
|
+
/** A stdio server's program, when its name can be known before it is filled in. */
|
|
261
|
+
const programOf = (server) => server.kind === "stdio" && !server.command.includes("$") ? server.command : void 0;
|
|
262
|
+
/**
|
|
263
|
+
* What stops a workflow's `mcp.call` nodes, found before a run spends anything:
|
|
264
|
+
* a server that isn't there or can't be called, a program that isn't installed,
|
|
265
|
+
* or a variable that is not set.
|
|
266
|
+
*/
|
|
267
|
+
const mcpProblems = (workflow, root, environ = process.env, finished = /* @__PURE__ */ new Set(), installed = () => true) => Object.entries(workflow.nodes).flatMap(([id, node]) => {
|
|
268
|
+
if (node.type !== "mcp.call" || finished.has(id)) return [];
|
|
269
|
+
const read = readMcpServer(root, node.server);
|
|
270
|
+
if (!read.ok) return [`${id}: ${read.error}`];
|
|
271
|
+
const given = new Set(Object.keys(node.env ?? {}));
|
|
272
|
+
const unset = readsOf(read.value).flatMap(({ name, fallback }) => fallback || given.has(name) || readEnv(environ, name) !== void 0 ? [] : [`${id}: ${MCP_FILE} reads ${name}, which is not set`]);
|
|
273
|
+
const program = programOf(read.value);
|
|
274
|
+
return program === void 0 || installed(program) ? unset : [`\`${id}\`: MCP server \`${node.server}\` runs \`${program}\`, which is not installed`].concat(unset);
|
|
275
|
+
});
|
|
126
276
|
/** The only built-in tools a fork has: it reads, and reports. */
|
|
127
277
|
const FORK_TOOLS = [
|
|
128
278
|
"Read",
|
|
@@ -660,163 +810,6 @@ const tokenize = (command) => {
|
|
|
660
810
|
args
|
|
661
811
|
};
|
|
662
812
|
};
|
|
663
|
-
// ../nodes/src/environ.ts
|
|
664
|
-
/** A variable of the user's. */
|
|
665
|
-
const readEnv = (environ, name) => Object.hasOwn(environ, name) ? environ[name] : void 0;
|
|
666
|
-
// ../nodes/src/mcp-servers.ts
|
|
667
|
-
/**
|
|
668
|
-
* @file Reading an MCP server's definition from the project's `.mcp.json`, the
|
|
669
|
-
* file Claude Code reads too, so a server is defined in one place (RFC-006).
|
|
670
|
-
* `${VAR}` and `${VAR:-default}` read the user's environment, which is where
|
|
671
|
-
* secrets stay: Ork stores none, and a run masks each value read this way. A
|
|
672
|
-
* server that needs an OAuth sign-in holds Claude Code's tokens, not Ork's,
|
|
673
|
-
* so it is refused.
|
|
674
|
-
*/
|
|
675
|
-
const MCP_FILE = ".mcp.json";
|
|
676
|
-
const VARIABLE = /\$\{([A-Za-z_]\w*)(?::-([^}]*))?\}/gu;
|
|
677
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
678
|
-
/** The file's `mcpServers`; `{}` when there is no file. */
|
|
679
|
-
const serversIn = (root) => {
|
|
680
|
-
const file = path.join(root, MCP_FILE);
|
|
681
|
-
if (!existsSync(file)) return {
|
|
682
|
-
ok: true,
|
|
683
|
-
value: {}
|
|
684
|
-
};
|
|
685
|
-
try {
|
|
686
|
-
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
687
|
-
const servers = isRecord(parsed) ? parsed["mcpServers"] : void 0;
|
|
688
|
-
return {
|
|
689
|
-
ok: true,
|
|
690
|
-
value: isRecord(servers) ? servers : {}
|
|
691
|
-
};
|
|
692
|
-
} catch (error) {
|
|
693
|
-
const reason = error instanceof SyntaxError ? "it is not valid JSON" : error instanceof Error ? error.message : String(error);
|
|
694
|
-
return {
|
|
695
|
-
ok: false,
|
|
696
|
-
error: `${MCP_FILE} can't be read: ${reason}`
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
const stringsOf = (value) => isRecord(value) ? Object.fromEntries(Object.entries(value).flatMap(([key, item]) => typeof item === "string" ? [[key, item]] : [])) : {};
|
|
701
|
-
const listOf = (value) => Array.isArray(value) ? value.flatMap((item) => typeof item === "string" ? [item] : []) : [];
|
|
702
|
-
/** A server's field that holds something other than text: it would be dropped. */
|
|
703
|
-
const notTextIn = (server) => [
|
|
704
|
-
"args",
|
|
705
|
-
"env",
|
|
706
|
-
"headers"
|
|
707
|
-
].find((field) => {
|
|
708
|
-
const value = server[field];
|
|
709
|
-
return (Array.isArray(value) ? value : isRecord(value) ? Object.values(value) : []).some((item) => typeof item !== "string");
|
|
710
|
-
});
|
|
711
|
-
/** The server as written, or why Ork can't call it. */
|
|
712
|
-
const readMcpServer = (root, name) => {
|
|
713
|
-
const servers = serversIn(root);
|
|
714
|
-
if (!servers.ok) return servers;
|
|
715
|
-
const known = Object.keys(servers.value);
|
|
716
|
-
const server = Object.hasOwn(servers.value, name) ? servers.value[name] : void 0;
|
|
717
|
-
if (!isRecord(server)) return {
|
|
718
|
-
ok: false,
|
|
719
|
-
error: `no MCP server \`${name}\` in ${MCP_FILE}${known.length === 0 ? "" : `, which has ${known.join(", ")}`}`
|
|
720
|
-
};
|
|
721
|
-
if ("oauth" in server) return {
|
|
722
|
-
ok: false,
|
|
723
|
-
error: `MCP server \`${name}\` signs in with OAuth, whose tokens are Claude Code's: give it a token through headers and \`env:\`, or call it from a session`
|
|
724
|
-
};
|
|
725
|
-
const notText = notTextIn(server);
|
|
726
|
-
if (notText !== void 0) return {
|
|
727
|
-
ok: false,
|
|
728
|
-
error: `MCP server \`${name}\` has a value in ${notText} that isn't a string: write it in quotes`
|
|
729
|
-
};
|
|
730
|
-
const { type, url, command } = server;
|
|
731
|
-
if (type === "http" && typeof url === "string") return {
|
|
732
|
-
ok: true,
|
|
733
|
-
value: {
|
|
734
|
-
kind: "http",
|
|
735
|
-
url,
|
|
736
|
-
headers: stringsOf(server["headers"])
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
if ((type === void 0 || type === "stdio") && typeof command === "string") return {
|
|
740
|
-
ok: true,
|
|
741
|
-
value: {
|
|
742
|
-
kind: "stdio",
|
|
743
|
-
command,
|
|
744
|
-
args: listOf(server["args"]),
|
|
745
|
-
env: stringsOf(server["env"])
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
return {
|
|
749
|
-
ok: false,
|
|
750
|
-
error: `MCP server \`${name}\` is not one Ork can call: it takes stdio and http servers`
|
|
751
|
-
};
|
|
752
|
-
};
|
|
753
|
-
/** Each variable a server reads, and whether it has a default. */
|
|
754
|
-
const readsOf = (server) => {
|
|
755
|
-
return (server.kind === "stdio" ? [
|
|
756
|
-
server.command,
|
|
757
|
-
...server.args,
|
|
758
|
-
...Object.values(server.env)
|
|
759
|
-
] : [server.url, ...Object.values(server.headers)]).flatMap((text) => [...text.matchAll(VARIABLE)].map((match) => ({
|
|
760
|
-
name: String(match[1]),
|
|
761
|
-
fallback: match[2] !== void 0
|
|
762
|
-
})));
|
|
763
|
-
};
|
|
764
|
-
/**
|
|
765
|
-
* The server with its variables filled in, and the values to mask: all it read
|
|
766
|
-
* but those `plain` names, which aren't secret.
|
|
767
|
-
*/
|
|
768
|
-
const expandServer = (server, environ, plain = []) => {
|
|
769
|
-
const unset = readsOf(server).find(({ name, fallback }) => !fallback && readEnv(environ, name) === void 0);
|
|
770
|
-
if (unset !== void 0) return {
|
|
771
|
-
ok: false,
|
|
772
|
-
error: `${MCP_FILE} reads ${unset.name}, which is not set`
|
|
773
|
-
};
|
|
774
|
-
const values = [];
|
|
775
|
-
const fill = (text) => text.replaceAll(VARIABLE, (_all, name, fallback) => {
|
|
776
|
-
const value = readEnv(environ, name);
|
|
777
|
-
if (value !== void 0 && !plain.includes(name)) values.push(value);
|
|
778
|
-
return value ?? fallback ?? "";
|
|
779
|
-
});
|
|
780
|
-
const fillAll = (record) => Object.fromEntries(Object.entries(record).map(([key, text]) => [key, fill(text)]));
|
|
781
|
-
return {
|
|
782
|
-
ok: true,
|
|
783
|
-
value: {
|
|
784
|
-
server: server.kind === "stdio" ? {
|
|
785
|
-
kind: "stdio",
|
|
786
|
-
command: fill(server.command),
|
|
787
|
-
args: server.args.map((arg) => fill(arg)),
|
|
788
|
-
env: fillAll(server.env)
|
|
789
|
-
} : {
|
|
790
|
-
kind: "http",
|
|
791
|
-
url: fill(server.url),
|
|
792
|
-
headers: fillAll(server.headers)
|
|
793
|
-
},
|
|
794
|
-
values
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
};
|
|
798
|
-
/** What a workflow's MCP servers read of the user's, unless called not secret. */
|
|
799
|
-
const mcpVariablesOf = (workflow, root) => Object.values(workflow.nodes).flatMap((node) => {
|
|
800
|
-
if (node.type !== "mcp.call") return [];
|
|
801
|
-
const read = readMcpServer(root, node.server);
|
|
802
|
-
return read.ok ? readsOf(read.value).map(({ name }) => name).filter((name) => node.plainEnv?.includes(name) !== true) : [];
|
|
803
|
-
});
|
|
804
|
-
/** A stdio server's program, when its name can be known before it is filled in. */
|
|
805
|
-
const programOf = (server) => server.kind === "stdio" && !server.command.includes("$") ? server.command : void 0;
|
|
806
|
-
/**
|
|
807
|
-
* What stops a workflow's `mcp.call` nodes, found before a run spends anything:
|
|
808
|
-
* a server that isn't there or can't be called, a program that isn't installed,
|
|
809
|
-
* or a variable that is not set.
|
|
810
|
-
*/
|
|
811
|
-
const mcpProblems = (workflow, root, environ = process.env, finished = /* @__PURE__ */ new Set(), installed = () => true) => Object.entries(workflow.nodes).flatMap(([id, node]) => {
|
|
812
|
-
if (node.type !== "mcp.call" || finished.has(id)) return [];
|
|
813
|
-
const read = readMcpServer(root, node.server);
|
|
814
|
-
if (!read.ok) return [`${id}: ${read.error}`];
|
|
815
|
-
const given = new Set(Object.keys(node.env ?? {}));
|
|
816
|
-
const unset = readsOf(read.value).flatMap(({ name, fallback }) => fallback || given.has(name) || readEnv(environ, name) !== void 0 ? [] : [`${id}: ${MCP_FILE} reads ${name}, which is not set`]);
|
|
817
|
-
const program = programOf(read.value);
|
|
818
|
-
return program === void 0 || installed(program) ? unset : [`\`${id}\`: MCP server \`${node.server}\` runs \`${program}\`, which is not installed`].concat(unset);
|
|
819
|
-
});
|
|
820
813
|
// ../nodes/src/tool-scrub.ts
|
|
821
814
|
/**
|
|
822
815
|
* @file Masking a secret in what a program prints. A plain search, so a value
|
|
@@ -1589,7 +1582,12 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1589
1582
|
},
|
|
1590
1583
|
error: plan.error
|
|
1591
1584
|
};
|
|
1592
|
-
const
|
|
1585
|
+
const servers = namedMcpEntries(context.projectRoot, plan.mcp);
|
|
1586
|
+
if (!servers.ok) return notStarted(servers.error);
|
|
1587
|
+
const spec = {
|
|
1588
|
+
...specFor(plan.spec, context),
|
|
1589
|
+
...servers.value
|
|
1590
|
+
};
|
|
1593
1591
|
if (options.machineClaude === true) spec.machineClaude = true;
|
|
1594
1592
|
const driver = sessionDriver({
|
|
1595
1593
|
override: options.driver,
|
|
@@ -1691,10 +1689,11 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1691
1689
|
};
|
|
1692
1690
|
// ../nodes/src/gate.ts
|
|
1693
1691
|
/**
|
|
1694
|
-
* @file The `human.gate` node type: asks the human the rendered question,
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1692
|
+
* @file The `human.gate` node type: asks the human the rendered question, with
|
|
1693
|
+
* its document if it has one, both in readable text (no raw JSON), outputs
|
|
1694
|
+
* their choice with the note they gave, and answers with the `default` when
|
|
1695
|
+
* the `timeout` passes. The timeout counts only while the run and the gate
|
|
1696
|
+
* are not paused (ADR-041).
|
|
1698
1697
|
*/
|
|
1699
1698
|
/** A promise that never settles: the losing side of a race. */
|
|
1700
1699
|
const NEVER = new Promise(() => {});
|
|
@@ -1716,8 +1715,13 @@ const gateExecutor = async (context) => {
|
|
|
1716
1715
|
output: {},
|
|
1717
1716
|
error: `${context.id} is not a gate`
|
|
1718
1717
|
};
|
|
1719
|
-
const
|
|
1720
|
-
const
|
|
1718
|
+
const scope = runScope(context.inputs, context.outputs, context.runId);
|
|
1719
|
+
const render = (text) => {
|
|
1720
|
+
const template = parseTemplate(text);
|
|
1721
|
+
return template.ok ? renderTemplate(template.parts, scope, { readable: true }) : text;
|
|
1722
|
+
};
|
|
1723
|
+
const question = render(node.question);
|
|
1724
|
+
const document = render(node.document ?? "");
|
|
1721
1725
|
const options = node.options.map((o) => choiceOf(o));
|
|
1722
1726
|
const withdraw = new AbortController();
|
|
1723
1727
|
context.signal.addEventListener("abort", () => {
|
|
@@ -1734,6 +1738,7 @@ const gateExecutor = async (context) => {
|
|
|
1734
1738
|
}
|
|
1735
1739
|
};
|
|
1736
1740
|
if (node.recommended !== void 0) asked.recommended = node.recommended;
|
|
1741
|
+
if (document.trim() !== "") asked.document = document;
|
|
1737
1742
|
const answer = context.human.ask(asked).then((choice) => ({
|
|
1738
1743
|
choice,
|
|
1739
1744
|
answeredBy: "user",
|
|
@@ -2130,4 +2135,4 @@ const missingPrograms = (workflow, installed = (program) => onPath(program)) =>
|
|
|
2130
2135
|
}
|
|
2131
2136
|
return [...missing.values()];
|
|
2132
2137
|
};
|
|
2133
|
-
export {
|
|
2138
|
+
export { namedMcpEntries as _, builtinExecutors as a, toolExecutor as c, maskedEnv as d, maskedNamesOfRun as f, clearOutputFiles as g, sessionEnv as h, onPath as i, clearStateFiles as l, runScrubber as m, missingProgramMessage as n, spawnTool as o, runMarkerIn as p, missingPrograms as r, mcpCallExecutor as s, envProblems as t, inheritedScrubber as u, resultOf as v };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { B as linkNodes,
|
|
2
|
-
import { C as feedbackActions, S as placeOf$1, _ as previewPost, a as registryShown, b as acceptItem, c as checkDue, f as readUpdateCache, g as nodeSession, h as writeUpdateCache, i as registryOf, l as fetchLatest, m as updateCachePath, o as updateSwitchOff, p as refreshUpdateCache, s as runInstall, u as newerVersion, w as openFixRun, x as lessonPlaceOf, y as recordNote } from "./__build_info__-
|
|
3
|
-
import { n as pendingDecisions, r as builtinProblem, t as answerDecision } from "./decide-
|
|
4
|
-
import { n as newRunId, t as findRun } from "./find-
|
|
5
|
-
import { c as openStore, l as storePath, s as nodeOf } from "./output-
|
|
1
|
+
import { B as linkNodes, Bn as readDocument, Cn as placeFiles, Ct as walkChecks, En as runDirOf, G as requestContinue, Gn as EdgeInputSchema, Gt as planPost, Hn as parseWorkflow, In as skillsIn, J as tellLiveRun, Jn as expandEdge, K as requestPause, Kt as runGh, Ln as WorkflowEditSchema, Nt as nodeSays, On as workflowDirOf, Rn as applyEdits, St as checkWorkflowText, U as unlinkNodes, Vn as renderJsonSchema, Wt as DISCUSSIONS, Xn as NodeSchema, _t as runStatus, bt as processAlive, ct as explainWorkflowText, d as runningRunIds, f as runningScript, gt as pausedOf, ht as liveStatus, jn as LESSON_CAP_TOKENS, kn as inside, l as planUpgrade, nn as readCondition, nt as resumable, o as limitsReport, ot as detachRun, p as detectInstaller, q as requestStop, qt as sendPost, r as capsHere, st as followEvents, tt as detachResume, u as releaseNotes, v as triggersReport, vn as loadAgents, vt as runTree, wn as placeFor, wt as walkSummary, zn as newWorkflowText } from "./src-Do_MhwiV.mjs";
|
|
2
|
+
import { C as feedbackActions, S as placeOf$1, _ as previewPost, a as registryShown, b as acceptItem, c as checkDue, f as readUpdateCache, g as nodeSession, h as writeUpdateCache, i as registryOf, l as fetchLatest, m as updateCachePath, o as updateSwitchOff, p as refreshUpdateCache, s as runInstall, u as newerVersion, w as openFixRun, x as lessonPlaceOf, y as recordNote } from "./__build_info__-Dl8yTEu-.mjs";
|
|
3
|
+
import { n as pendingDecisions, r as builtinProblem, t as answerDecision } from "./decide-CK_-lI5V.mjs";
|
|
4
|
+
import { n as newRunId, t as findRun } from "./find-DWiDJ5Lc.mjs";
|
|
5
|
+
import { c as openStore, l as storePath, s as nodeOf } from "./output-BhYkshYq.mjs";
|
|
6
6
|
import { n as usageReport } from "./report-DK4LGqwF.mjs";
|
|
7
|
-
import { a as planClean, i as keyOf, o as stepActs, r as cleanStep, s as stepPrunes, t as applyClean } from "./clean-
|
|
8
|
-
import { a as readWorktrees, c as planLine, i as projectHead, l as removeRefusal, n as lostWork, o as labelOf, r as planRemove, s as mergeStateOf, t as applyRemove, u as stateParts } from "./remove-
|
|
7
|
+
import { a as planClean, i as keyOf, o as stepActs, r as cleanStep, s as stepPrunes, t as applyClean } from "./clean-DYVmLkd4.mjs";
|
|
8
|
+
import { a as readWorktrees, c as planLine, i as projectHead, l as removeRefusal, n as lostWork, o as labelOf, r as planRemove, s as mergeStateOf, t as applyRemove, u as stateParts } from "./remove-RVcX1KqP.mjs";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -349,7 +349,9 @@ const EventsQuerySchema = z.strictObject({
|
|
|
349
349
|
});
|
|
350
350
|
const DecisionsQuerySchema = z.strictObject({
|
|
351
351
|
run: z.string().min(1).optional(),
|
|
352
|
-
project: z.string().min(1).optional()
|
|
352
|
+
project: z.string().min(1).optional(),
|
|
353
|
+
/** Only runs of this workflow, by name; ANDed with `run` or `project`. */
|
|
354
|
+
workflow: z.string().min(1).optional()
|
|
353
355
|
});
|
|
354
356
|
/** An answer: the option, and what the person said with it, if anything. */
|
|
355
357
|
const AnswerSchema = z.strictObject({
|
|
@@ -483,7 +485,8 @@ const STREAMS = {
|
|
|
483
485
|
run: "/api/runs/:id/stream",
|
|
484
486
|
/**
|
|
485
487
|
* `runs`, `decisions`, `version`, each when it changes; `?project=` keeps
|
|
486
|
-
* one
|
|
488
|
+
* one, `?workflow=` narrows `decisions` to that workflow's. `upgrade`: the
|
|
489
|
+
* upgrade's output, then `end` (contract-update.ts).
|
|
487
490
|
*/
|
|
488
491
|
overview: "/api/stream",
|
|
489
492
|
upgrade: "/api/upgrade/stream"
|
|
@@ -1625,10 +1628,17 @@ const readRoutes = (context) => ({
|
|
|
1625
1628
|
return ok(query.limit === void 0 ? newest : newest.slice(0, query.limit));
|
|
1626
1629
|
},
|
|
1627
1630
|
decisions: ({ query }) => {
|
|
1628
|
-
|
|
1631
|
+
const workflow = query.workflow === void 0 ? {} : { workflow: query.workflow };
|
|
1632
|
+
if (query.run === void 0) return ok(pendingDecisions(context.store, {
|
|
1633
|
+
...workflow,
|
|
1634
|
+
...query.project === void 0 ? {} : { project: projectRoot(context, query.project) }
|
|
1635
|
+
}));
|
|
1629
1636
|
const found = runOf(context, query.run);
|
|
1630
1637
|
if (!found.ok) return found.refusal;
|
|
1631
|
-
return ok(pendingDecisions(context.store, {
|
|
1638
|
+
return ok(pendingDecisions(context.store, {
|
|
1639
|
+
...workflow,
|
|
1640
|
+
runId: found.run.id
|
|
1641
|
+
}));
|
|
1632
1642
|
},
|
|
1633
1643
|
decision: ({ params }) => {
|
|
1634
1644
|
const found = context.store.decision(params.id);
|
|
@@ -2869,6 +2879,7 @@ const runStream = async (context, req, res, url, id, shutdown) => {
|
|
|
2869
2879
|
const overviewStream = (context, res, url, shutdown) => {
|
|
2870
2880
|
const key = url.searchParams.get("project");
|
|
2871
2881
|
const root = key === null ? void 0 : projectOf(context, key)?.root ?? key;
|
|
2882
|
+
const workflow = url.searchParams.get("workflow");
|
|
2872
2883
|
const stream = openEventStream(res);
|
|
2873
2884
|
const last = {
|
|
2874
2885
|
runs: "",
|
|
@@ -2881,7 +2892,10 @@ const overviewStream = (context, res, url, shutdown) => {
|
|
|
2881
2892
|
...filter,
|
|
2882
2893
|
limit: OVERVIEW_RUNS
|
|
2883
2894
|
}).map((run) => viewOf$2(context, run));
|
|
2884
|
-
const decisions = pendingDecisions(context.store,
|
|
2895
|
+
const decisions = pendingDecisions(context.store, {
|
|
2896
|
+
...filter,
|
|
2897
|
+
...workflow === null ? {} : { workflow }
|
|
2898
|
+
});
|
|
2885
2899
|
for (const [name, value] of [
|
|
2886
2900
|
["runs", runs],
|
|
2887
2901
|
["decisions", decisions],
|