@krapht/ork 1.0.0-alpha.1 → 1.0.0-alpha.10
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 +3 -4
- package/dist/{__build_info__-D9lSlMFy.mjs → __build_info__-DFF18K8y.mjs} +72 -3
- package/dist/bin.mjs +579 -379
- package/dist/{clean-CA_Oi2Wl.mjs → clean-DVaEH9e7.mjs} +5 -2
- package/dist/{completion-DBGfL-gN.mjs → completion-Dv5kf598.mjs} +6 -6
- package/dist/context-CEaKOVmG.mjs +683 -0
- package/dist/{decide-CoIJh34e.mjs → decide-3l9q8UtX.mjs} +3 -3
- package/dist/{find-rBBk7Jui.mjs → find-DEjjOlTG.mjs} +1 -1
- package/dist/format-Cb5_m-Qd.mjs +191 -0
- package/dist/{in-a-run-DdAakpRR.mjs → in-a-run-BS9Nc-JW.mjs} +1 -1
- package/dist/{limits-BPM9wUlV.mjs → limits-BSwEjQST.mjs} +1 -1
- package/dist/{list-Bv5Lxu7z.mjs → list-Qxr6rwsY.mjs} +11 -4
- package/dist/{mcp-DM30MFBw.mjs → mcp-CslnG5wf.mjs} +6 -6
- package/dist/output-Bqljtmp_.mjs +40 -0
- package/dist/{pause-C7lJkSma.mjs → pause-D7OVB5Ac.mjs} +4 -4
- package/dist/{relay-BipJYszF.mjs → relay-EceRIebO.mjs} +2 -2
- package/dist/{remove-BDs9isB6.mjs → remove-0IQZVWmI.mjs} +14 -7
- package/dist/{send-nbkjf3sc.mjs → send-C0uwfUYi.mjs} +3 -3
- package/dist/{src-hrj2h0IZ.mjs → src-Df6KOIDC.mjs} +5678 -3245
- package/dist/{src-BxUJPGa5.mjs → src-cmmXKHn1.mjs} +265 -203
- package/dist/{src-B0TT7DBi.mjs → src-i5k2Y-kf.mjs} +35 -86
- package/dist/src-oaJYIddp.mjs +2255 -0
- package/dist/ui/THIRD_PARTY_LICENSES.txt +594 -0
- package/dist/ui/assets/app-C8uhHVYp.css +1 -0
- package/dist/ui/assets/app-qs8Lofv6.js +75 -0
- package/dist/ui/assets/{index-CPx74lFf.js → index-DV1ZaOk-.js} +3 -3
- package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/{ui-B98FSboa.mjs → ui-CBkHJdqk.mjs} +4 -4
- package/dist/{usage-EW7DI6Z7.mjs → usage-Cd8pEYyK.mjs} +5 -5
- package/dist/{watch-BAkSGaOp.mjs → watch-DW5R2OPO.mjs} +6 -6
- package/dist/{workflows-vsEohqa7.mjs → workflows-BT1gDRIV.mjs} +4 -3
- package/dist/{worktrees-Ahj9rdWK.mjs → worktrees-g1MQMGie.mjs} +78 -19
- package/package.json +2 -4
- package/dist/context-6x3kXREO.mjs +0 -274
- package/dist/format-zMmhqyLE.mjs +0 -427
- package/dist/output-TV_j7Ni0.mjs +0 -950
- package/dist/src-B9v0lDc6.mjs +0 -1204
- package/dist/ui/assets/app-BNT0SuIz.css +0 -1
- package/dist/ui/assets/app-CmkynZyW.js +0 -67
- package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
@@ -69,7 +69,7 @@ const builtinWorkflowFile = (place, arg) => {
|
|
|
69
69
|
* An answer goes to the store (the first one wins), then the run is woken on
|
|
70
70
|
* its socket so it picks the answer up at once (specs/messaging.md).
|
|
71
71
|
*/
|
|
72
|
-
/** Open decisions whose run is still live, oldest first. */
|
|
72
|
+
/** Open decisions whose run is still live, oldest first, ANDing the filter. */
|
|
73
73
|
const pendingDecisions = (store, filter) => {
|
|
74
74
|
const live = /* @__PURE__ */ new Map();
|
|
75
75
|
return store.decisions({
|
|
@@ -78,7 +78,7 @@ const pendingDecisions = (store, filter) => {
|
|
|
78
78
|
}).filter((decision) => {
|
|
79
79
|
if (!live.has(decision.runId)) {
|
|
80
80
|
const [run] = store.lookup(decision.runId);
|
|
81
|
-
live.set(decision.runId, run !== void 0 && liveStatus(run) === "running");
|
|
81
|
+
live.set(decision.runId, run !== void 0 && liveStatus(run) === "running" && (filter.workflow === void 0 || run.workflow === filter.workflow));
|
|
82
82
|
}
|
|
83
83
|
return live.get(decision.runId) === true;
|
|
84
84
|
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Dt as isItem, bt as liveStatus } from "./src-Df6KOIDC.mjs";
|
|
2
|
+
import { i as style, n as cost } from "./output-Bqljtmp_.mjs";
|
|
3
|
+
// src/run/format-bus.ts
|
|
4
|
+
/** The first line of a message body, cut to fit one terminal line. */
|
|
5
|
+
const firstLine = (body) => {
|
|
6
|
+
const [line = ""] = body.trim().split("\n");
|
|
7
|
+
return line.length > 100 ? `${line.slice(0, 99)}…` : line;
|
|
8
|
+
};
|
|
9
|
+
/** Messages, owner questions and claims, without the node prefix. */
|
|
10
|
+
const formatBusEvent = (event) => {
|
|
11
|
+
if (event.type === "claim.taken") return [style("dim", `⚑ claimed ${event.resource}`)];
|
|
12
|
+
if (event.type === "claim.refused") {
|
|
13
|
+
const held = event.held === event.resource ? "" : ` (it holds ${event.held})`;
|
|
14
|
+
return [style("yellow", `⚑ ${event.resource} is ${event.holder}'s${held}`)];
|
|
15
|
+
}
|
|
16
|
+
if (event.type === "claim.released") return [style("dim", `⚑ released ${event.resource}`)];
|
|
17
|
+
if (event.type === "message.sent") {
|
|
18
|
+
const how = event.via === void 0 ? "" : ` (${event.via})`;
|
|
19
|
+
return [style("dim", `✉ to ${event.to}${how}: ${firstLine(event.body)}`)];
|
|
20
|
+
}
|
|
21
|
+
if (event.type === "message.received") {
|
|
22
|
+
const how = event.via === void 0 ? "" : ` (${event.via})`;
|
|
23
|
+
return [style("dim", `✉ from ${event.from}${how}: ${firstLine(event.body)}`)];
|
|
24
|
+
}
|
|
25
|
+
if (event.type === "call.started") return [style("dim", `↳ started ${event.workflow} as run ${event.run}`)];
|
|
26
|
+
if (event.type === "link.notice") return [style("dim", `↔ ${event.text}`)];
|
|
27
|
+
if (event.type === "message.read") return [];
|
|
28
|
+
if (event.type === "owner.asked") return [`${style(["yellow", "bold"], "?")} asked as the owner of ${event.about}: ${event.text} ${style("dim", `(${event.question})`)}`];
|
|
29
|
+
if (event.type === "owner.answered") return [style("dim", `→ ${event.answer} (owner)`)];
|
|
30
|
+
return [style("dim", "→ its session ended first; asking you")];
|
|
31
|
+
};
|
|
32
|
+
// src/run/items-ran.ts
|
|
33
|
+
/**
|
|
34
|
+
* @file How a fan-out's finished line counts its items: only the ones that
|
|
35
|
+
* started.
|
|
36
|
+
*/
|
|
37
|
+
/** How many of a fan-out's items started; a skipped one never did. */
|
|
38
|
+
const itemsRan = (count, statuses) => {
|
|
39
|
+
const ran = Array.isArray(statuses) ? statuses.filter((status) => status !== "skipped").length : count;
|
|
40
|
+
return ran === count ? `${String(count)} ran` : `${String(ran)} of ${String(count)} ran`;
|
|
41
|
+
};
|
|
42
|
+
// src/run/verdict.ts
|
|
43
|
+
/**
|
|
44
|
+
* @file The one-line verdict of a finished node: its report's status, unless
|
|
45
|
+
* the node failed or its work did not merge.
|
|
46
|
+
*/
|
|
47
|
+
/** Whether a finished node's work did not merge into its owner. */
|
|
48
|
+
const hasConflict = (output) => {
|
|
49
|
+
const { merge } = output;
|
|
50
|
+
return typeof merge === "object" && merge !== null && "ok" in merge && merge.ok === false;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A one-line verdict for a finished node, by what its type outputs. A node that
|
|
54
|
+
* failed reads `fail` even when its report said `pass` (a hook refused its
|
|
55
|
+
* commit); a child that did not merge says so.
|
|
56
|
+
*/
|
|
57
|
+
const nodeVerdict = (output, ok = true, error) => {
|
|
58
|
+
const { status, summary, exitCode, choice, answeredBy, count } = output;
|
|
59
|
+
if (typeof status === "string") {
|
|
60
|
+
if (!ok && (status === "pass" || status === "done")) return error === void 0 ? "fail" : `fail: ${firstLine(error)}`;
|
|
61
|
+
const label = hasConflict(output) ? `${status}, merge conflict` : status;
|
|
62
|
+
return typeof summary === "string" && summary !== "" ? `${label}: ${summary}` : label;
|
|
63
|
+
}
|
|
64
|
+
if (typeof exitCode === "number") return `exit ${String(exitCode)}`;
|
|
65
|
+
if (typeof choice === "string") return `${choice} (${String(answeredBy)})`;
|
|
66
|
+
if (typeof count === "number") return itemsRan(count, output["statuses"]);
|
|
67
|
+
return "done";
|
|
68
|
+
};
|
|
69
|
+
// src/runs/format.ts
|
|
70
|
+
/**
|
|
71
|
+
* @file How stored runs read on the terminal: the `ork ps` table and the `ork
|
|
72
|
+
* tree` ownership tree with each node's state, tokens and cost, and what is
|
|
73
|
+
* paused (ADR-041).
|
|
74
|
+
*/
|
|
75
|
+
const STATUS_STYLE = {
|
|
76
|
+
running: "cyan",
|
|
77
|
+
paused: "yellow",
|
|
78
|
+
succeeded: "green",
|
|
79
|
+
failed: "red",
|
|
80
|
+
crashed: "red",
|
|
81
|
+
budget_exhausted: "yellow",
|
|
82
|
+
quota_exhausted: "yellow",
|
|
83
|
+
timed_out: "yellow",
|
|
84
|
+
interrupted: "yellow"
|
|
85
|
+
};
|
|
86
|
+
/** `just now`, `5m ago`, `3h ago`, `2d ago`, then the date. */
|
|
87
|
+
const ago = (iso, now = Date.now()) => {
|
|
88
|
+
const minutes = Math.floor((now - Date.parse(iso)) / 6e4);
|
|
89
|
+
if (minutes < 1) return "just now";
|
|
90
|
+
if (minutes < 60) return `${String(minutes)}m ago`;
|
|
91
|
+
if (minutes < 1440) return `${String(Math.floor(minutes / 60))}h ago`;
|
|
92
|
+
if (minutes < 10080) return `${String(Math.floor(minutes / 1440))}d ago`;
|
|
93
|
+
return iso.slice(0, 10);
|
|
94
|
+
};
|
|
95
|
+
/** `42s`, `3m12s`, `1h05m`. */
|
|
96
|
+
const duration = (ms) => {
|
|
97
|
+
const s = Math.max(0, Math.round(ms / 1e3));
|
|
98
|
+
if (s < 60) return `${String(s)}s`;
|
|
99
|
+
if (s < 3600) return `${String(Math.floor(s / 60))}m${String(s % 60).padStart(2, "0")}s`;
|
|
100
|
+
return `${String(Math.floor(s / 3600))}h${String(Math.floor(s % 3600 / 60)).padStart(2, "0")}m`;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* The `ork ps` table; `withProject` adds each run's project. A called run's
|
|
104
|
+
* cost is dimmed and says its caller's, whose cost already counts it.
|
|
105
|
+
*/
|
|
106
|
+
const formatRuns = (runs, withProject, now = Date.now(), statusOf = liveStatus) => {
|
|
107
|
+
if (runs.length === 0) return ["No runs."];
|
|
108
|
+
const rows = runs.map((run) => {
|
|
109
|
+
const status = statusOf(run);
|
|
110
|
+
const end = run.finishedAt === void 0 ? now : Date.parse(run.finishedAt);
|
|
111
|
+
return {
|
|
112
|
+
id: run.id,
|
|
113
|
+
workflow: run.workflow,
|
|
114
|
+
status,
|
|
115
|
+
when: `${ago(run.startedAt, now)} · ${duration(end - Date.parse(run.startedAt))}`,
|
|
116
|
+
cost: run.parent === void 0 ? cost(run.costUsd) : `${cost(run.costUsd)} in ${run.parent}`,
|
|
117
|
+
called: run.parent !== void 0,
|
|
118
|
+
project: run.project
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
const width = (pick, title) => Math.max(title.length, ...rows.map((r) => pick(r).length));
|
|
122
|
+
const w = {
|
|
123
|
+
id: width((r) => r.id, "RUN"),
|
|
124
|
+
workflow: width((r) => r.workflow, "WORKFLOW"),
|
|
125
|
+
status: width((r) => r.status, "STATUS"),
|
|
126
|
+
when: width((r) => r.when, "STARTED"),
|
|
127
|
+
cost: width((r) => r.cost, "COST")
|
|
128
|
+
};
|
|
129
|
+
const header = [
|
|
130
|
+
"RUN".padEnd(w.id),
|
|
131
|
+
"WORKFLOW".padEnd(w.workflow),
|
|
132
|
+
"STATUS".padEnd(w.status),
|
|
133
|
+
"STARTED".padEnd(w.when),
|
|
134
|
+
"COST".padStart(w.cost),
|
|
135
|
+
...withProject ? ["PROJECT"] : []
|
|
136
|
+
].join(" ");
|
|
137
|
+
return [style("dim", header), ...rows.map((r) => [
|
|
138
|
+
style("bold", r.id.padEnd(w.id)),
|
|
139
|
+
r.workflow.padEnd(w.workflow),
|
|
140
|
+
style(STATUS_STYLE[r.status] ?? "reset", r.status.padEnd(w.status)),
|
|
141
|
+
style("dim", r.when.padEnd(w.when)),
|
|
142
|
+
r.called ? style("dim", r.cost.padStart(w.cost)) : r.cost.padStart(w.cost),
|
|
143
|
+
...withProject ? [style("dim", r.project)] : []
|
|
144
|
+
].join(" "))];
|
|
145
|
+
};
|
|
146
|
+
const STATE_MARK = {
|
|
147
|
+
waiting: style("dim", "·"),
|
|
148
|
+
running: style("cyan", "●"),
|
|
149
|
+
ok: style("green", "✓"),
|
|
150
|
+
failed: style("red", "✗"),
|
|
151
|
+
skipped: style("dim", "-")
|
|
152
|
+
};
|
|
153
|
+
/** `950`, `12.3k`, `1.2M`. */
|
|
154
|
+
const count = (n) => {
|
|
155
|
+
if (n < 1e3) return String(n);
|
|
156
|
+
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
157
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
158
|
+
};
|
|
159
|
+
const stateText = (node) => {
|
|
160
|
+
if (node.state === "running") return node.runs > 1 ? `running (round ${String(node.runs)})` : "running";
|
|
161
|
+
if (node.state === "ok" || node.state === "failed") {
|
|
162
|
+
const verdict = nodeVerdict(node.output ?? {}, node.state === "ok");
|
|
163
|
+
const line = verdict.length > 60 ? `${verdict.slice(0, 59)}…` : verdict;
|
|
164
|
+
return node.runs > 1 ? `${line} (${String(node.runs)} runs)` : line;
|
|
165
|
+
}
|
|
166
|
+
return node.state;
|
|
167
|
+
};
|
|
168
|
+
/** The ownership tree, one line per node, children under their owner. */
|
|
169
|
+
const formatTree = (roots, now = Date.now()) => {
|
|
170
|
+
const lines = [];
|
|
171
|
+
const walk = (node, lead, branch) => {
|
|
172
|
+
lines.push({
|
|
173
|
+
label: `${lead}${branch}${node.id}`,
|
|
174
|
+
node
|
|
175
|
+
});
|
|
176
|
+
const next = lead + (branch === "├─ " ? "│ " : branch === "" ? "" : " ");
|
|
177
|
+
node.children.forEach((child, index) => {
|
|
178
|
+
walk(child, next, index === node.children.length - 1 ? "└─ " : "├─ ");
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
for (const root of roots) walk(root, "", "");
|
|
182
|
+
const label = Math.max(...lines.map((l) => l.label.length));
|
|
183
|
+
const type = Math.max(...lines.map((l) => l.node.type.length));
|
|
184
|
+
return lines.map(({ label: text, node }) => {
|
|
185
|
+
const usage = node.inputTokens + node.outputTokens === 0 ? "" : style("dim", ` ${count(node.inputTokens)} in / ${count(node.outputTokens)} out · ${cost(node.costUsd)}`);
|
|
186
|
+
const state = node.held === true ? `${style("yellow", "‖")} paused` : `${STATE_MARK[node.state]} ${stateText(node)}`;
|
|
187
|
+
const spent = isItem(node) && node.startedAt !== void 0 ? style("dim", ` ${duration((node.finishedAt === void 0 ? now : Date.parse(node.finishedAt)) - Date.parse(node.startedAt))}`) : "";
|
|
188
|
+
return `${text.padEnd(label)} ${style("dim", node.type.padEnd(type))} ${state}${spent}${usage}`;
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
export { nodeVerdict as a, hasConflict as i, formatRuns as n, firstLine as o, formatTree as r, formatBusEvent as s, ago as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-
|
|
1
|
+
import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-Df6KOIDC.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 {
|
|
1
|
+
import { M as git, N as gitOk, P as lines } from "./src-Df6KOIDC.mjs";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
// ../core/src/worktree/list.ts
|
|
4
4
|
/**
|
|
@@ -56,7 +56,7 @@ const onNoBranch = async (entry) => entry === void 0 ? "0" : (await git(entry.pa
|
|
|
56
56
|
"--remotes"
|
|
57
57
|
])).stdout;
|
|
58
58
|
/** One created worktree or branch, with its state read from git. */
|
|
59
|
-
const stateOf = async (root, entries, item) => {
|
|
59
|
+
const stateOf = async (root, entries, item, recorded) => {
|
|
60
60
|
const { branch } = item;
|
|
61
61
|
const entry = branch === void 0 || item.path !== "" ? entries.find((e) => e.path === item.path) : entries.find((e) => e.branch === branch);
|
|
62
62
|
const holder = branch === void 0 ? void 0 : entries.find((e) => e.branch === branch && e.path !== entry?.path);
|
|
@@ -76,6 +76,7 @@ const stateOf = async (root, entries, item) => {
|
|
|
76
76
|
...checkedOut === void 0 ? {} : { checkedOut },
|
|
77
77
|
runId: item.runId,
|
|
78
78
|
nodeId: item.nodeId,
|
|
79
|
+
recorded,
|
|
79
80
|
path: entry?.path ?? null,
|
|
80
81
|
missing,
|
|
81
82
|
dirty: changes > 0,
|
|
@@ -104,7 +105,13 @@ const listOrkWorktrees = async (root, created = []) => {
|
|
|
104
105
|
"refs/heads/"
|
|
105
106
|
]));
|
|
106
107
|
const recordedBranches = new Set(created.flatMap((c) => c.branch ?? []));
|
|
107
|
-
const items = [...created.filter((c) => c.branch === void 0 ? entries.some((e) => e.path === c.path) : branches.includes(c.branch))
|
|
108
|
-
|
|
108
|
+
const items = [...created.filter((c) => c.branch === void 0 ? entries.some((e) => e.path === c.path) : branches.includes(c.branch)).map((item) => ({
|
|
109
|
+
item,
|
|
110
|
+
recorded: true
|
|
111
|
+
})), ...branches.filter((b) => b.startsWith("ork/") && !recordedBranches.has(b)).map((b) => ({
|
|
112
|
+
item: prefixed(b),
|
|
113
|
+
recorded: false
|
|
114
|
+
}))];
|
|
115
|
+
return Promise.all(items.map(({ item, recorded }) => stateOf(root, entries, item, recorded)));
|
|
109
116
|
};
|
|
110
117
|
export { listOrkWorktrees as n, createdWorktrees as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { h as isNewer } from "./src-
|
|
2
|
-
import { n as ORK_VERSION } from "./__build_info__-
|
|
3
|
-
import {
|
|
4
|
-
import { n as explainNode } from "./context-
|
|
5
|
-
import {
|
|
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";
|
|
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.
|
|
46
|
+
const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.10") ? tooOld(all, options.expects) : all;
|
|
47
47
|
const server = new McpServer({
|
|
48
48
|
name: "ork",
|
|
49
49
|
version: ORK_VERSION
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { stripVTControlCharacters, styleText } from "node:util";
|
|
3
|
+
// src/output.ts
|
|
4
|
+
/**
|
|
5
|
+
* @file Terminal output helpers: styled text that turns plain when stdout is
|
|
6
|
+
* not a TTY or `NO_COLOR` is set, and a yes-or-no question on the terminal.
|
|
7
|
+
*/
|
|
8
|
+
/** Styles text for stdout; plain when stdout is not a TTY or NO_COLOR is set. */
|
|
9
|
+
const style = (format, text) => styleText(format, text, { stream: process.stdout });
|
|
10
|
+
/**
|
|
11
|
+
* Text from outside, fit for a terminal: escape sequences could hide a line on
|
|
12
|
+
* the very screen a person judges from, and a line break could forge one.
|
|
13
|
+
*/
|
|
14
|
+
const plain = (text) => stripVTControlCharacters(text).replaceAll(/[\p{Cc}\p{Bidi_Control}]/gu, " ");
|
|
15
|
+
const write = (text) => {
|
|
16
|
+
process.stdout.write(`${text}\n`);
|
|
17
|
+
};
|
|
18
|
+
const writeErr = (text) => {
|
|
19
|
+
process.stderr.write(`${text}\n`);
|
|
20
|
+
};
|
|
21
|
+
/** What a run spent: `$0.0312`, or `$0.00` for nothing. */
|
|
22
|
+
const cost = (usd) => usd === 0 ? "$0.00" : `$${usd.toFixed(4)}`;
|
|
23
|
+
/** Asks a yes-or-no question on the terminal; anything but yes is no. */
|
|
24
|
+
const confirm = async (question) => {
|
|
25
|
+
const rl = createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stderr
|
|
28
|
+
});
|
|
29
|
+
const { promise: ended, resolve: end } = Promise.withResolvers();
|
|
30
|
+
rl.once("close", () => {
|
|
31
|
+
end("");
|
|
32
|
+
});
|
|
33
|
+
try {
|
|
34
|
+
const answer = await Promise.race([rl.question(question), ended]);
|
|
35
|
+
return ["y", "yes"].includes(answer.trim().toLowerCase());
|
|
36
|
+
} finally {
|
|
37
|
+
rl.close();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
export { write as a, style as i, cost as n, writeErr as o, plain as r, confirm as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as findRun } from "./find-
|
|
3
|
-
import { a as write,
|
|
4
|
-
import { r as continueRefusal } from "./in-a-run-
|
|
1
|
+
import { D as openStore, X as requestPause, Y as requestContinue } from "./src-Df6KOIDC.mjs";
|
|
2
|
+
import { t as findRun } from "./find-DEjjOlTG.mjs";
|
|
3
|
+
import { a as write, i as style, o as writeErr } from "./output-Bqljtmp_.mjs";
|
|
4
|
+
import { r as continueRefusal } from "./in-a-run-BS9Nc-JW.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
// src/commands/pause.ts
|
|
7
7
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as write,
|
|
1
|
+
import { D as openStore, V as addressOf, W as linkNodes, q as unlinkNodes } from "./src-Df6KOIDC.mjs";
|
|
2
|
+
import { a as write, i as style, o as writeErr } from "./output-Bqljtmp_.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
// src/commands/relay.ts
|
|
5
5
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as listOrkWorktrees, t as createdWorktrees } from "./list-
|
|
3
|
-
import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-
|
|
1
|
+
import { M as git, P as lines, bt as liveStatus } from "./src-Df6KOIDC.mjs";
|
|
2
|
+
import { n as listOrkWorktrees, t as createdWorktrees } from "./list-Qxr6rwsY.mjs";
|
|
3
|
+
import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-DVaEH9e7.mjs";
|
|
4
4
|
// ../core/src/worktree/words.ts
|
|
5
5
|
/**
|
|
6
6
|
* @file The plain words for what `ork worktrees` shows, in one place, so the
|
|
@@ -45,6 +45,7 @@ const stateParts = (item, into) => {
|
|
|
45
45
|
if (item.ahead > 0) part(`${counted(item.ahead, "commit")} on no branch`, true);
|
|
46
46
|
else if (item.path !== null && !item.missing) part("nothing committed");
|
|
47
47
|
}
|
|
48
|
+
if (!item.recorded) part("not created by Ork");
|
|
48
49
|
if (item.inMainCheckout) part("checked out in the main checkout");
|
|
49
50
|
if (item.heldBy !== void 0) part(`checked out in ${item.heldBy}`);
|
|
50
51
|
return parts;
|
|
@@ -67,11 +68,12 @@ const planLine = (step) => {
|
|
|
67
68
|
};
|
|
68
69
|
};
|
|
69
70
|
/**
|
|
70
|
-
* Why an item can't be removed at all, even forced, if it can't:
|
|
71
|
-
* checkout or a worktree Ork didn't create holds
|
|
72
|
-
* going.
|
|
71
|
+
* Why an item can't be removed at all, even forced, if it can't: no run
|
|
72
|
+
* recorded creating it, the main checkout or a worktree Ork didn't create holds
|
|
73
|
+
* it, or its run is still going.
|
|
73
74
|
*/
|
|
74
75
|
const removeRefusal = (item, live) => {
|
|
76
|
+
if (!item.recorded) return "not created by Ork (no run recorded it); delete it with git if it's yours to delete";
|
|
75
77
|
if (item.inMainCheckout) return "checked out in the main checkout; switch it to another branch first";
|
|
76
78
|
if (item.heldBy !== void 0) return `checked out in ${item.heldBy}, a worktree Ork didn't create; switch it to another branch first`;
|
|
77
79
|
return live ? `run ${item.runId} is still going; stop it first` : void 0;
|
|
@@ -101,6 +103,11 @@ const projectHead = async (root) => {
|
|
|
101
103
|
branch: answer.code === 0 ? answer.stdout.trim() : null
|
|
102
104
|
};
|
|
103
105
|
};
|
|
106
|
+
/** The top of the repository `cwd` is in, even one with no commit yet. */
|
|
107
|
+
const repoRoot = async (cwd) => {
|
|
108
|
+
const top = await git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
109
|
+
return top.code === 0 ? top.stdout.trim() : null;
|
|
110
|
+
};
|
|
104
111
|
/** What Ork created in the repo at `root`, from its runs' records and git. */
|
|
105
112
|
const readWorktrees = async (store, root) => ({
|
|
106
113
|
items: await listOrkWorktrees(root, createdWorktrees(store, root)),
|
|
@@ -191,4 +198,4 @@ const applyRemove = async (root, { step, force }, done = []) => {
|
|
|
191
198
|
await applyStep(root, step, force, done);
|
|
192
199
|
return done;
|
|
193
200
|
};
|
|
194
|
-
export { readWorktrees as a,
|
|
201
|
+
export { readWorktrees as a, mergeStateOf as c, stateParts as d, projectHead as i, planLine as l, lostWork as n, repoRoot as o, planRemove as r, labelOf as s, applyRemove as t, removeRefusal as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { u as inheritedScrubber } from "./src-
|
|
1
|
+
import { At as scriptContactsOf, D as openStore, an as socketPath, bt as liveStatus, sn as wakeRun } from "./src-Df6KOIDC.mjs";
|
|
2
|
+
import { o as writeErr } from "./output-Bqljtmp_.mjs";
|
|
3
|
+
import { u as inheritedScrubber } from "./src-cmmXKHn1.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
// ../core/src/remote/send.ts
|
|
6
6
|
/**
|