@krapht/ork 1.0.0-alpha.10 → 1.0.0-alpha.12

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.
Files changed (41) hide show
  1. package/dist/{__build_info__-DFF18K8y.mjs → __build_info__-BJbE3Zvp.mjs} +4 -72
  2. package/dist/attach-BmV5ckb9.mjs +89 -0
  3. package/dist/bin.mjs +467 -864
  4. package/dist/{decide-3l9q8UtX.mjs → builtin-files-w0YULJ4c.mjs} +2 -50
  5. package/dist/chips-CyeB_rhR.mjs +43 -0
  6. package/dist/{clean-DVaEH9e7.mjs → clean-D8GI_2K-.mjs} +1 -1
  7. package/dist/{completion-Dv5kf598.mjs → completion-CzElB8nS.mjs} +24 -16
  8. package/dist/{context-CEaKOVmG.mjs → context-BAwgVSX5.mjs} +43 -31
  9. package/dist/decide-DlbyctOE.mjs +50 -0
  10. package/dist/{find-DEjjOlTG.mjs → find-p05wlbmu.mjs} +1 -1
  11. package/dist/format-Dni98cbh.mjs +206 -0
  12. package/dist/{help-groups-BwYoAWsh.mjs → help-groups-DYP_zMRP.mjs} +4 -0
  13. package/dist/{in-a-run-BS9Nc-JW.mjs → in-a-run-BI-hLIl_.mjs} +4 -2
  14. package/dist/{limits-BSwEjQST.mjs → limits-CElmhowV.mjs} +1 -1
  15. package/dist/{list-Qxr6rwsY.mjs → list-zW2xKuGr.mjs} +1 -1
  16. package/dist/{mcp-CslnG5wf.mjs → mcp-CM3b65AZ.mjs} +6 -6
  17. package/dist/output-CnRzrbUM.mjs +423 -0
  18. package/dist/{pause-D7OVB5Ac.mjs → pause-BuS83WAI.mjs} +4 -4
  19. package/dist/{relay-EceRIebO.mjs → relay-DAsJA8wF.mjs} +2 -2
  20. package/dist/{remove-0IQZVWmI.mjs → remove-Dh7D_hVn.mjs} +3 -3
  21. package/dist/{report-DK4LGqwF.mjs → report-CEN8VoUb.mjs} +31 -20
  22. package/dist/{send-C0uwfUYi.mjs → send-MFEQZH31.mjs} +3 -3
  23. package/dist/{src-cmmXKHn1.mjs → src-BfxkJOYm.mjs} +234 -150
  24. package/dist/{src-Df6KOIDC.mjs → src-CdDTQLdQ.mjs} +2081 -1035
  25. package/dist/{src-oaJYIddp.mjs → src-D1QedD5y.mjs} +523 -81
  26. package/dist/{src-i5k2Y-kf.mjs → src-S9yK2OHU.mjs} +79 -11
  27. package/dist/store-B2OaE4ID.mjs +1356 -0
  28. package/dist/ui/assets/{app-qs8Lofv6.js → app-CAoZtku5.js} +16 -16
  29. package/dist/ui/assets/app-QoHVi-mb.css +1 -0
  30. package/dist/ui/assets/{index-DV1ZaOk-.js → index-BL7Ad0OA.js} +2 -2
  31. package/dist/ui/index.html +1 -1
  32. package/dist/{ui-CBkHJdqk.mjs → ui-C4Ys26oO.mjs} +4 -4
  33. package/dist/{usage-Cd8pEYyK.mjs → usage-C2zx-X6q.mjs} +43 -32
  34. package/dist/verdict-CIy8tAyX.mjs +84 -0
  35. package/dist/{watch-DW5R2OPO.mjs → watch-DzCeqM05.mjs} +28 -16
  36. package/dist/{workflows-BT1gDRIV.mjs → workflows-Rr5w90A0.mjs} +3 -3
  37. package/dist/{worktrees-g1MQMGie.mjs → worktrees-f6SAbum5.mjs} +11 -10
  38. package/package.json +1 -1
  39. package/dist/format-Cb5_m-Qd.mjs +0 -191
  40. package/dist/output-Bqljtmp_.mjs +0 -40
  41. package/dist/ui/assets/app-C8uhHVYp.css +0 -1
@@ -1,4 +1,4 @@
1
- import { Fn as plainPathInside, In as writePlainFile, Pn as guardedBase, an as socketPath, bt as liveStatus, sn as wakeRun, wn as placeFiles } from "./src-Df6KOIDC.mjs";
1
+ import { Bn as placeFiles, Qn as writePlainFile, Xn as guardedBase, Zn as plainPathInside } from "./src-CdDTQLdQ.mjs";
2
2
  import path from "node:path";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { createHash } from "node:crypto";
@@ -62,52 +62,4 @@ const builtinWorkflowFile = (place, arg) => {
62
62
  if (existsSync(file) && readFileSync(file, "utf8") === text) return file;
63
63
  return writePlainFile(base, file, text).ok ? file : void 0;
64
64
  };
