@krapht/ork 1.0.0-alpha.18 → 1.0.0-alpha.19
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__-B8TNbFtN.mjs → __build_info__-dPf4cEGL.mjs} +3 -134
- package/dist/{attach-CU4Pk42S.mjs → attach-BQl8nQz2.mjs} +5 -5
- package/dist/{attach-qvfyaQXC.mjs → attach-DhZQ87Nz.mjs} +49 -224
- package/dist/bin.mjs +204 -154
- package/dist/{brief-Bh1FnAHv.mjs → brief-BQbeoPMc.mjs} +19 -9
- package/dist/{builtin-files-dnTP2GaX.mjs → builtin-files-CWL3JEo-.mjs} +1 -1
- package/dist/{command-tree-BkSYWmYJ.mjs → command-tree-B6uvMJq1.mjs} +1 -1
- package/dist/{completion-CzXUGSVn.mjs → completion-DKPuD_Rw.mjs} +47 -14
- package/dist/{context-b4_gPlv3.mjs → context-Bw6R_EYq.mjs} +3 -3
- package/dist/detached-BQlbmqR0.mjs +38 -0
- package/dist/{find-P-VV4B47.mjs → find-B30-rd-u.mjs} +1 -1
- package/dist/flags-ByYBx-Xb.mjs +170 -0
- package/dist/{follow-ups-B_c1DSpA.mjs → follow-ups-Cxca1Jgg.mjs} +24 -18
- package/dist/{format-BS1qzpkf.mjs → format-C-nEZv-P.mjs} +27 -11
- package/dist/{help-groups-hB6kY34d.mjs → help-groups-BpRgBSTh.mjs} +13 -7
- package/dist/{in-a-run-DKoMGTNE.mjs → in-a-run-CZFzoT3R.mjs} +2 -2
- package/dist/{install-B743Bwzz.mjs → install-CsqK1avz.mjs} +2 -183
- package/dist/{limits-DrsKCqaN.mjs → limits-CXh052II.mjs} +1 -1
- package/dist/{mcp-BMwekw38.mjs → mcp-B4wME9C4.mjs} +6 -6
- package/dist/{once-6L8gciJV.mjs → once-BKn3pOtg.mjs} +3 -3
- package/dist/outputs-text-CNTJ7Qq_.mjs +19 -0
- package/dist/{pause-8gnVgICR.mjs → pause-B1grR1cl.mjs} +3 -3
- package/dist/{print-DLe4MHn-.mjs → print-Bd8MWVJk.mjs} +1 -1
- package/dist/{project-records-CsILxwfI.mjs → project-records-BOEVs69i.mjs} +1 -1
- package/dist/{parse-BzWpMN-a.mjs → refusals-DQQX3AIb.mjs} +10 -1
- package/dist/{relay-Bvm9JEoN.mjs → relay-CeNiq3GT.mjs} +1 -1
- package/dist/{run-D4yuTmmJ.mjs → run-C0C54UiU.mjs} +116 -137
- package/dist/{run-y8jI7bAI.mjs → run-D6yU2rLf.mjs} +1 -1
- package/dist/{send-BV9eIxQ7.mjs → send-DqgF58hb.mjs} +2 -2
- package/dist/{src-Frwpx3WF.mjs → src-Ciy7i3se.mjs} +843 -66
- package/dist/{src-ygFMh2eP.mjs → src-D2qpd-G7.mjs} +4 -4
- package/dist/{src-CVeA4tAw.mjs → src-DP02RP9O.mjs} +227 -180
- package/dist/{src-DWAcd3Sy.mjs → src-n-u4TGev.mjs} +186 -43
- package/dist/{store-B0nGqnPq.mjs → store-2KXzMCEN.mjs} +1 -1
- package/dist/ui/assets/app-C5vO1M-l.css +1 -0
- package/dist/ui/assets/{app-C4yxmYQR.js → app-ICEKpy_C.js} +21 -20
- package/dist/ui/assets/{index-CcbJWo5C.js → index-BGaix5wh.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-BZ9BhFw8.mjs → ui-CAvygxTb.mjs} +53 -24
- package/dist/{usage-BIs4j5Rw.mjs → usage-BFBco5OS.mjs} +7 -7
- package/dist/{validate-Xn2ZbJQC.mjs → validate-UrmC5AG1.mjs} +5 -5
- package/dist/{watch-9VM3DFTp.mjs → watch-DTxkgB8R.mjs} +5 -5
- package/dist/{workflows-CBYtycIy.mjs → workflows-8GV-SeRm.mjs} +28 -4
- package/dist/{worktrees-C7vfh7y5.mjs → worktrees-CUdUvZlO.mjs} +9 -6
- package/package.json +1 -1
- package/dist/flags-CqjJ44uW.mjs +0 -125
- package/dist/post-y1qL1Fnj.mjs +0 -46
- package/dist/ui/assets/app-9Q-62Ywz.css +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as isBuiltinArg, i as builtinWorkflowText, r as builtinWorkflowNames } from "./builtin-files-
|
|
3
|
-
import { t as findRun } from "./find-
|
|
4
|
-
import { a as SHELL_SCRIPTS, i as SHELLS, n as groupedEntries, r as COMPLETE_COMMAND, t as groupOf } from "./help-groups-
|
|
5
|
-
import {
|
|
1
|
+
import { Ci as NodeIdSchema, Nn as liveStatus, Pi as AGENT_DRIVERS, R as openStore, Ur as placeFiles, Wr as placeFor, ot as pendingDecisions, z as storePath } from "./src-Ciy7i3se.mjs";
|
|
2
|
+
import { a as isBuiltinArg, i as builtinWorkflowText, r as builtinWorkflowNames } from "./builtin-files-CWL3JEo-.mjs";
|
|
3
|
+
import { t as findRun } from "./find-B30-rd-u.mjs";
|
|
4
|
+
import { a as SHELL_SCRIPTS, i as SHELLS, n as groupedEntries, r as COMPLETE_COMMAND, t as groupOf } from "./help-groups-BpRgBSTh.mjs";
|
|
5
|
+
import { o as subcommandsOf, r as findEntry, t as argInfoOf } from "./command-tree-B6uvMJq1.mjs";
|
|
6
6
|
import { l as writeErr } from "./output-B1kwo0YU.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { n as WORKFLOW_SUFFIX, o as resolveWorkflowArg } from "./workflows-8GV-SeRm.mjs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { parse } from "yaml";
|
|
10
10
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
@@ -279,7 +279,8 @@ const providerFor = (providers, path, name) => providers[[...path, name].join("
|
|
|
279
279
|
/**
|
|
280
280
|
* @file What `ork __complete` offers at a position: subcommands (the root's in
|
|
281
281
|
* `ork --help`'s groups), a command's public flags, an argument's values from
|
|
282
|
-
* its provider, or an enum flag's values
|
|
282
|
+
* its provider, or an enum flag's values (also when written `--flag=value`,
|
|
283
|
+
* each candidate then keeping `--flag=`); filtered by what was typed, and
|
|
283
284
|
* printed one per line as `value<TAB>description`, with `:group <title>`
|
|
284
285
|
* before each group and `:files` when file names fit too.
|
|
285
286
|
*/
|
|
@@ -365,10 +366,14 @@ const offerAt = (position, providers, context) => {
|
|
|
365
366
|
/** What fits at `position`, keeping what starts with the typed word. */
|
|
366
367
|
const offerFor = (position, providers, context) => {
|
|
367
368
|
const offer = offerAt(position, providers, context);
|
|
369
|
+
const prefix = position.slot.kind === "value" ? position.slot.prefix ?? "" : "";
|
|
368
370
|
return {
|
|
369
371
|
sections: offer.sections.map((section) => ({
|
|
370
372
|
...section,
|
|
371
|
-
candidates: section.candidates.filter((c) => c.value.startsWith(position.current))
|
|
373
|
+
candidates: section.candidates.filter((c) => c.value.startsWith(position.current)).map((c) => ({
|
|
374
|
+
value: `${prefix}${c.value}`,
|
|
375
|
+
description: c.description
|
|
376
|
+
}))
|
|
372
377
|
})).filter((section) => section.candidates.length > 0),
|
|
373
378
|
files: offer.files
|
|
374
379
|
};
|
|
@@ -436,12 +441,26 @@ const step = async (walk, word) => {
|
|
|
436
441
|
index: walk.index + 1
|
|
437
442
|
};
|
|
438
443
|
};
|
|
439
|
-
|
|
444
|
+
/** `--flag=` of a typed `--flag=text` whose flag takes a value, else nothing. */
|
|
445
|
+
const valuePrefixOf = (walk, typed) => {
|
|
446
|
+
if (walk.pending !== void 0 || walk.rest || !typed.startsWith("-")) return;
|
|
447
|
+
const at = typed.indexOf("=");
|
|
448
|
+
return (at < 0 ? void 0 : flagNamed(walk.flags, typed))?.takesValue === true ? typed.slice(0, at + 1) : void 0;
|
|
449
|
+
};
|
|
450
|
+
const slotOf = (walk, typed, prefix) => {
|
|
440
451
|
if (walk.pending !== void 0) return {
|
|
441
452
|
kind: "value",
|
|
442
453
|
flag: walk.pending
|
|
443
454
|
};
|
|
444
|
-
if (
|
|
455
|
+
if (prefix !== void 0) {
|
|
456
|
+
const flag = flagNamed(walk.flags, prefix);
|
|
457
|
+
if (flag !== void 0) return {
|
|
458
|
+
kind: "value",
|
|
459
|
+
flag,
|
|
460
|
+
prefix
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (!walk.rest && typed.startsWith("-")) return { kind: "flag" };
|
|
445
464
|
const positional = walk.positionals[walk.index];
|
|
446
465
|
return positional === void 0 ? { kind: "word" } : {
|
|
447
466
|
kind: "word",
|
|
@@ -454,7 +473,7 @@ const slotOf = (walk, current) => {
|
|
|
454
473
|
*/
|
|
455
474
|
const positionOf = async (root, words) => {
|
|
456
475
|
const before = words.slice(0, -1);
|
|
457
|
-
const
|
|
476
|
+
const typed = words.at(-1) ?? "";
|
|
458
477
|
const start = {
|
|
459
478
|
...await argInfoOf(root),
|
|
460
479
|
path: [],
|
|
@@ -465,10 +484,12 @@ const positionOf = async (root, words) => {
|
|
|
465
484
|
};
|
|
466
485
|
const walk = await before.reduce(async (previous, word) => step(await previous, word), Promise.resolve(start));
|
|
467
486
|
const { pending: _pending, rest: _rest, ...reached } = walk;
|
|
487
|
+
const prefix = valuePrefixOf(walk, typed);
|
|
488
|
+
const current = prefix === void 0 ? typed : typed.slice(prefix.length);
|
|
468
489
|
return {
|
|
469
490
|
...reached,
|
|
470
491
|
current,
|
|
471
|
-
slot: slotOf(walk,
|
|
492
|
+
slot: slotOf(walk, typed, prefix)
|
|
472
493
|
};
|
|
473
494
|
};
|
|
474
495
|
// src/commands/completion.ts
|
|
@@ -495,12 +516,24 @@ const completionLines = async (root, words, cwd, storeFile) => {
|
|
|
495
516
|
opened.store?.close();
|
|
496
517
|
}
|
|
497
518
|
};
|
|
519
|
+
const guard = (error) => {
|
|
520
|
+
if (error.code !== "EPIPE") throw error;
|
|
521
|
+
};
|
|
522
|
+
/**
|
|
523
|
+
* Writes `text` to stdout. A reader that quits early (`ork completion zsh |
|
|
524
|
+
* head -1`) closes the pipe: that EPIPE is ignored, any other error is not.
|
|
525
|
+
*/
|
|
526
|
+
const writeOut = (text) => {
|
|
527
|
+
process.stdout.off("error", guard);
|
|
528
|
+
process.stdout.on("error", guard);
|
|
529
|
+
process.stdout.write(text);
|
|
530
|
+
};
|
|
498
531
|
/** `ork __complete`'s whole job: print the candidates, or nothing. */
|
|
499
532
|
const printCompletions = async (root, args, cwd = process.cwd()) => {
|
|
500
533
|
try {
|
|
501
534
|
const words = args[0] === "--" ? args.slice(1) : args;
|
|
502
535
|
const lines = await completionLines(root, words, cwd);
|
|
503
|
-
|
|
536
|
+
writeOut(lines.map((line) => `${line}\n`).join(""));
|
|
504
537
|
} catch {}
|
|
505
538
|
};
|
|
506
539
|
/** The hidden `ork __complete` of the command `root`. */
|
|
@@ -529,7 +562,7 @@ const completion = defineCommand({
|
|
|
529
562
|
process.exitCode = 1;
|
|
530
563
|
return;
|
|
531
564
|
}
|
|
532
|
-
|
|
565
|
+
writeOut(script);
|
|
533
566
|
}
|
|
534
567
|
});
|
|
535
568
|
export { completeCommand, completion, printCompletions };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Zr as LESSON_CAP_TOKENS, yt as explainSession } from "./src-Ciy7i3se.mjs";
|
|
2
2
|
import { c as write, s as style } from "./output-B1kwo0YU.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { a as formatReport, t as loadWorkflow } from "./validate-
|
|
3
|
+
import { a as refusedBuiltin, s as workflowArg } from "./workflows-8GV-SeRm.mjs";
|
|
4
|
+
import { a as formatReport, t as loadWorkflow } from "./validate-UrmC5AG1.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
// src/commands/context.ts
|
|
7
7
|
/**
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ft as detachRun } from "./src-Ciy7i3se.mjs";
|
|
2
|
+
import { n as newRunId } from "./find-B30-rd-u.mjs";
|
|
3
|
+
import { c as write, i as detachHint, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
|
|
4
|
+
// src/run/detached.ts
|
|
5
|
+
/**
|
|
6
|
+
* @file `ork run --detach`: checks what the run would refuse (a `--follow-ups`
|
|
7
|
+
* that can't be taken) before any run folder is made, then starts the run in
|
|
8
|
+
* the background and says how to follow it.
|
|
9
|
+
*/
|
|
10
|
+
/** Starts the run in the background; `rawArgs` are `ork run`'s own, as typed. */
|
|
11
|
+
const startDetached = async (store, options) => {
|
|
12
|
+
const { rawArgs, cwd, target } = options;
|
|
13
|
+
const { prepareFollowUps } = await import("./brief-BQbeoPMc.mjs");
|
|
14
|
+
if (await prepareFollowUps(store, rawArgs, void 0, target, void 0, false) === void 0) return;
|
|
15
|
+
const single = options.node !== void 0 && options.node !== "";
|
|
16
|
+
const runId = newRunId(store, single ? "node-" : "");
|
|
17
|
+
const detached = await detachRun({
|
|
18
|
+
store,
|
|
19
|
+
runId,
|
|
20
|
+
args: [
|
|
21
|
+
"run",
|
|
22
|
+
...rawArgs.filter((arg) => arg !== "--detach"),
|
|
23
|
+
"--run-id",
|
|
24
|
+
runId
|
|
25
|
+
],
|
|
26
|
+
cwd,
|
|
27
|
+
runDir: target.runDir(runId)
|
|
28
|
+
});
|
|
29
|
+
if (detached.ok) {
|
|
30
|
+
write(`▸ run ${detached.runId} started in the background`);
|
|
31
|
+
write(style("dim", ` ${detachHint(detached.runId)}`));
|
|
32
|
+
} else {
|
|
33
|
+
writeErr(detached.error);
|
|
34
|
+
writeErr(style("dim", `(output: ${detached.log})`));
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export { startDetached };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import "./src-Ciy7i3se.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-B30-rd-u.mjs";
|
|
4
|
+
import { a as resolved, n as argsOf } from "./command-tree-B6uvMJq1.mjs";
|
|
5
|
+
import { l as writeErr } from "./output-B1kwo0YU.mjs";
|
|
6
|
+
import { parseArgs } from "citty";
|
|
7
|
+
// src/list/known-flags.ts
|
|
8
|
+
/**
|
|
9
|
+
* @file The citty plugin that refuses a flag a list command does not have (`ork
|
|
10
|
+
* ps --bogus`, `ork decide --list --since 24h`). citty's parser is not
|
|
11
|
+
* strict: it puts an unknown `--since 24h` into the arguments as `since:
|
|
12
|
+
* true` plus a positional, which `decide` would read as a run id. The plugin
|
|
13
|
+
* runs before the command, on the nine list commands only (specs/cli/
|
|
14
|
+
* list-filters.md), and refuses with citty's own argument error, so the
|
|
15
|
+
* one-line message and exit code 1 are the same as for a wrong enum value.
|
|
16
|
+
*/
|
|
17
|
+
/** Names citty reads without the command declaring them. */
|
|
18
|
+
const BUILT_IN = /* @__PURE__ */ new Set([
|
|
19
|
+
"_",
|
|
20
|
+
"help",
|
|
21
|
+
"h"
|
|
22
|
+
]);
|
|
23
|
+
/** A flag's name without its spelling: `run-id`, `runId` and `run_id` agree. */
|
|
24
|
+
const spelling = (name) => name.replaceAll(/[-_]/gu, "").toLowerCase();
|
|
25
|
+
/**
|
|
26
|
+
* An error citty's `runMain` takes for an argument error (its `CLIError`, which
|
|
27
|
+
* it does not export): a failed parse gives one, and it takes `message`.
|
|
28
|
+
*/
|
|
29
|
+
const argumentError = (message) => {
|
|
30
|
+
try {
|
|
31
|
+
parseArgs([], { x: {
|
|
32
|
+
type: "string",
|
|
33
|
+
required: true
|
|
34
|
+
} });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
error.message = message;
|
|
38
|
+
return error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return new Error(message);
|
|
42
|
+
};
|
|
43
|
+
/** How the flag is typed: `--since`, or `-x` for a one-letter name. */
|
|
44
|
+
const written = (name) => name.length === 1 ? `-${name}` : `--${name}`;
|
|
45
|
+
/** Refuses the first flag the command does not declare. */
|
|
46
|
+
const refuseUnknownFlags = {
|
|
47
|
+
name: "refuse-unknown-flags",
|
|
48
|
+
setup: async ({ cmd, rawArgs, args }) => {
|
|
49
|
+
const [first] = rawArgs;
|
|
50
|
+
const subs = await resolved(cmd.subCommands ?? {});
|
|
51
|
+
if (first !== void 0 && first in subs) return;
|
|
52
|
+
const known = /* @__PURE__ */ new Set();
|
|
53
|
+
for (const [name, def] of Object.entries(await argsOf(cmd))) {
|
|
54
|
+
known.add(spelling(name));
|
|
55
|
+
if ("alias" in def && def.alias !== void 0) for (const alias of [def.alias].flat()) known.add(spelling(alias));
|
|
56
|
+
}
|
|
57
|
+
const unknown = Object.keys(args).find((key) => !BUILT_IN.has(key) && !known.has(spelling(key)));
|
|
58
|
+
if (unknown !== void 0) throw argumentError(`Unknown flag \`${written(unknown)}\`.`);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
// src/list/flags.ts
|
|
62
|
+
/**
|
|
63
|
+
* @file The flags every list command shares (RFC-017), made from its field
|
|
64
|
+
* table: one flag per field (an enum field's values as citty `options`, so
|
|
65
|
+
* `--help` and Tab completion read them), `--ids`, `--limit` and `--json`;
|
|
66
|
+
* and reading them back: each value checked by core's `parseFilters`,
|
|
67
|
+
* `--limit` a positive whole number, `--project` a registered one. A refusal
|
|
68
|
+
* says why on stderr and sets the exit code; the command then returns.
|
|
69
|
+
*/
|
|
70
|
+
const fieldArg = (field) => {
|
|
71
|
+
if (field.type !== "enum") return {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: field.description
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
type: "enum",
|
|
77
|
+
options: [...field.values ?? [], ...field.all === true ? ["all"] : []],
|
|
78
|
+
description: field.description
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/** The citty flags of `table`, then `--ids`, `--limit`, `--json`, `extra`. */
|
|
82
|
+
const filterArgs = (table, options = {}) => ({
|
|
83
|
+
...Object.fromEntries(table.fields.map((field) => [field.name, fieldArg(field)])),
|
|
84
|
+
...options.ids ?? table.id !== void 0 ? { ids: {
|
|
85
|
+
type: "boolean",
|
|
86
|
+
description: "Print only the ids, comma-separated, on one line",
|
|
87
|
+
default: false
|
|
88
|
+
} } : {},
|
|
89
|
+
...options.limit ?? true ? { limit: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Show at most this many (a positive whole number)"
|
|
92
|
+
} } : {},
|
|
93
|
+
...options.json ?? true ? { json: {
|
|
94
|
+
type: "boolean",
|
|
95
|
+
description: "Print JSON",
|
|
96
|
+
default: false
|
|
97
|
+
} } : {},
|
|
98
|
+
...options.extra
|
|
99
|
+
});
|
|
100
|
+
/** Says why, and makes the command exit 1. */
|
|
101
|
+
const fail = (message) => {
|
|
102
|
+
writeErr(message);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
};
|
|
105
|
+
/** The `--ids` with `--json` refusal; `true` when `args` has both. */
|
|
106
|
+
const refuseIdsWithJson = (args) => {
|
|
107
|
+
if (args.ids !== true || args.json !== true) return false;
|
|
108
|
+
fail("--ids and --json can't be used together.");
|
|
109
|
+
return true;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* `args` (a command's parsed flags) read against `table`: the filter, and the
|
|
113
|
+
* limit. `undefined` when something was refused (already said, exit code set).
|
|
114
|
+
*/
|
|
115
|
+
const readListArgs = (table, args, options = {}) => {
|
|
116
|
+
if (refuseIdsWithJson(args)) return void 0;
|
|
117
|
+
const parsed = parseFilters(table, args, options.now ?? Date.now());
|
|
118
|
+
if (!parsed.ok) {
|
|
119
|
+
fail(parsed.error);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const text = args["limit"];
|
|
123
|
+
if (typeof text !== "string") return {
|
|
124
|
+
filter: parsed.filter,
|
|
125
|
+
limit: void 0
|
|
126
|
+
};
|
|
127
|
+
const limit = parseLimit(text);
|
|
128
|
+
if (limit === null) {
|
|
129
|
+
fail(limitRefusal(text));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
filter: parsed.filter,
|
|
134
|
+
limit
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* The scope the filter's `project` names, checked against the registered
|
|
139
|
+
* projects (core does not know them): an id or a root, or `all`; none given is
|
|
140
|
+
* `here`. A project not registered is refused, listing the registered ones
|
|
141
|
+
* (said, exit code set, `undefined`).
|
|
142
|
+
*/
|
|
143
|
+
const projectScope = (filter, projects) => {
|
|
144
|
+
const key = filter["project"];
|
|
145
|
+
if (key === void 0) return "here";
|
|
146
|
+
if (key === "all") return "all";
|
|
147
|
+
const found = projects.find((p) => p.id === key || p.root === key);
|
|
148
|
+
if (found !== void 0) return { root: found.root };
|
|
149
|
+
fail(unknownProjectRefusal(key, projects.map((p) => p.id)));
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* `filter` with its `run` (a prefix) made the full id of the run it names, as
|
|
153
|
+
* `ork decide --run` reads it; an unknown or ambiguous run is refused with
|
|
154
|
+
* `findRun`'s message (said, exit code set, `undefined`). No `run` (or an empty
|
|
155
|
+
* one) leaves `filter` as it is.
|
|
156
|
+
*/
|
|
157
|
+
const resolveRun = (store, filter) => {
|
|
158
|
+
const run = filter["run"];
|
|
159
|
+
if (run === void 0 || run === "") return filter;
|
|
160
|
+
const found = findRun(store, run, process.cwd());
|
|
161
|
+
if (!found.ok) {
|
|
162
|
+
fail(found.error);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
...filter,
|
|
167
|
+
run: found.run.id
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
export { refuseIdsWithJson as a, readListArgs as i, filterArgs as n, resolveRun as o, projectScope as r, refuseUnknownFlags as s, fail as t };
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as followUpsRule, f as refreshDue, n as flagValues } from "./run-
|
|
3
|
-
import { g as changeFollowUp, v as findFollowUp, x as isGithubIssueUrl, y as followUpActions } from "./__build_info__-B8TNbFtN.mjs";
|
|
4
|
-
import { t as postFollowUpMessage } from "./post-y1qL1Fnj.mjs";
|
|
1
|
+
import { Gt as followUpListTable, Jn as changeFollowUp, Qn as isGithubIssueUrl, Sn as followUpProblem, Wr as placeFor, Xn as findFollowUp, Zn as followUpActions, gn as postFollowUpMessage, pr as runGh, vn as markDue, xn as deliverFollowUp, yn as recordPublished } from "./src-Ciy7i3se.mjs";
|
|
2
|
+
import { d as followUpsRule, f as refreshDue, n as flagValues } from "./run-C0C54UiU.mjs";
|
|
5
3
|
import { t as applyFilters } from "./apply-Mb0fNpCX.mjs";
|
|
6
|
-
import {
|
|
4
|
+
import { n as unknownProjectRefusal } from "./refusals-DQQX3AIb.mjs";
|
|
5
|
+
import { t as findRun } from "./find-B30-rd-u.mjs";
|
|
7
6
|
import { a as plain, c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
|
|
8
|
-
import { i as readListArgs, n as filterArgs,
|
|
9
|
-
import { t as printList } from "./print-
|
|
10
|
-
import { t as withStore } from "./store-
|
|
7
|
+
import { i as readListArgs, n as filterArgs, s as refuseUnknownFlags } from "./flags-ByYBx-Xb.mjs";
|
|
8
|
+
import { t as printList } from "./print-Bd8MWVJk.mjs";
|
|
9
|
+
import { t as withStore } from "./store-2KXzMCEN.mjs";
|
|
11
10
|
import { homedir } from "node:os";
|
|
12
11
|
import { defineCommand } from "citty";
|
|
13
12
|
// src/follow-ups/command.ts
|
|
@@ -41,8 +40,7 @@ const placeOf = (store, key) => {
|
|
|
41
40
|
if (key === void 0) return placeFor(process.cwd(), projects);
|
|
42
41
|
const found = projects.find((p) => p.id === key || p.root === key);
|
|
43
42
|
if (found === void 0) {
|
|
44
|
-
|
|
45
|
-
fail(`No registered project \`${key}\`; registered: ${names.length === 0 ? "none" : names.join(", ")}.`);
|
|
43
|
+
fail(unknownProjectRefusal(key, projects.map((p) => p.id)));
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
46
|
return placeFor(found.root, projects);
|
|
@@ -112,8 +110,10 @@ const byFor = (a, b) => forOrder(a.for) - forOrder(b.for) || a.for.localeCompare
|
|
|
112
110
|
/** A multi-line text, each line safe to print. */
|
|
113
111
|
const linesOf = (text) => text.split("\n").map((line) => plain(line));
|
|
114
112
|
const when = (f) => f.ref === void 0 ? f.due : `${f.due} ${f.ref}`;
|
|
115
|
-
/** One follow-up on one line. */
|
|
116
|
-
const followUpLine = (f) => `${style("dim", f.id)} ${f.kind.padEnd(7)} ${plain(f.title)} ${style("dim", `(${plain(when(f))})`)}`;
|
|
113
|
+
/** One follow-up on one line; a short note when a run has taken it. */
|
|
114
|
+
const followUpLine = (f) => `${style("dim", f.id)} ${f.kind.padEnd(7)} ${plain(f.title)} ${style("dim", `(${plain(when(f))})`)}${f.takenBy === void 0 ? "" : style("dim", ` taken by ${plain(f.takenBy)}`)}`;
|
|
115
|
+
/** What to do with a draft: copy and send it, unless it is closed already. */
|
|
116
|
+
const draftHeading = (f, to) => f.status === "done" || f.status === "dropped" ? `Draft to ${to} (${f.status})` : `Draft to ${to}: copy it, send it, then \`ork follow-ups done ${f.id}\``;
|
|
117
117
|
/**
|
|
118
118
|
* The follow-ups grouped by status, then by `for`; the closed ones only with
|
|
119
119
|
* `all`. A line each, under a heading each.
|
|
@@ -139,13 +139,14 @@ const formatFollowUp = (f) => [
|
|
|
139
139
|
`${style("bold", plain(f.title))} ${style("dim", f.id)}`,
|
|
140
140
|
`status ${f.status}${f.note === void 0 ? "" : ` (${plain(f.note)})`}, for ${plain(f.for)}, ${f.kind}, due ${plain(f.due)}`,
|
|
141
141
|
...f.ref === void 0 ? [] : [`waits on ${plain(f.ref)}`],
|
|
142
|
+
...f.takenBy === void 0 ? [] : [`taken by run ${plain(f.takenBy)}`],
|
|
142
143
|
...fromLine(f),
|
|
143
144
|
...f.where.length === 0 ? [] : [`files ${f.where.map((file) => plain(file)).join(", ")}`],
|
|
144
145
|
...f.url === void 0 ? [] : [`kept at ${plain(f.url)}`],
|
|
145
146
|
...f.body === "" ? [] : ["", ...linesOf(f.body)],
|
|
146
147
|
...f.draft === void 0 ? [] : [
|
|
147
148
|
"",
|
|
148
|
-
style("bold",
|
|
149
|
+
style("bold", draftHeading(f, `${plain(f.draft.to)}${f.draft.channel === void 0 ? "" : ` (${plain(f.draft.channel)})`}`)),
|
|
149
150
|
...linesOf(f.draft.text).map((line) => ` ${line}`)
|
|
150
151
|
],
|
|
151
152
|
...f.steps === void 0 ? [] : [
|
|
@@ -419,6 +420,7 @@ const add = defineCommand({
|
|
|
419
420
|
const { followUp } = added;
|
|
420
421
|
write(`${style("green", "✓")} ${added.merged ? "merged into" : "added"} ${followUp.id}: ${plain(followUp.title)}`);
|
|
421
422
|
if (added.note !== void 0) writeErr(style("dim", added.note));
|
|
423
|
+
if (added.merged && input.draft !== void 0 && followUp.draft !== void 0 && input.draft.text !== followUp.draft.text) write(style("dim", ` the new --text was not kept: ${followUp.id} keeps its first draft (see \`ork follow-ups show ${followUp.id}\`)`));
|
|
422
424
|
if (relayed !== void 0) write(style("dim", ` sent to ${relayed.node} of run ${relayed.run}, which is live in that project`));
|
|
423
425
|
})
|
|
424
426
|
});
|
|
@@ -450,17 +452,21 @@ const followUps = defineCommand({
|
|
|
450
452
|
name: "list",
|
|
451
453
|
description: "List the follow-ups, due first, then by who they are for"
|
|
452
454
|
},
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
...allAlias
|
|
456
|
-
},
|
|
455
|
+
plugins: [refuseUnknownFlags],
|
|
456
|
+
args: filterArgs(followUpListTable),
|
|
457
457
|
run: async ({ args }) => {
|
|
458
458
|
const given = args;
|
|
459
|
-
const asked = readListArgs(followUpListTable, given
|
|
459
|
+
const asked = readListArgs(followUpListTable, given);
|
|
460
460
|
if (asked === void 0) return;
|
|
461
461
|
const { filter, limit } = asked;
|
|
462
462
|
const status = filter["status"];
|
|
463
463
|
await inProjects(filter["project"], (store, places) => {
|
|
464
|
+
const target = filter["for"]?.match(/^project:(.*)$/u)?.[1];
|
|
465
|
+
const ids = store.projects().map((p) => p.id);
|
|
466
|
+
if (target !== void 0 && !ids.includes(target)) {
|
|
467
|
+
fail(unknownProjectRefusal(target, ids));
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
464
470
|
const stored = places.flatMap((place) => store.followUps(place.root));
|
|
465
471
|
const shown = applyFilters(followUpListTable, stored, filter).filter((f) => status !== void 0 || OPEN.has(f.status)).toSorted((a, b) => RANK[a.status] - RANK[b.status] || byFor(a, b)).slice(0, limit);
|
|
466
472
|
printList(shown, followUpListTable, given, (rows) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { In as runStatus, Ln as slotWaitsOf, Mt as lockWaitLine, Nn as liveStatus, Or as maskSecrets, Pn as lockWaitsOf, Vn as isItem } from "./src-Ciy7i3se.mjs";
|
|
2
2
|
import { C as termOf, E as widthOf, n as cost, o as shortCount, r as costOf, s as style, w as cut } from "./output-B1kwo0YU.mjs";
|
|
3
3
|
import { i as statusChip, n as chip } from "./chips-BqcT58qr.mjs";
|
|
4
4
|
import { n as nodeVerdict } from "./verdict-CMvPCpWC.mjs";
|
|
@@ -193,13 +193,27 @@ const worth = (node) => {
|
|
|
193
193
|
return style("dim", ` ${node.unpriced === true ? tok : `${tok} · ${cost(node.costUsd)}`}`);
|
|
194
194
|
};
|
|
195
195
|
/**
|
|
196
|
+
* A crashed run's node that never ended: cut off when it had started, not run
|
|
197
|
+
* when it had not; `null` for one that ended.
|
|
198
|
+
*/
|
|
199
|
+
const crashedState = (node) => {
|
|
200
|
+
if (node.state === "running") return `${STATE_MARK.failed()} cut off`;
|
|
201
|
+
if (node.state === "waiting") return `${STATE_MARK.skipped()} not run`;
|
|
202
|
+
return null;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
196
205
|
* The ownership tree, one line per node, children under their owner. A node in
|
|
197
|
-
* `waits` (a live run's `lockWaitsOf`) reads `waiting for <lock>`, an item
|
|
198
|
-
*
|
|
206
|
+
* `waits` (a live run's `lockWaitsOf`) reads `waiting for <lock>`, an item too.
|
|
207
|
+
* In a `crashed` run, a node started but not ended reads `cut off` and one not
|
|
208
|
+
* started `not run`: nothing will end them.
|
|
199
209
|
*/
|
|
200
|
-
const formatTree = (roots, now = Date.now(), waits = []) => {
|
|
210
|
+
const formatTree = (roots, now = Date.now(), waits = [], crashed = false) => {
|
|
201
211
|
const lines = [];
|
|
202
|
-
const walk = (
|
|
212
|
+
const walk = (raw, lead, branch) => {
|
|
213
|
+
const node = raw.description === void 0 ? raw : {
|
|
214
|
+
...raw,
|
|
215
|
+
description: maskSecrets(raw.description)
|
|
216
|
+
};
|
|
203
217
|
const named = node.description === void 0 ? "" : ` ${node.description}`;
|
|
204
218
|
lines.push({
|
|
205
219
|
label: `${lead}${branch}${node.id}${named}`,
|
|
@@ -211,14 +225,15 @@ const formatTree = (roots, now = Date.now(), waits = []) => {
|
|
|
211
225
|
});
|
|
212
226
|
};
|
|
213
227
|
for (const root of roots) walk(root, "", "");
|
|
214
|
-
const label = Math.max(...lines.map((l) => l.label
|
|
228
|
+
const label = Math.max(...lines.map((l) => widthOf(l.label)));
|
|
215
229
|
const type = Math.max(...lines.map((l) => l.node.type.length));
|
|
216
230
|
return lines.map(({ label: text, node }) => {
|
|
217
231
|
const usage = worth(node);
|
|
218
232
|
const lock = waits.find((wait) => wait.id === node.id)?.lock;
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
|
|
233
|
+
const cut = crashed ? crashedState(node) : null;
|
|
234
|
+
const state = cut ?? (node.held === true ? `${style("yellow", "‖")} paused` : lock === void 0 ? `${STATE_MARK[node.state]()} ${stateText(node)}` : `${STATE_MARK.waiting()} waiting for ${lock}`);
|
|
235
|
+
const spent = isItem(node) && node.startedAt !== void 0 && cut === null ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
|
|
236
|
+
return `${node.description === void 0 ? text : `${text.slice(0, text.length - node.description.length)}${dim(node.description)}`}${" ".repeat(label - widthOf(text))} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
|
|
222
237
|
});
|
|
223
238
|
};
|
|
224
239
|
/**
|
|
@@ -227,7 +242,8 @@ const formatTree = (roots, now = Date.now(), waits = []) => {
|
|
|
227
242
|
* show; `events` are the run's, `roots` its nested tree.
|
|
228
243
|
*/
|
|
229
244
|
const formatTreeView = (run, events, roots, now = Date.now()) => {
|
|
230
|
-
const
|
|
231
|
-
|
|
245
|
+
const status = runStatus(run, events);
|
|
246
|
+
const waits = status === "running" ? lockWaitsOf(events) : [];
|
|
247
|
+
return [`${style("bold", `run ${run.id}`)} ${dim(`of ${run.workflow} · ${listedStatus(run, events)} · ${cost(run.costUsd)}`)}`, ...formatTree(roots, now, waits, status === "crashed")];
|
|
232
248
|
};
|
|
233
249
|
export { header as a, listedStatus as i, formatRuns as n, table as o, formatTreeView as r, ago as t };
|
|
@@ -54,9 +54,10 @@ fi
|
|
|
54
54
|
`,
|
|
55
55
|
bash: `# Tab completion for ork, in bash. Add to your ~/.bashrc:
|
|
56
56
|
# eval "$(ork completion bash)"
|
|
57
|
-
# (macOS's bash 3.2
|
|
58
|
-
#
|
|
59
|
-
#
|
|
57
|
+
# (macOS's bash 3.2 loads nothing from source <(…), and prints no error;
|
|
58
|
+
# saving it to a file and sourcing that works in every version.) Every
|
|
59
|
+
# expansion is quoted and lines are read with read -r, so a candidate is never
|
|
60
|
+
# split or glob-expanded.
|
|
60
61
|
|
|
61
62
|
_ork() {
|
|
62
63
|
local line="\${COMP_LINE:0:COMP_POINT}" cur files=0 strip="" out
|
|
@@ -65,10 +66,15 @@ _ork() {
|
|
|
65
66
|
if [[ "$line" == *[[:space:]] ]]; then words+=(""); fi
|
|
66
67
|
cur="\${words[\${#words[@]}-1]}"
|
|
67
68
|
COMPREPLY=()
|
|
68
|
-
# bash splits a word at ":" (ork:feature); offer only
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
# bash splits a word at ":" and "=" (ork:feature, --input=plan=); offer only
|
|
70
|
+
# what follows the last of them that is a word break.
|
|
71
|
+
local brk pre
|
|
72
|
+
for brk in : =; do
|
|
73
|
+
if [[ "$cur" == *"$brk"* && "$COMP_WORDBREAKS" == *"$brk"* ]]; then
|
|
74
|
+
pre="\${cur%"\${cur##*"$brk"}"}"
|
|
75
|
+
if (( \${#pre} > \${#strip} )); then strip="$pre"; fi
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
72
78
|
out="$("\${words[0]}" ${COMPLETE_COMMAND} -- "\${words[@]:1}" 2>/dev/null)"
|
|
73
79
|
while IFS= read -r line; do
|
|
74
80
|
case "$line" in
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { g as storeGuarded, h as runMarkerIn } from "./src-
|
|
1
|
+
import { z as storePath } from "./src-Ciy7i3se.mjs";
|
|
2
|
+
import { g as storeGuarded, h as runMarkerIn } from "./src-DP02RP9O.mjs";
|
|
3
3
|
// src/commands/in-a-run.ts
|
|
4
4
|
/**
|
|
5
5
|
* @file The commands that act as the user refuse a program or an agent in a run
|