@krapht/ork 1.0.0-alpha.13 → 1.0.0-alpha.15
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__-Bsb1kjgF.mjs +340 -0
- package/dist/{store-CM3cERGl.mjs → attach-BHO3X3cf.mjs} +36 -91
- package/dist/{attach-E6NNyA3T.mjs → attach-CPespxlQ.mjs} +6 -5
- package/dist/bin.mjs +148 -1444
- package/dist/brief-za0ozN_E.mjs +58 -0
- package/dist/{builtin-files-n6o6oWpB.mjs → builtin-files-2bHf76x-.mjs} +1 -1
- package/dist/{chips-CyeB_rhR.mjs → chips-BqcT58qr.mjs} +1 -1
- package/dist/{completion-SNNNxnv9.mjs → completion-CQ2UYKuJ.mjs} +52 -11
- package/dist/context-BNqDw9z1.mjs +106 -0
- package/dist/{find-CjsNYAAf.mjs → find-BYLlszqj.mjs} +1 -1
- package/dist/follow-ups-B4GJ3Vzw.mjs +461 -0
- package/dist/{format-C60TFpRe.mjs → format-C8MsFj8_.mjs} +30 -16
- package/dist/{help-groups-CyfPWlST.mjs → help-groups-CQ98mLjF.mjs} +20 -6
- package/dist/{in-a-run-BYjTXaST.mjs → in-a-run-Bp0k0J09.mjs} +1 -1
- package/dist/{__build_info__-Da73_nBs.mjs → install-DWtXTq4y.mjs} +3 -227
- package/dist/{limits-C5QKZNLT.mjs → limits-B1qzS4cX.mjs} +1 -1
- package/dist/{mcp-bcctR-wc.mjs → mcp-BOrVkyhN.mjs} +6 -6
- package/dist/{once-DPQu5Q3H.mjs → once-C1fL3GVo.mjs} +4 -4
- package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
- package/dist/{output-CnRzrbUM.mjs → output-B1kwo0YU.mjs} +17 -1
- package/dist/{pause-BPvlDIvz.mjs → pause-DOMK-R8V.mjs} +4 -4
- package/dist/post-BzA6Qobq.mjs +44 -0
- package/dist/{relay-C8W0t_zc.mjs → relay-OWuu4fib.mjs} +2 -2
- package/dist/run--4-1xAF2.mjs +1451 -0
- package/dist/{run-CQIfq918.mjs → run-CiPsW9Kf.mjs} +1 -1
- package/dist/send-CnNNW-ot.mjs +105 -0
- package/dist/{src-CqRqsb2t.mjs → src-B9zWIVqo.mjs} +226 -44
- package/dist/{src-CGJGL1sZ.mjs → src-DQKz2eOH.mjs} +326 -81
- package/dist/{src-D93tHZ5B.mjs → src-DcrhgipG.mjs} +107 -47
- package/dist/{src-BeR6f70A.mjs → src-Dr_fTSu-.mjs} +5858 -3746
- package/dist/store-oKJ-cCf3.mjs +16 -0
- package/dist/ui/assets/app-Bdc6OBeZ.css +1 -0
- package/dist/ui/assets/app-Drp8Dm9y.js +111 -0
- package/dist/ui/assets/{index-CWwm8HTG.js → index-BtwkftKe.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-D2Ydofq0.mjs → ui-CzKlsCnm.mjs} +4 -4
- package/dist/{usage-6WIowblz.mjs → usage-DHGNk2JL.mjs} +13 -141
- package/dist/{context-DHudLA5e.mjs → validate-Bs_gg22g.mjs} +17 -107
- package/dist/{verdict-CIy8tAyX.mjs → verdict-CMvPCpWC.mjs} +2 -2
- package/dist/{watch-uOU_WeBI.mjs → watch-CCCfw3n7.mjs} +24 -8
- package/dist/{workflows-7tQdMnrA.mjs → workflows-CW28ZU-z.mjs} +3 -3
- package/dist/{worktrees-ZCII298F.mjs → worktrees-CyE8aF6j.mjs} +3 -5
- package/package.json +2 -2
- package/dist/clean-Bh4KZgzc.mjs +0 -91
- package/dist/decide-DuIHACvH.mjs +0 -50
- package/dist/list-BUb5Uc3H.mjs +0 -117
- package/dist/remove-BkFWGVYo.mjs +0 -201
- package/dist/report-CEN8VoUb.mjs +0 -140
- package/dist/send-CaZlGG5a.mjs +0 -163
- package/dist/ui/assets/app-DYRC0V4p.js +0 -111
- package/dist/ui/assets/app-QoHVi-mb.css +0 -1
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import { A as isNewer, j as parseVersion, k as compareVersions } from "./src-Dr_fTSu-.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
// ../core/src/follow-ups/change.ts
|
|
6
|
+
/** The project's follow-up whose id is, or starts with, `prefix`. */
|
|
7
|
+
const findFollowUp = (rows, project, prefix) => {
|
|
8
|
+
const exact = rows.followUp(project, prefix);
|
|
9
|
+
if (exact !== void 0) return {
|
|
10
|
+
ok: true,
|
|
11
|
+
followUp: exact
|
|
12
|
+
};
|
|
13
|
+
const matches = prefix === "" ? [] : rows.followUps(project).filter((f) => f.id.startsWith(prefix));
|
|
14
|
+
const [only, other] = matches;
|
|
15
|
+
if (only === void 0) return {
|
|
16
|
+
ok: false,
|
|
17
|
+
error: `No follow-up \`${prefix}\` in this project.`
|
|
18
|
+
};
|
|
19
|
+
if (other !== void 0) return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: `\`${prefix}\` matches ${matches.map((f) => f.id).join(", ")}; give more of the id.`
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
ok: true,
|
|
25
|
+
followUp: only
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/** Which statuses each action starts from, and where it goes. */
|
|
29
|
+
const ACTIONS = {
|
|
30
|
+
done: {
|
|
31
|
+
from: ["waiting", "due"],
|
|
32
|
+
to: () => "done",
|
|
33
|
+
note: "done"
|
|
34
|
+
},
|
|
35
|
+
drop: {
|
|
36
|
+
from: ["waiting", "due"],
|
|
37
|
+
to: () => "dropped",
|
|
38
|
+
note: "dropped"
|
|
39
|
+
},
|
|
40
|
+
reopen: {
|
|
41
|
+
from: ["done", "dropped"],
|
|
42
|
+
to: (f) => f.due === "now" ? "due" : "waiting",
|
|
43
|
+
note: "reopened"
|
|
44
|
+
},
|
|
45
|
+
due: {
|
|
46
|
+
from: ["waiting"],
|
|
47
|
+
to: () => "due",
|
|
48
|
+
note: "marked due"
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
/** Why `action` can't be done to `f` now, or `undefined`. */
|
|
52
|
+
const refusal = (f, action) => {
|
|
53
|
+
const rule = ACTIONS[action];
|
|
54
|
+
return rule.from.includes(f.status) ? void 0 : `${f.id} is ${f.status}, so it can't be ${rule.note}.`;
|
|
55
|
+
};
|
|
56
|
+
const check = (f, action) => {
|
|
57
|
+
const error = refusal(f, action);
|
|
58
|
+
return error === void 0 ? { ok: true } : {
|
|
59
|
+
ok: false,
|
|
60
|
+
why: `it is ${f.status}`,
|
|
61
|
+
error
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const postCheck = (f) => {
|
|
65
|
+
const open = f.status === "waiting" || f.status === "due";
|
|
66
|
+
if (f.kind !== "message" || f.draft?.channel !== "github") return {
|
|
67
|
+
ok: false,
|
|
68
|
+
why: "not drafted for the github channel",
|
|
69
|
+
error: "only a message drafted for the github channel is posted; send the others yourself"
|
|
70
|
+
};
|
|
71
|
+
return open ? { ok: true } : {
|
|
72
|
+
ok: false,
|
|
73
|
+
why: `it is ${f.status}`,
|
|
74
|
+
error: `${f.id} is ${f.status}, so it is not posted.`
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
const followUpActions = (f) => ({
|
|
78
|
+
done: check(f, "done"),
|
|
79
|
+
drop: check(f, "drop"),
|
|
80
|
+
reopen: check(f, "reopen"),
|
|
81
|
+
due: check(f, "due"),
|
|
82
|
+
post: postCheck(f)
|
|
83
|
+
});
|
|
84
|
+
/** Applies `action` to `followUp`, and keeps it. */
|
|
85
|
+
const changeFollowUp = (rows, followUp, action, now = () => (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
86
|
+
const rule = ACTIONS[action];
|
|
87
|
+
const refused = refusal(followUp, action);
|
|
88
|
+
if (refused !== void 0) return {
|
|
89
|
+
ok: false,
|
|
90
|
+
error: refused
|
|
91
|
+
};
|
|
92
|
+
const { note: _old, ...rest } = followUp;
|
|
93
|
+
const changed = {
|
|
94
|
+
...rest,
|
|
95
|
+
status: rule.to(followUp),
|
|
96
|
+
note: rule.note,
|
|
97
|
+
updatedAt: now()
|
|
98
|
+
};
|
|
99
|
+
rows.putFollowUp(changed);
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
followUp: changed
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Why a due follow-up is due, read from the note `due.ts` left: its ref merged,
|
|
107
|
+
* its date came, its ref closed unmerged, or a person marked it. `undefined`
|
|
108
|
+
* for one that is not due, or was due from the start.
|
|
109
|
+
*/
|
|
110
|
+
const dueCause = (f) => {
|
|
111
|
+
if (f.status !== "due" || f.note === void 0) return void 0;
|
|
112
|
+
if (f.note.includes("closed without merging")) return "closed-unmerged";
|
|
113
|
+
if (f.note.endsWith(" merged")) return "merged";
|
|
114
|
+
if (f.note.startsWith("due on ")) return "overdue";
|
|
115
|
+
return f.note === "marked due" ? "marked" : void 0;
|
|
116
|
+
};
|
|
117
|
+
// ../core/src/remote/session.ts
|
|
118
|
+
const chattable = (type) => type === "agent.leader" || type === "agent.worker";
|
|
119
|
+
/** Reads `node`'s session from a run's events, and every node's last output. */
|
|
120
|
+
const nodeSession = (events, node) => {
|
|
121
|
+
const session = {
|
|
122
|
+
known: false,
|
|
123
|
+
chattable: false,
|
|
124
|
+
running: false
|
|
125
|
+
};
|
|
126
|
+
const outputs = {};
|
|
127
|
+
for (const { event } of events) {
|
|
128
|
+
if (event.type === "run.started") {
|
|
129
|
+
const declared = event.nodes.find((n) => n.id === node);
|
|
130
|
+
if (declared !== void 0) {
|
|
131
|
+
session.known = true;
|
|
132
|
+
session.chattable = chattable(declared.type);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (event.type === "node.finished") outputs[event.id] = event.output;
|
|
136
|
+
if (!("id" in event) || event.id !== node) continue;
|
|
137
|
+
if (event.type === "node.started") {
|
|
138
|
+
session.known = true;
|
|
139
|
+
session.chattable = chattable(event.nodeType);
|
|
140
|
+
session.running = true;
|
|
141
|
+
}
|
|
142
|
+
if (event.type === "node.finished") session.running = false;
|
|
143
|
+
if (event.type === "worktree" && event.action === "created") session.checkout = {
|
|
144
|
+
path: event.path,
|
|
145
|
+
...event.branch === void 0 ? {} : { branch: event.branch },
|
|
146
|
+
own: true,
|
|
147
|
+
owner: node
|
|
148
|
+
};
|
|
149
|
+
if (event.type !== "node.event") continue;
|
|
150
|
+
const inner = event.event;
|
|
151
|
+
if (inner.fork === true) continue;
|
|
152
|
+
if (inner.type === "session.started") {
|
|
153
|
+
session.sessionId = inner.sessionId;
|
|
154
|
+
if (inner.cwd !== void 0) session.cwd = inner.cwd;
|
|
155
|
+
if (inner.driver !== void 0) session.driver = inner.driver;
|
|
156
|
+
if (inner.model !== "") session.model = inner.model;
|
|
157
|
+
}
|
|
158
|
+
if (inner.type === "session.finished" && inner.outcome.sessionId !== "") session.sessionId = inner.outcome.sessionId;
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
session,
|
|
162
|
+
outputs
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
/** The dist-tags every install follows, toward and after 1.0.0. */
|
|
166
|
+
const CHANNEL_TAGS = [
|
|
167
|
+
"latest",
|
|
168
|
+
"alpha",
|
|
169
|
+
"beta",
|
|
170
|
+
"rc"
|
|
171
|
+
];
|
|
172
|
+
/** The dist-tag of snapshots from `main`, followed only by a canary. */
|
|
173
|
+
const CANARY_TAG = "canary";
|
|
174
|
+
const updateCachePath = (home = homedir()) => path.join(home, ".ork", "update-check.json");
|
|
175
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176
|
+
const text = (record, key) => {
|
|
177
|
+
const value = record[key];
|
|
178
|
+
return typeof value === "string" ? value : void 0;
|
|
179
|
+
};
|
|
180
|
+
const notifiedOf = (value) => {
|
|
181
|
+
if (!isRecord(value)) return void 0;
|
|
182
|
+
const version = text(value, "version");
|
|
183
|
+
const at = text(value, "at");
|
|
184
|
+
return version === void 0 || at === void 0 ? void 0 : {
|
|
185
|
+
version,
|
|
186
|
+
at
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
/** The cache, or `null` when there is none or it cannot be read. */
|
|
190
|
+
const readUpdateCache = (file) => {
|
|
191
|
+
let parsed;
|
|
192
|
+
try {
|
|
193
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
194
|
+
} catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
if (!isRecord(parsed)) return null;
|
|
198
|
+
const checkedAt = text(parsed, "checkedAt");
|
|
199
|
+
if (checkedAt === void 0) return null;
|
|
200
|
+
const latest = text(parsed, "latest");
|
|
201
|
+
const notified = notifiedOf(parsed["notified"]);
|
|
202
|
+
return {
|
|
203
|
+
checkedAt,
|
|
204
|
+
...latest === void 0 ? {} : { latest },
|
|
205
|
+
...notified === void 0 ? {} : { notified }
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
/** Writes the cache whole (a temporary file, then a rename). */
|
|
209
|
+
const writeUpdateCache = (file, cache) => {
|
|
210
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
211
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
212
|
+
writeFileSync(temporary, `${JSON.stringify(cache, null, 2)}\n`);
|
|
213
|
+
renameSync(temporary, file);
|
|
214
|
+
};
|
|
215
|
+
const age = (iso, now) => {
|
|
216
|
+
const time = Date.parse(iso);
|
|
217
|
+
return Number.isNaN(time) ? Infinity : now.getTime() - time;
|
|
218
|
+
};
|
|
219
|
+
/** Whether it is time to ask the registry again. */
|
|
220
|
+
const checkDue = (cache, now) => cache === null || age(cache.checkedAt, now) >= 864e5;
|
|
221
|
+
/** Whether `version` is a canary snapshot: its pre-release starts `canary`. */
|
|
222
|
+
const isCanary = (version) => parseVersion(version)?.pre[0]?.startsWith(CANARY_TAG) === true;
|
|
223
|
+
/**
|
|
224
|
+
* The newest version, by semver precedence, among the dist-tags `current`
|
|
225
|
+
* follows: the channel tags, and `canary` too when `current` is a canary. A tag
|
|
226
|
+
* whose value doesn't parse is skipped; `null` when none does.
|
|
227
|
+
*/
|
|
228
|
+
const newestTagged = (tags, current) => {
|
|
229
|
+
return (current !== void 0 && isCanary(current) ? [...CHANNEL_TAGS, "canary"] : CHANNEL_TAGS).flatMap((tag) => {
|
|
230
|
+
const value = tags[tag];
|
|
231
|
+
const version = typeof value === "string" ? parseVersion(value) : null;
|
|
232
|
+
return typeof value === "string" && version !== null ? [{
|
|
233
|
+
value,
|
|
234
|
+
version
|
|
235
|
+
}] : [];
|
|
236
|
+
}).reduce((best, candidate) => best === null || compareVersions(candidate.version, best.version) > 0 ? candidate : best, null)?.value ?? null;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* The newest version on the registry that the running version follows ({@link
|
|
240
|
+
* newestTagged}), or `null` on any failure.
|
|
241
|
+
*/
|
|
242
|
+
const fetchLatest = async (name, options = {}) => {
|
|
243
|
+
const registry = (options.registry ?? "https://registry.npmjs.org").replace(/\/+$/u, "");
|
|
244
|
+
const get = options.fetch ?? globalThis.fetch;
|
|
245
|
+
try {
|
|
246
|
+
const response = await get(`${registry}/-/package/${name}/dist-tags`, {
|
|
247
|
+
headers: { accept: "application/json" },
|
|
248
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? 5e3)
|
|
249
|
+
});
|
|
250
|
+
if (!response.ok) return null;
|
|
251
|
+
const tags = await response.json();
|
|
252
|
+
return isRecord(tags) ? newestTagged(tags, options.current) : null;
|
|
253
|
+
} catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Asks the registry and records the answer. A failed check is recorded too, so
|
|
259
|
+
* an offline machine is not asked again until the interval passes; the last
|
|
260
|
+
* known version is kept. `options.current` is the running version.
|
|
261
|
+
*/
|
|
262
|
+
const refreshUpdateCache = async (file, name, now, options = {}) => {
|
|
263
|
+
const before = readUpdateCache(file);
|
|
264
|
+
const latest = await fetchLatest(name, options) ?? before?.latest;
|
|
265
|
+
const cache = {
|
|
266
|
+
...before,
|
|
267
|
+
checkedAt: now.toISOString(),
|
|
268
|
+
...latest === void 0 ? {} : { latest }
|
|
269
|
+
};
|
|
270
|
+
writeUpdateCache(file, cache);
|
|
271
|
+
return cache;
|
|
272
|
+
};
|
|
273
|
+
/** The newer version the cache knows of, if any. */
|
|
274
|
+
const newerVersion = (cache, current) => cache?.latest !== void 0 && isNewer(cache.latest, current) ? cache.latest : null;
|
|
275
|
+
/**
|
|
276
|
+
* The version to tell the user about now: newer than theirs, and not already
|
|
277
|
+
* told today. `null` when there is nothing to say.
|
|
278
|
+
*/
|
|
279
|
+
const pendingNotice = (cache, current, now) => {
|
|
280
|
+
const newer = newerVersion(cache, current);
|
|
281
|
+
if (newer === null) return null;
|
|
282
|
+
const told = cache?.notified;
|
|
283
|
+
return told?.version === newer && age(told.at, now) < 864e5 ? null : newer;
|
|
284
|
+
};
|
|
285
|
+
// ../core/src/update/switches.ts
|
|
286
|
+
/**
|
|
287
|
+
* @file The environment's say over the update check (ADR-038): the variables
|
|
288
|
+
* that turn it off (`ORK_NO_UPDATE_CHECK`, the common `NO_UPDATE_NOTIFIER`,
|
|
289
|
+
* and `CI`), and the registry to ask instead of npm's. The CLI adds its own
|
|
290
|
+
* rules about where a notice may go; the server behind `ork ui` uses only
|
|
291
|
+
* these.
|
|
292
|
+
*/
|
|
293
|
+
/** Turns the automatic check and its notice off. */
|
|
294
|
+
const NO_UPDATE_ENV = "ORK_NO_UPDATE_CHECK";
|
|
295
|
+
/** A registry to ask instead of npm's, e.g. a company mirror. */
|
|
296
|
+
const UPDATE_REGISTRY_ENV = "ORK_UPDATE_REGISTRY";
|
|
297
|
+
/** The variables that turn the check off, in the order they are looked at. */
|
|
298
|
+
const UPDATE_SWITCHES = [
|
|
299
|
+
NO_UPDATE_ENV,
|
|
300
|
+
"NO_UPDATE_NOTIFIER",
|
|
301
|
+
"CI"
|
|
302
|
+
];
|
|
303
|
+
/** Set to anything but empty, `0` or `false`. */
|
|
304
|
+
const isSwitchedOn = (value) => value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
305
|
+
/** The variable that turns the check off in `env`, or `null` when none does. */
|
|
306
|
+
const updateSwitchOff = (env) => UPDATE_SWITCHES.find((name) => isSwitchedOn(env[name])) ?? null;
|
|
307
|
+
/** The registry `ORK_UPDATE_REGISTRY` names, if it names one. */
|
|
308
|
+
const registryOf = (env) => {
|
|
309
|
+
const registry = env[UPDATE_REGISTRY_ENV];
|
|
310
|
+
return registry === void 0 || registry === "" ? void 0 : registry;
|
|
311
|
+
};
|
|
312
|
+
/** The host of a registry that isn't an http(s) URL, past any `user:pass@`. */
|
|
313
|
+
const hostOf = (registry) => registry.replace(/^[a-z][\w+.-]*:\/\//iu, "").replace(/^[^/?#]*@/u, "").split(/[/?#]/u)[0] ?? "";
|
|
314
|
+
/**
|
|
315
|
+
* The registry as it may be shown (on a page, in a message): an http(s) URL
|
|
316
|
+
* without its user name and password, which a mirror's URL may carry; anything
|
|
317
|
+
* else only as its host.
|
|
318
|
+
*/
|
|
319
|
+
const registryShown = (registry) => {
|
|
320
|
+
try {
|
|
321
|
+
const url = new URL(registry);
|
|
322
|
+
if (url.protocol === "http:" || url.protocol === "https:") {
|
|
323
|
+
if (url.username === "" && url.password === "") return registry;
|
|
324
|
+
url.username = "";
|
|
325
|
+
url.password = "";
|
|
326
|
+
return url.href;
|
|
327
|
+
}
|
|
328
|
+
} catch {}
|
|
329
|
+
return hostOf(registry);
|
|
330
|
+
};
|
|
331
|
+
// src/__build_info__.ts
|
|
332
|
+
/**
|
|
333
|
+
* @file The published package's name and version, bound to
|
|
334
|
+
* `packages/cli/package.json`. Do not edit by hand: `pnpm changesets:version`
|
|
335
|
+
* and `pnpm build` rewrite it (`tools/release/build-info.ts`), and a test
|
|
336
|
+
* fails when it is stale.
|
|
337
|
+
*/
|
|
338
|
+
const ORK_NAME = "@krapht/ork";
|
|
339
|
+
const ORK_VERSION = "1.0.0-alpha.15";
|
|
340
|
+
export { dueCause as _, registryShown as a, fetchLatest as c, readUpdateCache as d, refreshUpdateCache as f, changeFollowUp as g, nodeSession as h, registryOf as i, newerVersion as l, writeUpdateCache as m, ORK_VERSION as n, updateSwitchOff as o, updateCachePath as p, isSwitchedOn as r, checkDue as s, ORK_NAME as t, pendingNotice as u, findFollowUp as v, followUpActions as y };
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
5
|
-
import { a as createView, i as formatBusEvent, n as nodeVerdict, o as styler, r as firstLine, t as hasConflict } from "./verdict-CIy8tAyX.mjs";
|
|
1
|
+
import { Bn as outcomeEnd, H as isNodeEvent, Hn as END_KINDS, Lt as lockWaitLine, Ut as slotWaitLine, _t as followEvents, ct as followRunTree, fr as maskSecrets, lt as answerDecision, ut as pendingDecisions, zn as endOf } from "./src-Dr_fTSu-.mjs";
|
|
2
|
+
import { C as termOf, D as wrap, E as widthOf, O as readKeys, _ as interrupt, a as plain, b as canKey, g as createRegion, h as paint, m as bold, n as cost, p as apply, r as costOf, s as style, u as confirmOrStop, v as readLine, w as cut, x as plainEnv, y as withdrawn } from "./output-B1kwo0YU.mjs";
|
|
3
|
+
import { n as chip, r as nodeChip, t as CHIP_LABELS } from "./chips-BqcT58qr.mjs";
|
|
4
|
+
import { a as createView, i as formatBusEvent, n as nodeVerdict, o as styler, r as firstLine, t as hasConflict } from "./verdict-CMvPCpWC.mjs";
|
|
6
5
|
import { createInterface } from "node:readline/promises";
|
|
7
6
|
// ../core/src/control/queue.ts
|
|
8
7
|
const PERMISSION_OPTIONS = [{
|
|
@@ -219,66 +218,6 @@ const createDecisionQueue = (options) => {
|
|
|
219
218
|
}
|
|
220
219
|
};
|
|
221
220
|
};
|
|
222
|
-
// ../core/src/remote/follow-tree.ts
|
|
223
|
-
/**
|
|
224
|
-
* Calls `onEvent` for each event of `root` and of the runs it called, once
|
|
225
|
-
* each. A called run's stored events come right after the `call.started` that
|
|
226
|
-
* started it; live events as they arrive. Ends when the root ends or `signal`
|
|
227
|
-
* aborts.
|
|
228
|
-
*/
|
|
229
|
-
const followRunTree = async (store, root, onEvent, options = {}) => {
|
|
230
|
-
/** Every run followed, with its call path, in the order found. */
|
|
231
|
-
const paths = /* @__PURE__ */ new Map([[root, []]]);
|
|
232
|
-
/** Each run's last event seen, for the catch-up once the root ends. */
|
|
233
|
-
const seqs = /* @__PURE__ */ new Map();
|
|
234
|
-
const followers = [];
|
|
235
|
-
/** Ends the called runs' following once the root's has ended. */
|
|
236
|
-
const halt = new AbortController();
|
|
237
|
-
const signal = options.signal === void 0 ? halt.signal : AbortSignal.any([options.signal, halt.signal]);
|
|
238
|
-
const handle = (run, path) => (stored) => {
|
|
239
|
-
seqs.set(run, stored.seq);
|
|
240
|
-
onEvent({
|
|
241
|
-
run,
|
|
242
|
-
path,
|
|
243
|
-
stored
|
|
244
|
-
});
|
|
245
|
-
const { event } = stored;
|
|
246
|
-
if (event.type === "call.started" && !paths.has(event.run)) follow(event.run, [...path, event.node]);
|
|
247
|
-
};
|
|
248
|
-
const follow = (run, path) => {
|
|
249
|
-
paths.set(run, path);
|
|
250
|
-
followers.push(followEvents(store, run, handle(run, path), {
|
|
251
|
-
...options,
|
|
252
|
-
follow: true,
|
|
253
|
-
after: seqs.get(run) ?? 0,
|
|
254
|
-
signal
|
|
255
|
-
}));
|
|
256
|
-
};
|
|
257
|
-
/** Waits for every follower, including those started while waiting. */
|
|
258
|
-
const settle = async () => {
|
|
259
|
-
while (followers.length > 0) await Promise.all(followers.splice(0));
|
|
260
|
-
};
|
|
261
|
-
const end = await followEvents(store, root, handle(root, []), {
|
|
262
|
-
...options,
|
|
263
|
-
follow: true
|
|
264
|
-
});
|
|
265
|
-
halt.abort();
|
|
266
|
-
await settle();
|
|
267
|
-
if (end !== "aborted") {
|
|
268
|
-
for (const [run, path] of [...paths].slice(1)) followers.push(followEvents(store, run, handle(run, path), {
|
|
269
|
-
follow: false,
|
|
270
|
-
after: seqs.get(run) ?? 0
|
|
271
|
-
}));
|
|
272
|
-
await settle();
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
end,
|
|
276
|
-
stillRunning: [...paths.keys()].slice(1).filter((id) => {
|
|
277
|
-
const [run] = store.lookup(id);
|
|
278
|
-
return run !== void 0 && liveStatus(run) === "running";
|
|
279
|
-
})
|
|
280
|
-
};
|
|
281
|
-
};
|
|
282
221
|
// src/human-card.ts
|
|
283
222
|
/** Lines of context a card shows before `d` (or `--verbose`) shows the rest. */
|
|
284
223
|
const CONTEXT_LINES = 6;
|
|
@@ -830,8 +769,9 @@ const createTerminalHuman = (io) => {
|
|
|
830
769
|
/**
|
|
831
770
|
* @file How a node's end reads on the terminal (specs/runtime/session-ends.md):
|
|
832
771
|
* its kind's label and Ork's own message, a note for a kind that may pass,
|
|
833
|
-
* the setup hint for an account or provider end,
|
|
834
|
-
* only when asked for. Never
|
|
772
|
+
* the setup hint for an account or provider end, the last line of a failed
|
|
773
|
+
* tool's own stderr, and the scrubbed raw detail only when asked for. Never
|
|
774
|
+
* the provider's own words first.
|
|
835
775
|
*/
|
|
836
776
|
/** What a transient kind says in place of its hint on the log line. */
|
|
837
777
|
const MAY_PASS = "(may pass; ork resume retries)";
|
|
@@ -840,11 +780,17 @@ const endHeadline = (end) => {
|
|
|
840
780
|
const { label, transient } = END_KINDS[end.kind];
|
|
841
781
|
return `${label}: ${end.message}${transient ? ` ${MAY_PASS}` : ""}`;
|
|
842
782
|
};
|
|
783
|
+
/** The longest a script's last stderr line is shown on the log line. */
|
|
784
|
+
const LAST_LINE_LIMIT = 200;
|
|
785
|
+
/** The last non-empty line of a scrubbed detail, cut; "" when there is none. */
|
|
786
|
+
const lastLine = (detail) => (detail ?? "").split("\n").map((line) => line.trim()).findLast((line) => line !== "")?.slice(0, LAST_LINE_LIMIT) ?? "";
|
|
843
787
|
/**
|
|
844
788
|
* The lines under a headline: the hint for an account or provider end (what to
|
|
845
789
|
* set up) or whenever `full` (`--verbose`, one node's log), and the detail only
|
|
846
790
|
* when `full`. A transient kind's hint is its retry wording, which the headline
|
|
847
|
-
* already says, so it is not printed twice.
|
|
791
|
+
* already says, so it is not printed twice. A failed tool (`command_failed`)
|
|
792
|
+
* also shows the last line of its detail without `full`: a script's stderr is
|
|
793
|
+
* the workflow's own message, not the provider's.
|
|
848
794
|
*/
|
|
849
795
|
const endLines = (end, full, term = createView().term) => {
|
|
850
796
|
const style = styler(term);
|
|
@@ -853,6 +799,10 @@ const endLines = (end, full, term = createView().term) => {
|
|
|
853
799
|
const lines = [];
|
|
854
800
|
if (hint !== "" && !transient && (full || setup)) lines.push(` ${style("dim", hint)}`);
|
|
855
801
|
if (full && end.detail !== void 0) lines.push(style("dim", " details:"), ...end.detail.split("\n").map((line) => style("dim", ` ${line}`)));
|
|
802
|
+
else if (end.kind === "command_failed") {
|
|
803
|
+
const last = lastLine(end.detail);
|
|
804
|
+
if (last !== "") lines.push(` ${style("dim", last)}`);
|
|
805
|
+
}
|
|
856
806
|
return lines;
|
|
857
807
|
};
|
|
858
808
|
/**
|
|
@@ -869,6 +819,21 @@ const formatFinished = (event, tag, verbose, term = createView().term, rest = ta
|
|
|
869
819
|
if (!(!event.ok && end.kind !== "reported_failure" && (event.end !== void 0 || end.kind !== "unknown"))) return [`${tag} ${mark} ${nodeVerdict(event.output, event.ok, event.error)}${spent}`];
|
|
870
820
|
return [`${tag} ${mark} ${endHeadline(end)}${spent}`, ...endLines(end, verbose, term).map((line) => `${rest} ${line}`)];
|
|
871
821
|
};
|
|
822
|
+
// src/run/format-machine.ts
|
|
823
|
+
/**
|
|
824
|
+
* @file How a node's waits for the machine read in `ork logs`: a slot for a
|
|
825
|
+
* heavy command (ADR-080) and a lock (RFC-016), each naming who holds it.
|
|
826
|
+
*/
|
|
827
|
+
/** Whether `event` is a slot's or a lock's. */
|
|
828
|
+
const isMachineEvent = (event) => event.type.startsWith("slot.") || event.type.startsWith("lock.");
|
|
829
|
+
/** A slot or lock event's line, after its node's tag. */
|
|
830
|
+
const machineLine = (event, style) => {
|
|
831
|
+
if (event.type === "slot.waiting") return style("yellow", `‖ ${slotWaitLine(event.holders)}`);
|
|
832
|
+
if (event.type === "slot.taken") return style("dim", "▸ got a check slot");
|
|
833
|
+
if (event.type === "lock.waiting") return style("yellow", `‖ ${lockWaitLine(event.lock, event.holders)}`);
|
|
834
|
+
if (event.type === "lock.taken") return style("dim", `▸ holds ${event.lock}`);
|
|
835
|
+
return style("dim", `▸ let ${event.lock} go`);
|
|
836
|
+
};
|
|
872
837
|
// src/run/format-outcome.ts
|
|
873
838
|
/**
|
|
874
839
|
* @file How a finished session reads on the terminal: its report or its end,
|
|
@@ -972,7 +937,7 @@ const formatRunResult = (result, term = createView().term) => {
|
|
|
972
937
|
const ran = result.nodes.filter((n) => n.runs > 0).length;
|
|
973
938
|
const skipped = result.skipped ?? result.nodes.length - ran;
|
|
974
939
|
const handled = result.failuresHandled ?? 0;
|
|
975
|
-
const lines = ["", `${mark} run ${result.runId} ${result.status.replace("_", " ")} ${style("dim", `· ${
|
|
940
|
+
const lines = ["", `${mark} run ${result.runId} ${result.status.replace("_", " ")} ${style("dim", `· ${costOf(result.costUsd, result.partial, result.tokens)} · ${String(ran)} ran, ${String(skipped)} skipped${handled === 0 ? "" : `, ${failuresHandled(handled)}`}`)}`];
|
|
976
941
|
for (const kept of result.worktrees) lines.push(` ${kept.id}: ${kept.branch ?? "detached"} ${style("dim", `at ${kept.path} (${kept.reason})`)}`);
|
|
977
942
|
for (const reason of result.reasons) lines.push(` ${style("red", reason)}`);
|
|
978
943
|
for (const { node, messages } of result.unread ?? []) lines.push(` ${style(["yellow", "bold"], "warning")} ${node} never ran, so ${messages === 1 ? "1 message" : `${String(messages)} messages`} to it went unread`);
|
|
@@ -1025,11 +990,6 @@ const toolLines = (name, input, view, room, verbose, node) => {
|
|
|
1025
990
|
return [`${styledHead}${style("dim", cut(first.trim(), Math.max(1, room - headWidth - more.length)))}${style("dim", more)}`];
|
|
1026
991
|
};
|
|
1027
992
|
// src/run/format.ts
|
|
1028
|
-
/**
|
|
1029
|
-
* @file How runs read on the terminal: session events and run events with node
|
|
1030
|
-
* chips or prefixes; the node result is `format-outcome.ts`'s, a tool call
|
|
1031
|
-
* `format-tool.ts`'s and the run summary `format-result.ts`'s.
|
|
1032
|
-
*/
|
|
1033
993
|
/** A line's 2-column indent, after any style codes that open it. */
|
|
1034
994
|
const INDENT = /^((?:\u001B\[[0-9;]*m)*) {2}/u;
|
|
1035
995
|
/** Who answered a decision, as the log says it. */
|
|
@@ -1126,8 +1086,7 @@ const formatRunEvent = (event, width, tools, terminal = false, verbose = false,
|
|
|
1126
1086
|
return [`${tag(event.id)} ${style("yellow", `‖ paused: ${at}`)}`];
|
|
1127
1087
|
}
|
|
1128
1088
|
if (event.type === "node.released") return [`${tag(event.id)} ${style("dim", "▸ continues")}`];
|
|
1129
|
-
if (event
|
|
1130
|
-
if (event.type === "slot.taken") return [`${tag(event.id)} ${style("dim", "▸ got a check slot")}`];
|
|
1089
|
+
if (isMachineEvent(event)) return [`${tag(event.id)} ${machineLine(event, style)}`];
|
|
1131
1090
|
if (event.type === "verify.skipped") return [`${tag(event.id)} ${style("dim", `✓ ${skippedLine(event)}`)}`];
|
|
1132
1091
|
if (event.type === "each.listed") {
|
|
1133
1092
|
const round = event.round > 1 ? ` (round ${String(event.round)})` : "";
|
|
@@ -1348,18 +1307,4 @@ const attachRun = async (store, root, out, options = {}) => {
|
|
|
1348
1307
|
exitCode: end === "aborted" || seen.succeeded ? 0 : 1
|
|
1349
1308
|
};
|
|
1350
1309
|
};
|
|
1351
|
-
|
|
1352
|
-
/**
|
|
1353
|
-
* @file Opening the user's store (`~/.ork/state.db`) for the length of one
|
|
1354
|
-
* command, and closing it after, whatever happens.
|
|
1355
|
-
*/
|
|
1356
|
-
/** Runs `body` with the user's store open, and closes it after. */
|
|
1357
|
-
const withStore = async (body) => {
|
|
1358
|
-
const store = openStore();
|
|
1359
|
-
try {
|
|
1360
|
-
await body(store);
|
|
1361
|
-
} finally {
|
|
1362
|
-
store.close();
|
|
1363
|
-
}
|
|
1364
|
-
};
|
|
1365
|
-
export { unknownNode as a, callsShown as c, createDecisionQueue as d, followRun as i, createTerminalHuman as l, attachRun as n, formatEvent as o, askStored as r, formatRunEvent as s, withStore as t, readText as u };
|
|
1310
|
+
export { formatEvent as a, createTerminalHuman as c, unknownNode as i, readText as l, askStored as n, formatRunEvent as o, followRun as r, callsShown as s, attachRun as t, createDecisionQueue as u };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import "./src-
|
|
2
|
-
import {
|
|
3
|
-
import { t as findRun } from "./find-
|
|
4
|
-
import {
|
|
5
|
-
import { n as attachRefusal } from "./in-a-run-
|
|
1
|
+
import "./src-Dr_fTSu-.mjs";
|
|
2
|
+
import { c as createTerminalHuman, t as attachRun } from "./attach-BHO3X3cf.mjs";
|
|
3
|
+
import { t as findRun } from "./find-BYLlszqj.mjs";
|
|
4
|
+
import { c as write, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
|
|
5
|
+
import { n as attachRefusal } from "./in-a-run-Bp0k0J09.mjs";
|
|
6
|
+
import { t as withStore } from "./store-oKJ-cCf3.mjs";
|
|
6
7
|
import { defineCommand } from "citty";
|
|
7
8
|
// src/commands/attach.ts
|
|
8
9
|
/**
|