65
- // ../core/src/remote/decide.ts
66
- /**
67
- * @file The open decisions of live runs, and answering one from another process
68
- * (`ork decide`, the local server), with the person's note if they gave one.
69
- * An answer goes to the store (the first one wins), then the run is woken on
70
- * its socket so it picks the answer up at once (specs/messaging.md).
71
- */
72
- /** Open decisions whose run is still live, oldest first, ANDing the filter. */
73
- const pendingDecisions = (store, filter) => {
74
- const live = /* @__PURE__ */ new Map();
75
- return store.decisions({
76
- ...filter,
77
- status: "open"
78
- }).filter((decision) => {
79
- if (!live.has(decision.runId)) {
80
- const [run] = store.lookup(decision.runId);
81
- live.set(decision.runId, run !== void 0 && liveStatus(run) === "running" && (filter.workflow === void 0 || run.workflow === filter.workflow));
82
- }
83
- return live.get(decision.runId) === true;
84
- });
85
- };
86
- /**
87
- * Answers an open decision with one of its options, and wakes its run. `home`
88
- * is `~`, where the sockets are (tests pass a folder); `by` says who answered;
89
- * `note` is what they said with it, if anything (blank is nothing).
90
- */
91
- const answerDecision = async (store, id, choice, home, by = "decide", note) => {
92
- const decision = store.decision(id);
93
- if (decision === void 0) return {
94
- ok: false,
95
- error: `No decision \`${id}\`.`
96
- };
97
- if (!decision.options.some((o) => o.id === choice)) return {
98
- ok: false,
99
- error: `\`${choice}\` is not an option of \`${id}\`: \`${decision.options.map((o) => o.id).join("`, `")}\``
100
- };
101
- if (!store.answerDecision(id, choice, by, note)) {
102
- const now = store.decision(id);
103
- return {
104
- ok: false,
105
- error: now?.status === "answered" ? `${id} was already answered: ${now.answer ?? ""} (${now.answeredBy ?? ""})` : `${id} was withdrawn`
106
- };
107
- }
108
- return {
109
- ok: true,
110
- woke: await wakeRun(socketPath(decision.runId, home))
111
- };
112
- };
113
- export { builtinWorkflowNames as a, builtinWorkflowFile as i, pendingDecisions as n, builtinWorkflowText as o, builtinProblem as r, isBuiltinArg as s, answerDecision as t };
65
+ export { isBuiltinArg as a, builtinWorkflowText as i, builtinWorkflowFile as n, builtinWorkflowNames as r, builtinProblem as t };
@@ -0,0 +1,43 @@
1
+ import { d as apply, l as CHIP, u as NODE_PALETTE } from "./output-CnRzrbUM.mjs";
2
+ // src/term/chips.ts
3
+ /** The chip words the kit's commands use. */
4
+ const CHIP_LABELS = {
5
+ ask: "ASK",
6
+ run: "RUN",
7
+ ok: "OK",
8
+ fail: "FAIL",
9
+ wait: "WAIT",
10
+ warn: "WARN",
11
+ skip: "SKIP",
12
+ error: "ERROR"
13
+ };
14
+ /** A run's or trigger's state word as a chip: its label and role. */
15
+ const statusChip = (status) => {
16
+ const role = status === "running" ? "accent" : status === "succeeded" ? "ok" : status === "failed" || status === "crashed" ? "danger" : [
17
+ "paused",
18
+ "budget_exhausted",
19
+ "quota_exhausted",
20
+ "timed_out",
21
+ "interrupted"
22
+ ].includes(status) ? "warn" : "muted";
23
+ return {
24
+ label: status === "running" ? CHIP_LABELS.run : status === "succeeded" ? CHIP_LABELS.ok : status === "failed" ? CHIP_LABELS.fail : status.replaceAll("_", " ").toUpperCase(),
25
+ role
26
+ };
27
+ };
28
+ /** A chip: ` LABEL ` on the role's background; `[LABEL]` when plain. */
29
+ const chip = (term, label, role) => term.rich ? apply(term, CHIP[role], ` ${label} `) : `[${label}]`;
30
+ /** A stable non-negative number for `text` (FNV-1a). */
31
+ const hash = (text) => {
32
+ let h = 2166136261;
33
+ for (const char of text) h = Math.imul(h ^ (char.codePointAt(0) ?? 0), 16777619);
34
+ return h >>> 0;
35
+ };
36
+ /** The palette slot of a node id, 0 to 5, the same on every call. */
37
+ const nodeSlot = (id) => hash(id) % NODE_PALETTE.length;
38
+ /** A node's chip: its id on the color its id picks; `[id]` when plain. */
39
+ const nodeChip = (term, id) => {
40
+ const format = NODE_PALETTE[nodeSlot(id)] ?? CHIP.muted;
41
+ return term.rich ? apply(term, format, ` ${id} `) : `[${id}]`;
42
+ };
43
+ export { statusChip as i, chip as n, nodeChip as r, CHIP_LABELS as t };
@@ -1,4 +1,4 @@
1
- import { N as gitOk, j as removeWorktree } from "./src-Df6KOIDC.mjs";
1
+ import { D as removeWorktree, k as gitOk } from "./src-CdDTQLdQ.mjs";
2
2
  // ../core/src/worktree/clean.ts
