@krapht/ork 1.0.0-alpha.2 → 1.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/__build_info__-0wqQ-rtf.mjs +180 -0
- package/dist/apply-Mb0fNpCX.mjs +17 -0
- package/dist/argument-error-CihH46Le.mjs +27 -0
- package/dist/attach-C0-YyNoY.mjs +92 -0
- package/dist/attach-CAKNqnUK.mjs +1259 -0
- package/dist/bin.mjs +1885 -1609
- package/dist/brief-C2hew8cS.mjs +116 -0
- package/dist/{decide-CGyx9rv7.mjs → builtin-files-YYAnbZGD.mjs} +3 -51
- package/dist/chips-CP_pgKlj.mjs +48 -0
- package/dist/command-tree-CEkzWNy6.mjs +63 -0
- package/dist/{completion-CSU5vkEt.mjs → completion-K4rAVsrf.mjs} +200 -30
- package/dist/context-B-fjPJky.mjs +106 -0
- package/dist/context-DMK0U1w0.mjs +160 -0
- package/dist/detached-q0AYG0Bs.mjs +39 -0
- package/dist/extra-arguments-BxQMXARO.mjs +13 -0
- package/dist/{find-D6Gj1sxg.mjs → find-Ct5CNl1J.mjs} +2 -2
- package/dist/flags-C90N4J1i.mjs +145 -0
- package/dist/follow-ups-D3HPyNYM.mjs +568 -0
- package/dist/format-CiPhQqbX.mjs +260 -0
- package/dist/{help-groups-BwYoAWsh.mjs → help-groups-BQvcKQ9H.mjs} +49 -78
- package/dist/in-a-run-D_wg4iJi.mjs +27 -0
- package/dist/install-Hji6LIei.mjs +259 -0
- package/dist/{limits-B1-DYc3d.mjs → limits-BD6RZRCS.mjs} +2 -2
- package/dist/{mcp-DE_EmyVm.mjs → mcp-CpbDmBLG.mjs} +6 -6
- package/dist/once-J8Qn5fTG.mjs +112 -0
- package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
- package/dist/output-DXZbnFt_.mjs +458 -0
- package/dist/outputs-text-CNTJ7Qq_.mjs +19 -0
- package/dist/{pause-CL6N_Xrj.mjs → pause-Cl4ZYA35.mjs} +7 -4
- package/dist/print-DzugGZQB.mjs +19 -0
- package/dist/project-records-CtkmlsHN.mjs +233 -0
- package/dist/refusals-DQQX3AIb.mjs +116 -0
- package/dist/{relay-0SD0erwf.mjs → relay-CiP77vga.mjs} +2 -2
- package/dist/run-CtuNY3mE.mjs +234 -0
- package/dist/send-DRYGks8K.mjs +104 -0
- package/dist/short-flags-DkJLWVqh.mjs +1 -0
- package/dist/short-flags-Qf21as0E.mjs +83 -0
- package/dist/src-BMFQKHz4.mjs +3122 -0
- package/dist/{src-BbyEoMn4.mjs → src-BrFfDKux.mjs} +800 -235
- package/dist/{src-Dujmvv4s.mjs → src-BuF9Lbow.mjs} +12580 -5505
- package/dist/{src-DOcK8Jfp.mjs → src-k0A_QpST.mjs} +1024 -590
- package/dist/store-rTgRu8xy.mjs +25 -0
- package/dist/ui/THIRD_PARTY_LICENSES.txt +612 -0
- package/dist/ui/assets/app-BYxpKJRh.css +1 -0
- package/dist/ui/assets/app-SEFEJLkF.js +112 -0
- package/dist/ui/assets/{index-9dFha9Qn.js → index-Cb6Voq_W.js} +3 -3
- package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/{ui-CTmO2Xzu.mjs → ui-C2f37TSb.mjs} +57 -25
- package/dist/usage-BuvGcQSk.mjs +256 -0
- package/dist/validate-ObP5lt29.mjs +1 -0
- package/dist/validate-_HjACtk9.mjs +632 -0
- package/dist/verdict-B592j1W3.mjs +84 -0
- package/dist/{watch-CtbgDAcz.mjs → watch-DV3ixCYj.mjs} +45 -17
- package/dist/{workflows-D1_WVukk.mjs → workflows-QLjBhGNS.mjs} +30 -5
- package/dist/worktrees-D3ubG_Qw.mjs +302 -0
- package/dist/yolo-BSH1E_kE.mjs +33 -0
- package/package.json +10 -8
- package/dist/__build_info__-fy6L1Mrd.mjs +0 -615
- package/dist/clean-DaHjs__2.mjs +0 -88
- package/dist/context-DT8MfEIR.mjs +0 -274
- package/dist/format-Dnfv8zMT.mjs +0 -427
- package/dist/in-a-run-pLAZeNys.mjs +0 -22
- package/dist/list-BrH8KRSJ.mjs +0 -110
- package/dist/output-BLQWD6MJ.mjs +0 -950
- package/dist/remove-BeGdqroy.mjs +0 -194
- package/dist/report-DK4LGqwF.mjs +0 -129
- package/dist/send-C8tunWhv.mjs +0 -163
- package/dist/src-CCUKRX51.mjs +0 -1204
- package/dist/ui/assets/app-BNT0SuIz.css +0 -1
- package/dist/ui/assets/app-T7wVAhgx.js +0 -67
- package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
- package/dist/usage-DYUmP0i4.mjs +0 -419
- package/dist/worktrees-DdMYoNOC.mjs +0 -212
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { bt as explainSession, ei as LESSON_CAP_TOKENS } from "./src-BuF9Lbow.mjs";
|
|
2
|
+
import { c as write, s as style } from "./output-DXZbnFt_.mjs";
|
|
3
|
+
import { a as refusedBuiltin, s as workflowArg } from "./workflows-QLjBhGNS.mjs";
|
|
4
|
+
import { a as formatReport, t as loadWorkflow } from "./validate-_HjACtk9.mjs";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
// src/commands/context.ts
|
|
7
|
+
/**
|
|
8
|
+
* @file `ork context explain <wf> <node>`: what one node's session would load,
|
|
9
|
+
* layer by layer, as core's `explainSession` plans it without writing
|
|
10
|
+
* anything (one of Ork's own `ork:` workflows is written into the project's
|
|
11
|
+
* run folder first, as `ork run` does): its project instructions, every skill
|
|
12
|
+
* with the layer it came from and whether it is live, shadowed or switched
|
|
13
|
+
* off, its context files, its practices, and estimated tokens.
|
|
14
|
+
*/
|
|
15
|
+
/** Loads a workflow file and plans one node's session in it, in place. */
|
|
16
|
+
const explainNode = (file, nodeId, cwd, home) => {
|
|
17
|
+
const { report, workflow, graph, place, workflowDir } = loadWorkflow(file, cwd, home);
|
|
18
|
+
if (workflow === null) return {
|
|
19
|
+
ok: false,
|
|
20
|
+
lines: formatReport(report)
|
|
21
|
+
};
|
|
22
|
+
const result = explainSession(workflow, graph, nodeId, {
|
|
23
|
+
root: place.root,
|
|
24
|
+
orkDir: place.dir,
|
|
25
|
+
workflowDir,
|
|
26
|
+
...home === void 0 ? {} : { home }
|
|
27
|
+
});
|
|
28
|
+
return result.ok ? result : {
|
|
29
|
+
ok: false,
|
|
30
|
+
lines: [result.error]
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const approx = (count) => style("dim", `≈${count.toLocaleString("en")} tokens`);
|
|
34
|
+
const formatExplanation = (e) => {
|
|
35
|
+
const width = Math.max(8, ...e.skills.map((s) => s.name.length));
|
|
36
|
+
const sourceWidth = Math.max(0, ...e.skills.map((s) => s.source.length));
|
|
37
|
+
const lines = [
|
|
38
|
+
`${style("bold", e.node)} ${style("dim", `(${e.type}) in ${e.workflow}`)}${e.template === void 0 ? "" : style("dim", `, planned as ${e.template}, the node it was spawned from`)}`,
|
|
39
|
+
"",
|
|
40
|
+
...e.agent === void 0 ? [] : [`${style("bold", "Agent")} ${e.agent.name} ${style("dim", `(${e.agent.where})`)} ${approx(e.agent.tokens)}`],
|
|
41
|
+
`${style("bold", "Instructions")} ${e.instructions}`,
|
|
42
|
+
"",
|
|
43
|
+
style("bold", "Skills")
|
|
44
|
+
];
|
|
45
|
+
if (e.skills.length === 0) lines.push(style("dim", " none"));
|
|
46
|
+
for (const s of e.skills) {
|
|
47
|
+
const mark = s.state === "live" ? style("green", "✓") : style("dim", "−");
|
|
48
|
+
const state = s.why === void 0 ? approx(s.tokens).replace("tokens", "tokens when used") : style("yellow", `off: ${s.why}`);
|
|
49
|
+
lines.push(` ${mark} ${s.name.padEnd(width)} ${s.layer.padEnd(8)} ${style("dim", s.source.padEnd(sourceWidth))} ${state}`);
|
|
50
|
+
}
|
|
51
|
+
lines.push("", style("bold", "Context files"));
|
|
52
|
+
if (e.files.length === 0) lines.push(style("dim", " none"));
|
|
53
|
+
for (const f of e.files) {
|
|
54
|
+
const cut = f.cutFrom === void 0 ? "" : ` ${style("yellow", `cut at the ${LESSON_CAP_TOKENS.toLocaleString("en")}-token cap; the whole file is ${approx(f.cutFrom)}`)}`;
|
|
55
|
+
lines.push(` ${f.layer.padEnd(8)} ${f.name} ${approx(f.tokens)}${cut}`);
|
|
56
|
+
}
|
|
57
|
+
lines.push("", style("bold", "Practices"));
|
|
58
|
+
for (const p of e.practices) lines.push(` ${p.name.padEnd(8)} ${approx(p.tokens)}`);
|
|
59
|
+
for (const name of e.skippedPractices) lines.push(` ${name.padEnd(8)} ${style("dim", "the project's own skill wins")}`);
|
|
60
|
+
if (e.practices.length === 0 && e.skippedPractices.length === 0) lines.push(style("dim", " off"));
|
|
61
|
+
lines.push("", `Appended to Claude Code's system prompt: ${approx(e.appendedTokens)}`);
|
|
62
|
+
return lines;
|
|
63
|
+
};
|
|
64
|
+
const explain = defineCommand({
|
|
65
|
+
meta: {
|
|
66
|
+
name: "explain",
|
|
67
|
+
description: "Show what one node's session loads, layer by layer"
|
|
68
|
+
},
|
|
69
|
+
args: {
|
|
70
|
+
workflow: {
|
|
71
|
+
type: "positional",
|
|
72
|
+
description: "A workflow file, the name of one in the project's workflows (.ork/workflows/), or one of Ork's own (ork:feedback-apply)",
|
|
73
|
+
required: true
|
|
74
|
+
},
|
|
75
|
+
node: {
|
|
76
|
+
type: "positional",
|
|
77
|
+
description: "Node id",
|
|
78
|
+
required: true
|
|
79
|
+
},
|
|
80
|
+
json: {
|
|
81
|
+
type: "boolean",
|
|
82
|
+
description: "Print JSON",
|
|
83
|
+
default: false
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
run: ({ args }) => {
|
|
87
|
+
const cwd = process.cwd();
|
|
88
|
+
if (refusedBuiltin([args.workflow], cwd)) return;
|
|
89
|
+
const result = explainNode(workflowArg(args.workflow, cwd).file, args.node, cwd);
|
|
90
|
+
if (!result.ok) {
|
|
91
|
+
for (const line of result.lines) process.stderr.write(`${line}\n`);
|
|
92
|
+
process.exitCode = 1;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (args.json) write(JSON.stringify(result.explanation, void 0, 2));
|
|
96
|
+
else for (const line of formatExplanation(result.explanation)) write(line);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const context = defineCommand({
|
|
100
|
+
meta: {
|
|
101
|
+
name: "context",
|
|
102
|
+
description: "Inspect the context Ork compiles for each node"
|
|
103
|
+
},
|
|
104
|
+
subCommands: { explain }
|
|
105
|
+
});
|
|
106
|
+
export { explainNode as n, context as t };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { $n as findFollowUp, Kr as placeFiles, Pn as liveStatus, Sn as deliverFollowUp, Yn as loadProject, gr as runGh, vn as checkFollowUpsDue } from "./src-BuF9Lbow.mjs";
|
|
2
|
+
// ../core/src/follow-ups/take.ts
|
|
3
|
+
const put = (rows, f, run, at) => {
|
|
4
|
+
const next = {
|
|
5
|
+
...f,
|
|
6
|
+
takenBy: run,
|
|
7
|
+
updatedAt: at
|
|
8
|
+
};
|
|
9
|
+
rows.putFollowUp(next);
|
|
10
|
+
return next;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Why `f` is held by the run that took it, as the end of `taken by run <id>,
|
|
14
|
+
* <note>`; `undefined` when it is free. A run the store does not know counts as
|
|
15
|
+
* live while the mark is recent, so a run that fails its preflight frees its
|
|
16
|
+
* follow-ups when the window ends.
|
|
17
|
+
*/
|
|
18
|
+
const heldNote = (f, isLive, now) => {
|
|
19
|
+
if (f.takenBy === void 0) return void 0;
|
|
20
|
+
const live = isLive(f.takenBy);
|
|
21
|
+
if (live !== void 0) return live ? "still running" : void 0;
|
|
22
|
+
return now - Date.parse(f.updatedAt) < 12e4 ? "not started yet; free in under 2 minutes" : void 0;
|
|
23
|
+
};
|
|
24
|
+
/** Whether another run holds `f`: it is not free to take again. */
|
|
25
|
+
const heldByLive = (f, run, isLive, now) => f.takenBy !== run && heldNote(f, isLive, now) !== void 0;
|
|
26
|
+
/**
|
|
27
|
+
* The project's due `agent` follow-ups no live run has taken (`ids`), and those
|
|
28
|
+
* a live run holds (`held`), so a second `--follow-ups due` doesn't take them
|
|
29
|
+
* twice.
|
|
30
|
+
*/
|
|
31
|
+
const availableDue = (rows, project, isLive, run = "", now = () => (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
32
|
+
const at = Date.parse(now());
|
|
33
|
+
const due = rows.followUps(project).filter((f) => f.for === "agent" && f.status === "due");
|
|
34
|
+
return {
|
|
35
|
+
ids: due.filter((f) => !heldByLive(f, run, isLive, at)).map((f) => f.id),
|
|
36
|
+
held: due.filter((f) => heldByLive(f, run, isLive, at))
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* `ork run start --follow-ups <ids>`: the open `agent` follow-ups among `ids`
|
|
41
|
+
* become `run`'s brief; why each other id wasn't taken. One a live run took is
|
|
42
|
+
* refused; one a finished, failed or crashed run took can be taken again.
|
|
43
|
+
*/
|
|
44
|
+
const takeFollowUps = (rows, taking, now = () => (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
45
|
+
const taken = [];
|
|
46
|
+
const refused = [];
|
|
47
|
+
const at = Date.parse(now());
|
|
48
|
+
for (const id of taking.ids) {
|
|
49
|
+
const f = rows.followUp(taking.project, id);
|
|
50
|
+
if (f === void 0) refused.push(`${id}: there is no such follow-up`);
|
|
51
|
+
else if (f.for !== "agent") refused.push(`${id}: it is for ${f.for}`);
|
|
52
|
+
else if (f.status === "done" || f.status === "dropped") refused.push(`${id}: it is ${f.status}`);
|
|
53
|
+
else {
|
|
54
|
+
const note = f.takenBy === taking.run ? void 0 : heldNote(f, taking.isLive, at);
|
|
55
|
+
if (note === void 0) taken.push(put(rows, f, taking.run, now()));
|
|
56
|
+
else refused.push(`${id}: taken by run ${f.takenBy}, ${note}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
taken,
|
|
61
|
+
refused
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
// src/follow-ups/context.ts
|
|
65
|
+
/**
|
|
66
|
+
* @file What `ork follow-ups` and a run share: the project's `followUps` rule,
|
|
67
|
+
* the sink a run hands its sessions' `follow_up` tool, the due check that
|
|
68
|
+
* runs before every call (a `gh` that fails only warns), and the follow-ups
|
|
69
|
+
* `ork run --follow-ups` takes as a run's brief.
|
|
70
|
+
*/
|
|
71
|
+
/** The project's `followUps` rule, when it sets one. */
|
|
72
|
+
const followUpsRule = (place) => loadProject(place.root, placeFiles(place).rules).project?.followUps;
|
|
73
|
+
/** What a run hands its sessions: each `follow_up` call, kept as the rule says. */
|
|
74
|
+
const followUpSink = (options) => (input, at) => {
|
|
75
|
+
const rule = followUpsRule(options.place);
|
|
76
|
+
return deliverFollowUp(input, {
|
|
77
|
+
store: options.store,
|
|
78
|
+
project: options.place.root,
|
|
79
|
+
worktree: at.cwd,
|
|
80
|
+
...rule === void 0 ? {} : { rule },
|
|
81
|
+
...options.home === void 0 ? {} : { home: options.home },
|
|
82
|
+
...options.gh === void 0 ? {} : { gh: options.gh }
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
/** Gh's own words when it has no login. */
|
|
86
|
+
const SIGNED_OUT = /not logged in|gh auth login/iu;
|
|
87
|
+
/**
|
|
88
|
+
* A gh error's first line, without a trailing period, so the notice is one
|
|
89
|
+
* line.
|
|
90
|
+
*/
|
|
91
|
+
const oneLine = (error) => (error.trim().split("\n")[0] ?? "").trim().replace(/\.$/u, "");
|
|
92
|
+
/**
|
|
93
|
+
* Moves the project's follow-ups whose time has come to due, as every `ork
|
|
94
|
+
* follow-ups` call does first. `gh` failing only warns, through `warn`: one
|
|
95
|
+
* line per call, however many refs failed.
|
|
96
|
+
*/
|
|
97
|
+
const refreshDue = async (store, project, warn, gh = runGh) => {
|
|
98
|
+
try {
|
|
99
|
+
const checked = await checkFollowUpsDue({
|
|
100
|
+
rows: store,
|
|
101
|
+
project,
|
|
102
|
+
gh
|
|
103
|
+
});
|
|
104
|
+
const [first] = checked.errors;
|
|
105
|
+
if (first !== void 0) warn(checked.errors.some((error) => SIGNED_OUT.test(error)) ? "Can't check PRs: `gh` isn't signed in. After-merge follow-ups stay waiting; run `gh auth login` yourself." : `Can't check PRs: ${oneLine(first)}. After-merge follow-ups stay waiting.`);
|
|
106
|
+
return checked.due;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
warn(`couldn't check what is due: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
/** The text a run starts from when it takes these follow-ups as its brief. */
|
|
113
|
+
const briefOf = (followUps) => ["Do these follow-ups left by earlier changes:", ...followUps.flatMap((f) => [
|
|
114
|
+
"",
|
|
115
|
+
`## ${f.title} (${f.id})`,
|
|
116
|
+
...f.body === "" ? [] : [f.body],
|
|
117
|
+
...f.where.length === 0 ? [] : [`Files: ${f.where.join(", ")}`]
|
|
118
|
+
])].join("\n");
|
|
119
|
+
/**
|
|
120
|
+
* Whether a run is going, as `ork ps` decides: running with its process alive.
|
|
121
|
+
* `undefined` for a run the store does not know yet: core holds its mark for a
|
|
122
|
+
* short while, as the run may be about to begin.
|
|
123
|
+
*/
|
|
124
|
+
const runIsLive = (store) => (runId) => {
|
|
125
|
+
const [run] = store.lookup(runId);
|
|
126
|
+
return run === void 0 ? void 0 : liveStatus(run) === "running";
|
|
127
|
+
};
|
|
128
|
+
const nothing = () => {};
|
|
129
|
+
/**
|
|
130
|
+
* The follow-ups `ids` (or prefixes) name, when every one can be taken as a
|
|
131
|
+
* brief; nothing is written, so a run that fails to start leaves them alone.
|
|
132
|
+
*/
|
|
133
|
+
const checkTakeable = (store, project, ids) => {
|
|
134
|
+
const full = [];
|
|
135
|
+
const lines = [];
|
|
136
|
+
for (const id of ids) {
|
|
137
|
+
const found = findFollowUp(store, project, id);
|
|
138
|
+
if (found.ok) full.push(found.followUp.id);
|
|
139
|
+
else lines.push(found.error);
|
|
140
|
+
}
|
|
141
|
+
const { taken, refused } = takeFollowUps({
|
|
142
|
+
followUp: (p, id) => store.followUp(p, id),
|
|
143
|
+
followUps: (p) => store.followUps(p),
|
|
144
|
+
putFollowUp: nothing
|
|
145
|
+
}, {
|
|
146
|
+
project,
|
|
147
|
+
run: "",
|
|
148
|
+
ids: full,
|
|
149
|
+
isLive: runIsLive(store)
|
|
150
|
+
});
|
|
151
|
+
const all = [...lines, ...refused];
|
|
152
|
+
return all.length > 0 ? {
|
|
153
|
+
ok: false,
|
|
154
|
+
lines: all.map((line) => `--follow-ups: ${line}`)
|
|
155
|
+
} : {
|
|
156
|
+
ok: true,
|
|
157
|
+
followUps: taken
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
export { refreshDue as a, heldNote as c, followUpsRule as i, takeFollowUps as l, checkTakeable as n, runIsLive as o, followUpSink as r, availableDue as s, briefOf as t };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { pt as detachRun } from "./src-BuF9Lbow.mjs";
|
|
2
|
+
import { n as newRunId } from "./find-Ct5CNl1J.mjs";
|
|
3
|
+
import { c as write, i as detachHint, l as writeErr, s as style } from "./output-DXZbnFt_.mjs";
|
|
4
|
+
import { r as isFlag } from "./short-flags-Qf21as0E.mjs";
|
|
5
|
+
// src/run/detached.ts
|
|
6
|
+
/**
|
|
7
|
+
* @file `ork run --detach`: checks what the run would refuse (a `--follow-ups`
|
|
8
|
+
* that can't be taken) before any run folder is made, then starts the run in
|
|
9
|
+
* the background and says how to follow it.
|
|
10
|
+
*/
|
|
11
|
+
/** Starts the run in the background; `rawArgs` are `ork run`'s own, as typed. */
|
|
12
|
+
const startDetached = async (store, options) => {
|
|
13
|
+
const { rawArgs, cwd, target } = options;
|
|
14
|
+
const { prepareFollowUps } = await import("./brief-C2hew8cS.mjs");
|
|
15
|
+
if (await prepareFollowUps(store, rawArgs, void 0, target, void 0, false) === void 0) return;
|
|
16
|
+
const single = options.node !== void 0 && options.node !== "";
|
|
17
|
+
const runId = newRunId(store, single ? "node-" : "");
|
|
18
|
+
const detached = await detachRun({
|
|
19
|
+
store,
|
|
20
|
+
runId,
|
|
21
|
+
args: [
|
|
22
|
+
"run",
|
|
23
|
+
...rawArgs.filter((arg) => !isFlag(arg, "detach")),
|
|
24
|
+
"--run-id",
|
|
25
|
+
runId
|
|
26
|
+
],
|
|
27
|
+
cwd,
|
|
28
|
+
runDir: target.runDir(runId)
|
|
29
|
+
});
|
|
30
|
+
if (detached.ok) {
|
|
31
|
+
write(`▸ run ${detached.runId} started in the background`);
|
|
32
|
+
write(style("dim", ` ${detachHint(detached.runId)}`));
|
|
33
|
+
} else {
|
|
34
|
+
writeErr(detached.error);
|
|
35
|
+
writeErr(style("dim", `(output: ${detached.log})`));
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export { startDetached };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { n as argsOf } from "./command-tree-CEkzWNy6.mjs";
|
|
2
|
+
import { t as argumentError } from "./argument-error-CihH46Le.mjs";
|
|
3
|
+
// src/extra-arguments.ts
|
|
4
|
+
/** Refuses the first word beyond the positionals the command declares. */
|
|
5
|
+
const refuseExtraArguments = {
|
|
6
|
+
name: "refuse-extra-arguments",
|
|
7
|
+
setup: async ({ cmd, args }) => {
|
|
8
|
+
const declared = Object.values(await argsOf(cmd)).filter((def) => def.type === "positional").length;
|
|
9
|
+
const extra = args._[declared];
|
|
10
|
+
if (extra !== void 0) throw argumentError(`Unexpected argument \`${extra}\`.`);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export { refuseExtraArguments as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { qr as placeFor } from "./src-BuF9Lbow.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
// ../core/src/remote/find.ts
|
|
4
4
|
/**
|
|
@@ -16,7 +16,7 @@ const findRun = (store, arg, cwd) => {
|
|
|
16
16
|
});
|
|
17
17
|
return latest === void 0 ? {
|
|
18
18
|
ok: false,
|
|
19
|
-
error: "No runs in this project yet; see `ork ps --all`."
|
|
19
|
+
error: "No runs in this project yet; see `ork ps --project all`."
|
|
20
20
|
} : {
|
|
21
21
|
ok: true,
|
|
22
22
|
run: latest
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { qr as placeFor } from "./src-BuF9Lbow.mjs";
|
|
2
|
+
import { i as parseLimit, n as unknownProjectRefusal, r as parseFilters, t as limitRefusal } from "./refusals-DQQX3AIb.mjs";
|
|
3
|
+
import { t as findRun } from "./find-Ct5CNl1J.mjs";
|
|
4
|
+
import { a as resolved, n as argsOf } from "./command-tree-CEkzWNy6.mjs";
|
|
5
|
+
import { l as writeErr } from "./output-DXZbnFt_.mjs";
|
|
6
|
+
import { t as argumentError } from "./argument-error-CihH46Le.mjs";
|
|
7
|
+
// src/list/known-flags.ts
|
|
8
|
+
/** Names citty reads without the command declaring them. */
|
|
9
|
+
const BUILT_IN = /* @__PURE__ */ new Set([
|
|
10
|
+
"_",
|
|
11
|
+
"help",
|
|
12
|
+
"h"
|
|
13
|
+
]);
|
|
14
|
+
/** A flag's name without its spelling: `run-id`, `runId` and `run_id` agree. */
|
|
15
|
+
const spelling = (name) => name.replaceAll(/[-_]/gu, "").toLowerCase();
|
|
16
|
+
/** How the flag is typed: `--since`, or `-x` for a one-letter name. */
|
|
17
|
+
const written = (name) => name.length === 1 ? `-${name}` : `--${name}`;
|
|
18
|
+
/** Refuses the first flag the command does not declare. */
|
|
19
|
+
const refuseUnknownFlags = {
|
|
20
|
+
name: "refuse-unknown-flags",
|
|
21
|
+
setup: async ({ cmd, rawArgs, args }) => {
|
|
22
|
+
const [first] = rawArgs;
|
|
23
|
+
const subs = await resolved(cmd.subCommands ?? {});
|
|
24
|
+
if (first !== void 0 && first in subs) return;
|
|
25
|
+
const known = /* @__PURE__ */ new Set();
|
|
26
|
+
for (const [name, def] of Object.entries(await argsOf(cmd))) {
|
|
27
|
+
known.add(spelling(name));
|
|
28
|
+
if ("alias" in def && def.alias !== void 0) for (const alias of [def.alias].flat()) known.add(spelling(alias));
|
|
29
|
+
}
|
|
30
|
+
const unknown = Object.keys(args).find((key) => !BUILT_IN.has(key) && !known.has(spelling(key)));
|
|
31
|
+
if (unknown !== void 0) throw argumentError(`Unknown flag \`${written(unknown)}\`.`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// src/list/flags.ts
|
|
35
|
+
/**
|
|
36
|
+
* @file The flags every list command shares (RFC-017), made from its field
|
|
37
|
+
* table: one flag per field (an enum field's values as citty `options`, so
|
|
38
|
+
* `--help` and Tab completion read them), `--ids`, `--limit` and `--json`;
|
|
39
|
+
* and reading them back: each value checked by core's `parseFilters`,
|
|
40
|
+
* `--limit` a positive whole number, `--project` a registered one. A refusal
|
|
41
|
+
* says why on stderr and sets the exit code; the command then returns.
|
|
42
|
+
*/
|
|
43
|
+
const fieldArg = (field) => {
|
|
44
|
+
if (field.type !== "enum") return {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: field.description
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
type: "enum",
|
|
50
|
+
options: [...field.values ?? [], ...field.all === true ? ["all"] : []],
|
|
51
|
+
description: field.description
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/** The citty flags of `table`, then `--ids`, `--limit`, `--json`, `extra`. */
|
|
55
|
+
const filterArgs = (table, options = {}) => ({
|
|
56
|
+
...Object.fromEntries(table.fields.map((field) => [field.name, fieldArg(field)])),
|
|
57
|
+
...options.ids ?? table.id !== void 0 ? { ids: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
description: "Print only the ids, comma-separated, on one line",
|
|
60
|
+
default: false
|
|
61
|
+
} } : {},
|
|
62
|
+
...options.limit ?? true ? { limit: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Show at most this many (a positive whole number)"
|
|
65
|
+
} } : {},
|
|
66
|
+
...options.json ?? true ? { json: {
|
|
67
|
+
type: "boolean",
|
|
68
|
+
description: "Print JSON",
|
|
69
|
+
default: false
|
|
70
|
+
} } : {},
|
|
71
|
+
...options.extra
|
|
72
|
+
});
|
|
73
|
+
/** Says why, and makes the command exit 1. */
|
|
74
|
+
const fail = (message) => {
|
|
75
|
+
writeErr(message);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
};
|
|
78
|
+
/** The `--ids` with `--json` refusal; `true` when `args` has both. */
|
|
79
|
+
const refuseIdsWithJson = (args) => {
|
|
80
|
+
if (args.ids !== true || args.json !== true) return false;
|
|
81
|
+
fail("--ids and --json can't be used together.");
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* `args` (a command's parsed flags) read against `table`: the filter, and the
|
|
86
|
+
* limit. `undefined` when something was refused (already said, exit code set).
|
|
87
|
+
*/
|
|
88
|
+
const readListArgs = (table, args, options = {}) => {
|
|
89
|
+
if (refuseIdsWithJson(args)) return void 0;
|
|
90
|
+
const parsed = parseFilters(table, args, options.now ?? Date.now());
|
|
91
|
+
if (!parsed.ok) {
|
|
92
|
+
fail(parsed.error);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const text = args["limit"];
|
|
96
|
+
if (typeof text !== "string") return {
|
|
97
|
+
filter: parsed.filter,
|
|
98
|
+
limit: void 0
|
|
99
|
+
};
|
|
100
|
+
const limit = parseLimit(text);
|
|
101
|
+
if (limit === null) {
|
|
102
|
+
fail(limitRefusal(text));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
filter: parsed.filter,
|
|
107
|
+
limit
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
/** The project root a scope other than `all` names. */
|
|
111
|
+
const rootOf = (scope, projects) => scope === "here" ? placeFor(process.cwd(), projects).root : scope.root;
|
|
112
|
+
/**
|
|
113
|
+
* The scope the filter's `project` names, checked against the registered
|
|
114
|
+
* projects (core does not know them): an id or a root, or `all`; none given is
|
|
115
|
+
* `here`. A project not registered is refused, listing the registered ones
|
|
116
|
+
* (said, exit code set, `undefined`).
|
|
117
|
+
*/
|
|
118
|
+
const projectScope = (filter, projects) => {
|
|
119
|
+
const key = filter["project"];
|
|
120
|
+
if (key === void 0) return "here";
|
|
121
|
+
if (key === "all") return "all";
|
|
122
|
+
const found = projects.find((p) => p.id === key || p.root === key);
|
|
123
|
+
if (found !== void 0) return { root: found.root };
|
|
124
|
+
fail(unknownProjectRefusal(key, projects.map((p) => p.id)));
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* `filter` with its `run` (a prefix) made the full id of the run it names, as
|
|
128
|
+
* `ork decide --run` reads it; an unknown or ambiguous run is refused with
|
|
129
|
+
* `findRun`'s message (said, exit code set, `undefined`). No `run` (or an empty
|
|
130
|
+
* one) leaves `filter` as it is.
|
|
131
|
+
*/
|
|
132
|
+
const resolveRun = (store, filter) => {
|
|
133
|
+
const run = filter["run"];
|
|
134
|
+
if (run === void 0 || run === "") return filter;
|
|
135
|
+
const found = findRun(store, run, process.cwd());
|
|
136
|
+
if (!found.ok) {
|
|
137
|
+
fail(found.error);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
...filter,
|
|
142
|
+
run: found.run.id
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
export { refuseIdsWithJson as a, refuseUnknownFlags as c, readListArgs as i, filterArgs as n, resolveRun as o, projectScope as r, rootOf as s, fail as t };
|