3
3
  /**
4
4
  * @file What `ork worktrees clean` and `ork worktrees remove` do with what
@@ -1,9 +1,10 @@
1
- import { D as openStore, O as storePath, Tn as placeFor, bt as liveStatus, mr as AGENT_DRIVERS, rr as NodeIdSchema, wn as placeFiles } from "./src-Df6KOIDC.mjs";
2
- import { a as builtinWorkflowNames, n as pendingDecisions, o as builtinWorkflowText, s as isBuiltinArg } from "./decide-3l9q8UtX.mjs";
3
- import { t as findRun } from "./find-DEjjOlTG.mjs";
4
- import { a as findEntry, c as subcommandsOf, d as SHELL_SCRIPTS, l as COMPLETE_COMMAND, n as groupedEntries, r as argInfoOf, t as groupOf, u as SHELLS } from "./help-groups-BwYoAWsh.mjs";
5
- import { o as writeErr } from "./output-Bqljtmp_.mjs";
6
- import { a as resolveWorkflowArg, n as WORKFLOW_SUFFIX } from "./workflows-BT1gDRIV.mjs";
1
+ import { Ar as AGENT_DRIVERS, Bn as placeFiles, C as openStore, Vn as placeFor, w as storePath, xt as liveStatus, yr as NodeIdSchema } from "./src-CdDTQLdQ.mjs";
2
+ import { a as isBuiltinArg, i as builtinWorkflowText, r as builtinWorkflowNames } from "./builtin-files-w0YULJ4c.mjs";
3
+ import { n as pendingDecisions } from "./decide-DlbyctOE.mjs";
4
+ import { t as findRun } from "./find-p05wlbmu.mjs";
5
+ import { a as findEntry, c as subcommandsOf, d as SHELL_SCRIPTS, l as COMPLETE_COMMAND, n as groupedEntries, r as argInfoOf, t as groupOf, u as SHELLS } from "./help-groups-DYP_zMRP.mjs";
6
+ import { s as writeErr } from "./output-CnRzrbUM.mjs";
7
+ import { a as resolveWorkflowArg, n as WORKFLOW_SUFFIX } from "./workflows-Rr5w90A0.mjs";
7
8
  import path from "node:path";
8
9
  import { parse } from "yaml";
9
10
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -184,16 +185,23 @@ const subcommandSections = (position) => {
184
185
  };
185
186
  const flagSection = (position) => ({
186
187
  title: "flags",
187
- candidates: [...position.flags.flatMap((flag) => [{
188
- value: `--${flag.name}`,
189
- description: brief(flag.description)
190
- }, ...flag.negatable ? [{
191
- value: `--no-${flag.name}`,
192
- description: `Turn off --${flag.name}`
193
- }] : []]), {
194
- value: "--help",
195
- description: "Show this command's help"
196
- }]
188
+ candidates: [
189
+ ...position.flags.flatMap((flag) => [{
190
+ value: `--${flag.name}`,
191
+ description: brief(flag.description)
192
+ }, ...flag.negatable ? [{
193
+ value: `--no-${flag.name}`,
194
+ description: `Turn off --${flag.name}`
195
+ }] : []]),
196
+ {
197
+ value: "--plain",
198
+ description: "Plain output, no color or redraw"
199
+ },
200
+ {
201
+ value: "--help",
202
+ description: "Show this command's help"
203
+ }
204
+ ]
197
205
  });
198
206
  const fromProvider = (provider, context) => provider === void 0 ? NONE : {
199
207
  sections: [{
@@ -1,7 +1,8 @@
1
- import { C as workflowTextHash, D as openStore, Mn as LESSON_CAP_TOKENS, O as storePath, R as driverRefusals, Rt as loadProject, S as readProjectTriggers, Tn as placeFor, _ as lateTriggers, _r as EXPERIMENTS, dr as readyDriver, gr as firstLine, hr as DRIVER_IDS, ht as walkSummary, ir as retiredDriverHint, kn as workflowDirOf, lt as namingNoDriver, mr as AGENT_DRIVERS, mt as walkChecks, nr as DriverSchema, on as socketRoom, or as INTEGRATIONS, pt as checkWorkflowText, st as explainSession, ur as driverChecks, v as triggersReport, vr as experimentsOn, wn as placeFiles, yr as knownExperiments, z as driverQueues } from "./src-Df6KOIDC.mjs";
2
- import { c as checkDue, d as pendingNotice, f as readUpdateCache, h as writeUpdateCache, i as registryOf, m as updateCachePath, n as ORK_VERSION, o as updateSwitchOff, p as refreshUpdateCache, r as isSwitchedOn, t as ORK_NAME, u as newerVersion } from "./__build_info__-DFF18K8y.mjs";
3
- import { a as write, i as style, o as writeErr, r as plain } from "./output-Bqljtmp_.mjs";
4
- import { c as registeredProjects, i as refusedBuiltin, o as workflowArg, r as findWorkflowFiles, s as projectAt } from "./workflows-BT1gDRIV.mjs";
1
+ import { Ar as AGENT_DRIVERS, Bn as placeFiles, C as openStore, Dr as readyDriver, Er as driverChecks, F as driverQueues, Fr as knownExperiments, Gn as workflowDirOf, Jn as LESSON_CAP_TOKENS, Mr as firstLine, Nr as EXPERIMENTS, P as driverRefusals, Pr as experimentsOn, Sn as socketRoom, Sr as INTEGRATIONS, Vn as placeFor, _ as readProjectTriggers, br as retiredDriverHint, ct as explainSession, f as lateTriggers, gt as walkSummary, ht as walkChecks, jr as DRIVER_IDS, mt as checkWorkflowText, p as triggersReport, qt as loadProject, ut as namingNoDriver, v as workflowTextHash, vr as DriverSchema, w as storePath } from "./src-CdDTQLdQ.mjs";
2
+ import { c as checkDue, d as pendingNotice, f as readUpdateCache, h as writeUpdateCache, i as registryOf, m as updateCachePath, n as ORK_VERSION, o as updateSwitchOff, p as refreshUpdateCache, r as isSwitchedOn, t as ORK_NAME, u as newerVersion } from "./__build_info__-BJbE3Zvp.mjs";
3
+ import { S as cut, T as wrap, a as style, i as plain, o as write, p as paint, s as writeErr, w as widthOf, x as termOf, y as plainEnv } from "./output-CnRzrbUM.mjs";
4
+ import { n as chip, t as CHIP_LABELS } from "./chips-CyeB_rhR.mjs";
5
+ import { c as registeredProjects, i as refusedBuiltin, o as workflowArg, r as findWorkflowFiles, s as projectAt } from "./workflows-Rr5w90A0.mjs";
5
6
  import path from "node:path";
6
7
  import { readFileSync } from "node:fs";
7
8
  import { execFile, spawn } from "node:child_process";
@@ -73,7 +74,7 @@ const showUpdateNotice = (file = updateCachePath(), now = /* @__PURE__ */ new Da
73
74
  const latest = pendingNotice(cache, ORK_VERSION, now);
74
75
  if (cache === null || latest === null) return;
75
76
  const text = updateNotice(latest, ORK_VERSION);
76
- process.stderr.write(`\n${styleText("yellow", text, { stream: process.stderr })}\n`);
77
+ process.stderr.write(`\n${plainEnv() ? text : styleText("yellow", text, { stream: process.stderr })}\n`);
77
78
  writeUpdateCache(file, {
78
79
  ...cache,
79
80
  notified: {
@@ -417,6 +418,41 @@ const pathLines = (walk) => {
417
418
  const endings = walk?.endings ?? [];
418
419
  return [...endings.filter(({ status }) => status === "succeeded"), ...endings.filter(({ status }) => status !== "succeeded")].flatMap((ending) => endingLines(ending));
419
420
  };
421
+ // src/commands/validate-report.ts
422
+ const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
423
+ const diagnosticLine = (file, d, term) => {
424
+ const where = d.line === void 0 ? file : `${file}:${d.line}:${d.column ?? 1}`;
425
+ if (term.rich) return [
426
+ `${d.severity === "error" ? chip(term, CHIP_LABELS.error, "danger") : chip(term, CHIP_LABELS.warn, "warn")} ${paint(term, "muted", cut(where, term.width - 9))}`,
427
+ ...wrap(d.message, term.width - 2).map((line) => ` ${line}`),
428
+ ` ${paint(term, "muted", `(${d.code})`)}`
429
+ ];
430
+ const severity = d.severity === "error" ? style(["red", "bold"], "error") : style(["yellow", "bold"], "warning");
431
+ return [`${style("dim", where)} ${severity} ${d.message} ${style("dim", `(${d.code})`)}`];
432
+ };
433
+ /** The warnings alone, for a run that goes ahead despite them. */
434
+ const warningLines = (report, term = termOf(process.stderr)) => report.diagnostics.filter((d) => d.severity === "warning").flatMap((d) => diagnosticLine(report.file, d, term));
435
+ /** The verdict line: `OK` or `FAIL` chip when rich, else `✓` or `✗`. */
436
+ const verdictLine = (term, ok, file, note) => {
437
+ const mark = term.rich ? chip(term, ok ? CHIP_LABELS.ok : CHIP_LABELS.fail, ok ? "ok" : "danger") : style(ok ? "green" : "red", ok ? "✓" : "✗");
438
+ const room = term.rich ? Math.max(8, term.width - widthOf(file) - 8) : Infinity;
439
+ return `${mark} ${file}${note === "" ? "" : ` ${style("dim", cut(note, room))}`}`;
440
+ };
441
+ /** Human-readable report, one line per diagnostic, compiler style. */
442
+ const formatReport = (report, showsPaths = false, term = termOf(process.stdout)) => {
443
+ const lines = report.diagnostics.flatMap((d) => diagnosticLine(report.file, d, term));
444
+ const errors = report.diagnostics.filter((d) => d.severity === "error").length;
445
+ const warnings = report.diagnostics.length - errors;
446
+ if (report.summary === void 0 && errors === 0) lines.push(verdictLine(term, true, report.file, warnings === 0 ? "" : `(${plural(warnings, "warning")})`));
447
+ else if (report.summary !== void 0 && errors === 0) {
448
+ const { nodes, edges, entries } = report.summary;
449
+ const extra = warnings > 0 ? `, ${plural(warnings, "warning")}` : "";
450
+ lines.push(verdictLine(term, true, report.file, `(${plural(nodes, "node")}, ${plural(edges, "edge")}, starts at ${entries.join(", ")}${extra})`));
451
+ } else lines.push(verdictLine(term, false, report.file, `(${plural(errors, "error")}, ${plural(warnings, "warning")})`));
452
+ return [...lines, ...walkLine(report.walk, showsPaths)];
453
+ };
454
+ /** A report's lines, and with `showsPaths` each path the walk found. */
455
+ const reportLines = (report, showsPaths) => [...formatReport(report, showsPaths), ...showsPaths ? pathLines(report.walk) : []];
420
456
  // src/commands/validate.ts
421
457
  /**
422
458
  * @file `ork validate`: loads, parses and compiles workflow files with the
@@ -494,27 +530,6 @@ const walkLoaded = async (loaded) => {
494
530
  }
495
531
  };
496
532
  };
497
- const plural = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
498
- const diagnosticLine = (file, d) => {
499
- const where = d.line === void 0 ? file : `${file}:${d.line}:${d.column ?? 1}`;
500
- const severity = d.severity === "error" ? style(["red", "bold"], "error") : style(["yellow", "bold"], "warning");
501
- return `${style("dim", where)} ${severity} ${d.message} ${style("dim", `(${d.code})`)}`;
502
- };
503
- /** The warnings alone, for a run that goes ahead despite them. */
504
- const warningLines = (report) => report.diagnostics.filter((d) => d.severity === "warning").map((d) => diagnosticLine(report.file, d));
505
- /** Human-readable report, one line per diagnostic, compiler style. */
506
- const formatReport = (report, showsPaths = false) => {
507
- const lines = report.diagnostics.map((d) => diagnosticLine(report.file, d));
508
- const errors = report.diagnostics.filter((d) => d.severity === "error").length;
509
- const warnings = report.diagnostics.length - errors;
510
- if (report.summary === void 0 && errors === 0) lines.push(`${style("green", "✓")} ${report.file}${warnings === 0 ? "" : style("dim", ` (${plural(warnings, "warning")})`)}`);
511
- else if (report.summary !== void 0 && errors === 0) {
512
- const { nodes, edges, entries } = report.summary;
513
- const extra = warnings > 0 ? `, ${plural(warnings, "warning")}` : "";
514
- lines.push(`${style("green", "✓")} ${report.file} ${style("dim", `(${plural(nodes, "node")}, ${plural(edges, "edge")}, starts at ${entries.join(", ")}${extra})`)}`);
515
- } else lines.push(`${style("red", "✗")} ${report.file} ${style("dim", `(${plural(errors, "error")}, ${plural(warnings, "warning")})`)}`);
516
- return [...lines, ...walkLine(report.walk, showsPaths)];
517
- };
518
533
  const validate = defineCommand({
519
534
  meta: {
520
535
  name: "validate",
@@ -573,10 +588,7 @@ const validate = defineCommand({
573
588
  diagnostics: [...triggers.diagnostics, ...triggers.triggers.flatMap((item) => item.diagnostics)]
574
589
  }] : []];
575
590
  if (args.json) write(JSON.stringify(reports, void 0, 2));
576
- else for (const report of reports) {
577
- for (const line of formatReport(report, args.paths)) write(line);
578
- if (args.paths) for (const line of pathLines(report.walk)) write(line);
579
- }
591
+ else for (const report of reports) for (const line of reportLines(report, args.paths)) write(line);
580
592
  if (reports.some((r) => hasErrors(r.diagnostics))) process.exitCode = 1;
581
593
  }
582
594
  });
@@ -680,4 +692,4 @@ const context = defineCommand({
680
692
  },
681
693
  subCommands: { explain }
682
694
  });
683
- export { startUpdateCheck as C, showUpdateNotice as S, runChecks as _, validate as a, UPDATE_CHILD_ENV as b, warningLines as c, runReadyDriver as d, unfitLines as f, orkCheck as g, headWarning as h, loadWorkflow as i, checksHere as l, experimentsCheck as m, explainNode as n, validateFile as o, exec as p, formatReport as r, walkLoaded as s, context as t, chooseReadyDriver as u, socketCheck as v, updateCheckOff as w, runUpdateChild as x, triggerChecks as y };
695
+ export { startUpdateCheck as C, showUpdateNotice as S, runChecks as _, validateFile as a, UPDATE_CHILD_ENV as b, warningLines as c, runReadyDriver as d, unfitLines as f, orkCheck as g, headWarning as h, validate as i, checksHere as l, experimentsCheck as m, explainNode as n, walkLoaded as o, exec as p, loadWorkflow as r, formatReport as s, context as t, chooseReadyDriver as u, socketCheck as v, updateCheckOff as w, runUpdateChild as x, triggerChecks as y };
@@ -0,0 +1,50 @@
1
+ import { Cn as wakeRun, xn as socketPath, xt as liveStatus } from "./src-CdDTQLdQ.mjs";
2
+ // ../core/src/remote/decide.ts
3
+ /**
4
+ * @file The open decisions of live runs, and answering one from another process
5
+ * (`ork decide`, the local server), with the person's note if they gave one.
6
+ * An answer goes to the store (the first one wins), then the run is woken on
7
+ * its socket so it picks the answer up at once (specs/messaging.md).
8
+ */
9
+ /** Open decisions whose run is still live, oldest first, ANDing the filter. */
10
+ const pendingDecisions = (store, filter) => {
11
+ const live = /* @__PURE__ */ new Map();
12
+ return store.decisions({
13
+ ...filter,
14
+ status: "open"
15
+ }).filter((decision) => {
16
+ if (!live.has(decision.runId)) {
17
+ const [run] = store.lookup(decision.runId);
18
+ live.set(decision.runId, run !== void 0 && liveStatus(run) === "running" && (filter.workflow === void 0 || run.workflow === filter.workflow));
19
+ }
20
+ return live.get(decision.runId) === true;
21
+ });
22
+ };
23
+ /**
24
+ * Answers an open decision with one of its options, and wakes its run. `home`
25
+ * is `~`, where the sockets are (tests pass a folder); `by` says who answered;
26
+ * `note` is what they said with it, if anything (blank is nothing).
27
+ */
28
+ const answerDecision = async (store, id, choice, home, by = "decide", note) => {
29
+ const decision = store.decision(id);
30
+ if (decision === void 0) return {
31
+ ok: false,
32
+ error: `No decision \`${id}\`.`
33
+ };
34
+ if (!decision.options.some((o) => o.id === choice)) return {
35
+ ok: false,
36
+ error: `\`${choice}\` is not an option of \`${id}\`: \`${decision.options.map((o) => o.id).join("`, `")}\``
37
+ };
38
+ if (!store.answerDecision(id, choice, by, note)) {
39
+ const now = store.decision(id);
40
+ return {
41
+ ok: false,
42
+ error: now?.status === "answered" ? `${id} was already answered: ${now.answer ?? ""} (${now.answeredBy ?? ""})` : `${id} was withdrawn`
43
+ };
44
+ }
45
+ return {
46
+ ok: true,
47
+ woke: await wakeRun(socketPath(decision.runId, home))
48
+ };
49
+ };
50
+ export { pendingDecisions as n, answerDecision as t };
@@ -1,4 +1,4 @@
1
- import { Tn as placeFor } from "./src-Df6KOIDC.mjs";
1
+ import { Vn as placeFor } from "./src-CdDTQLdQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  // ../core/src/remote/find.ts
4
4
  /**
@@ -0,0 +1,206 @@
1
+ import { Ct as runStatus, Tt as slotWaitsOf, kt as isItem, xt as liveStatus } from "./src-CdDTQLdQ.mjs";
2
+ import { S as cut, a as style, n as cost, w as widthOf, x as termOf } from "./output-CnRzrbUM.mjs";
3
+ import { i as statusChip, n as chip } from "./chips-CyeB_rhR.mjs";
4
+ import { n as nodeVerdict } from "./verdict-CIy8tAyX.mjs";
5
+ // src/term/table.ts
6
+ /**
7
+ * @file Aligned columns for the listing commands: each column as wide as its
8
+ * widest cell, a two-space gap, the last column left unpadded. Rich, a cell
9
+ * may be a chip and a table wider than the screen cuts its flexible columns
10
+ * with `…`; plain, nothing is cut or styled, as the listings always printed.
11
+ */
12
+ /** A dimmed header cell. */
13
+ const header = (text) => ({
14
+ text,
15
+ look: (s) => style("dim", s)
16
+ });
17
+ const GAP = " ";
18
+ /** A flexible column is never cut under this many columns. */
19
+ const MIN_FLEX = 8;
20
+ /** The text a cell shows, and its columns. */
21
+ const shown = (term, cell) => term.rich && cell.chip !== void 0 ? cell.chip.label : cell.text;
22
+ const widthOfCell = (term, cell) => widthOf(shown(term, cell)) + (term.rich && cell.chip !== void 0 ? 2 : 0);
23
+ /** The lines of a table, one per row (a header is a row the caller passes). */
24
+ const table = (term, columns, rows, indent = "") => {
25
+ const widths = columns.map((_, c) => Math.max(0, ...rows.map((row) => widthOfCell(term, row[c] ?? { text: "" }))));
26
+ if (term.rich) {
27
+ const gaps = 2 * (columns.length - 1);
28
+ let over = indent.length + widths.reduce((a, b) => a + b, 0) + gaps - term.width;
29
+ columns.forEach((column, c) => {
30
+ const width = widths[c] ?? 0;
31
+ if (over <= 0 || column.flex !== true || width <= MIN_FLEX) return;
32
+ const next = Math.max(MIN_FLEX, width - over);
33
+ over -= width - next;
34
+ widths[c] = next;
35
+ });
36
+ }
37
+ const last = columns.length - 1;
38
+ return rows.map((row) => indent + columns.map((column, c) => {
39
+ const cell = row[c] ?? { text: "" };
40
+ const width = widths[c] ?? 0;
41
+ const look = cell.look ?? ((s) => s);
42
+ if (term.rich && cell.chip !== void 0) {
43
+ const used = widthOfCell(term, cell);
44
+ const pad = " ".repeat(Math.max(0, width - used));
45
+ return column.align === "right" ? pad + chip(term, cell.chip.label, cell.chip.role) : chip(term, cell.chip.label, cell.chip.role) + (c === last ? "" : pad);
46
+ }
47
+ const text = cut(shown(term, cell), width);
48
+ const pad = " ".repeat(Math.max(0, width - widthOf(text)));
49
+ if (column.align === "right") return look(pad + text);
50
+ return c === last ? look(text) : look(text + pad);
51
+ }).join(GAP));
52
+ };
53
+ // src/runs/format.ts
54
+ /**
55
+ * @file How stored runs read on the terminal: the `ork ps` table and the `ork
56
+ * tree` ownership tree with each node's state, tokens and cost, and what is
57
+ * paused (ADR-041).
58
+ */
59
+ const STATUS_STYLE = {
60
+ running: "cyan",
61
+ paused: "yellow",
62
+ succeeded: "green",
63
+ failed: "red",
64
+ crashed: "red",
65
+ budget_exhausted: "yellow",
66
+ quota_exhausted: "yellow",
67
+ timed_out: "yellow",
68
+ interrupted: "yellow"
69
+ };
70
+ /** `just now`, `5m ago`, `3h ago`, `2d ago`, then the date. */
71
+ const ago = (iso, now = Date.now()) => {
72
+ const minutes = Math.floor((now - Date.parse(iso)) / 6e4);
73
+ if (minutes < 1) return "just now";
74
+ if (minutes < 60) return `${String(minutes)}m ago`;
75
+ if (minutes < 1440) return `${String(Math.floor(minutes / 60))}h ago`;
76
+ if (minutes < 10080) return `${String(Math.floor(minutes / 1440))}d ago`;
77
+ return iso.slice(0, 10);
78
+ };
79
+ /** `42s`, `3m12s`, `1h05m`. */
80
+ const duration = (ms) => {
81
+ const s = Math.max(0, Math.round(ms / 1e3));
82
+ if (s < 60) return `${String(s)}s`;
83
+ if (s < 3600) return `${String(Math.floor(s / 60))}m${String(s % 60).padStart(2, "0")}s`;
84
+ return `${String(Math.floor(s / 3600))}h${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m`;
85
+ };
86
+ const dim = (s) => style("dim", s);
87
+ /**
88
+ * A run's status as `ork ps` reads it: `waiting for slot` for a running run
89
+ * with a command waiting for a machine-wide slot; `events` are a running
90
+ * run's.
91
+ */
92
+ const listedStatus = (run, events) => {
93
+ const status = runStatus(run, events);
94
+ return status === "running" && slotWaitsOf(events).length > 0 ? "waiting for slot" : status;
95
+ };
96
+ /**
97
+ * The `ork ps` table; `withProject` adds each run's project. A called run's
98
+ * cost is dimmed and says its caller's, whose cost already counts it.
99
+ */
100
+ const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus, term = termOf(process.stdout)) => {
101
+ if (runs.length === 0) return ["No runs."];
102
+ const rows = runs.map((run) => {
103
+ const status = statusOf(run);
104
+ const end = run.finishedAt === void 0 ? now : Date.parse(run.finishedAt);
105
+ return {
106
+ id: run.id,
107
+ workflow: run.workflow,
108
+ status,
109
+ when: `${ago(run.startedAt, now)} · ${duration(end - Date.parse(run.startedAt))}`,
110
+ cost: run.parent === void 0 ? cost(run.costUsd) : `${cost(run.costUsd)} in ${run.parent}`,
111
+ called: run.parent !== void 0,
112
+ project: run.project
113
+ };
114
+ });
115
+ const rowOf = (r) => {
116
+ const cells = [
117
+ {
118
+ text: r.id,
119
+ look: (s) => style("bold", s)
120
+ },
121
+ { text: r.workflow },
122
+ {
123
+ text: r.status,
124
+ chip: statusChip(r.status),
125
+ look: (s) => style(STATUS_STYLE[r.status] ?? "reset", s)
126
+ },
127
+ {
128
+ text: r.when,
129
+ look: dim
130
+ },
131
+ r.called ? {
132
+ text: r.cost,
133
+ look: dim
134
+ } : { text: r.cost }
135
+ ];
136
+ if (withProject) cells.push({
137
+ text: r.project,
138
+ look: dim
139
+ });
140
+ return cells;
141
+ };
142
+ const heads = [
143
+ "RUN",
144
+ "WORKFLOW",
145
+ "STATUS",
146
+ "STARTED",
147
+ "COST"
148
+ ].map((text) => header(text));
149
+ if (withProject) heads.push(header("PROJECT"));
150
+ const columns = [
151
+ {},
152
+ { flex: true },
153
+ {},
154
+ {},
155
+ { align: "right" }
156
+ ];
157
+ if (withProject) columns.push({ flex: true });
158
+ return table(term, columns, [heads, ...rows.map((row) => rowOf(row))]);
159
+ };
160
+ /** Marks are made when used, so `--plain` (set after load) is read. */
161
+ const STATE_MARK = {
162
+ waiting: () => style("dim", "·"),
163
+ running: () => style("cyan", "●"),
164
+ ok: () => style("green", "✓"),
165
+ failed: () => style("red", "✗"),
166
+ skipped: () => style("dim", "-")
167
+ };
168
+ /** `950`, `12.3k`, `1.2M`. */
169
+ const count = (n) => {
170
+ if (n < 1e3) return String(n);
171
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
172
+ return `${(n / 1e6).toFixed(1)}M`;
173
+ };
174
+ const stateText = (node) => {
175
+ if (node.state === "running") return node.runs > 1 ? `running (round ${String(node.runs)})` : "running";
176
+ if (node.state === "ok" || node.state === "failed") {
177
+ const verdict = nodeVerdict(node.output ?? {}, node.state === "ok");
178
+ const line = verdict.length > 60 ? `${verdict.slice(0, 59)}…` : verdict;
179
+ return node.runs > 1 ? `${line} (${String(node.runs)} runs)` : line;
180
+ }
181
+ return node.state;
182
+ };
183
+ /** The ownership tree, one line per node, children under their owner. */
184
+ const formatTree = (roots, now = Date.now()) => {
185
+ const lines = [];
186
+ const walk = (node, lead, branch) => {
187
+ lines.push({
188
+ label: `${lead}${branch}${node.id}`,
189
+ node
190
+ });
191
+ const next = lead + (branch === "├─ " ? "│ " : branch === "" ? "" : " ");
192
+ node.children.forEach((child, index) => {
193
+ walk(child, next, index === node.children.length - 1 ? "└─ " : "├─ ");
194
+ });
195
+ };
196
+ for (const root of roots) walk(root, "", "");
197
+ const label = Math.max(...lines.map((l) => l.label.length));
198
+ const type = Math.max(...lines.map((l) => l.node.type.length));
199
+ return lines.map(({ label: text, node }) => {
200
+ const usage = node.inputTokens + node.outputTokens === 0 ? "" : style("dim", ` ${count(node.inputTokens)} in / ${count(node.outputTokens)} out · ${cost(node.costUsd)}`);
201
+ const state = node.held === true ? `${style("yellow", "‖")} paused` : `${STATE_MARK[node.state]()} ${stateText(node)}`;
202
+ const spent = isItem(node) && node.startedAt !== void 0 ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
203
+ return `${text.padEnd(label)} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
204
+ });
205
+ };
206
+ export { header as a, listedStatus as i, formatRuns as n, table as o, formatTree as r, ago as t };
@@ -220,6 +220,10 @@ const HELP_GROUPS = [
220
220
  name: "logs",
221
221
  short: "Print a run's transcript"
222
222
  },
223
+ {
224
+ name: "attach",
225
+ short: "Follow a run here and answer its questions"
226
+ },
223
227
  {
224
228
  name: "tree",
225
229
  short: "Show a run's tree, tokens and cost"
@@ -1,4 +1,4 @@
1
- import { p as runMarkerIn } from "./src-cmmXKHn1.mjs";
1
+ import { p as runMarkerIn } from "./src-BfxkJOYm.mjs";
2
2
  // src/commands/in-a-run.ts
3
3
  /**
4
4
  * @file The commands that act as the user refuse a program or an agent in a run
@@ -19,4 +19,6 @@ const decideRefusal = (env = process.env) => refusal("`ork decide` answers as yo
19
19
  const continueRefusal = (env = process.env) => refusal("`ork continue` undoes a pause you set, so a program or an agent in a run cannot use it.", env);
20
20
  /** Whether `ork ui` may print its sign-in link, which acts as the user. */
21
21
  const mayShowSignIn = (env = process.env) => runMarkerIn(env) === null;
22
- export { mayShowSignIn as a, decideRefusal as i, chatRefusal as n, continueRefusal as r, acceptRefusal as t };
22
+ /** Why `ork attach` will not answer here, or `null` when it may. */
23
+ const attachRefusal = (env = process.env) => refusal("`ork attach` answers as you, so a program or an agent in a run cannot use it; `ork logs` still shows the run.", env);
24
+ export { decideRefusal as a, continueRefusal as i, attachRefusal as n, mayShowSignIn as o, chatRefusal as r, acceptRefusal as t };
@@ -1,4 +1,4 @@
1
- import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-Df6KOIDC.mjs";
1
+ import { it as limitsReport, nt as NOT_STARTED, r as capsHere, rt as limitsNow } from "./src-CdDTQLdQ.mjs";
2
2
  // src/run/limits.ts
3
3
  /**
4
4
  * @file The limits across runs that apply here (RFC-007): the user's settings
@@ -1,4 +1,4 @@
1
- import { M as git, N as gitOk, P as lines } from "./src-Df6KOIDC.mjs";
1
+ import { A as lines, O as git, k as gitOk } from "./src-CdDTQLdQ.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  // ../core/src/worktree/list.ts
4
4
  /**
@@ -1,8 +1,8 @@
1
- import { h as isNewer } from "./src-Df6KOIDC.mjs";
2
- import { n as ORK_VERSION } from "./__build_info__-DFF18K8y.mjs";
3
- import { p as runMarkerIn } from "./src-cmmXKHn1.mjs";
4
- import { n as explainNode } from "./context-CEaKOVmG.mjs";
5
- import { a as openMcp, c as refused, o as tool, s as answer } from "./src-i5k2Y-kf.mjs";
1
+ import { u as isNewer } from "./src-CdDTQLdQ.mjs";
2
+ import { n as ORK_VERSION } from "./__build_info__-BJbE3Zvp.mjs";
3
+ import { p as runMarkerIn } from "./src-BfxkJOYm.mjs";
4
+ import { n as explainNode } from "./context-BAwgVSX5.mjs";
5
+ import { a as openMcp, c as refused, o as tool, s as answer } from "./src-S9yK2OHU.mjs";
6
6
  import { z } from "zod";
7
7
  import { defineCommand } from "citty";
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -43,7 +43,7 @@ const orkMcpServer = (options = {}) => {
43
43
  ...options.script === void 0 ? {} : { script: options.script }
44
44
  });
45
45
  const all = [...opened.tools, explainTool(opened, options.home)];
46
- const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.10") ? tooOld(all, options.expects) : all;
46
+ const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.12") ? tooOld(all, options.expects) : all;
47
47
  const server = new McpServer({
48
48
  name: "ork",
49
49
  version: ORK_VERSION