@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
|
@@ -1,83 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { n as
|
|
7
|
-
import { a as planClean, i as keyOf, o as stepActs, r as cleanStep, s as stepPrunes, t as applyClean } from "./clean-Bh4KZgzc.mjs";
|
|
8
|
-
import { a as readWorktrees, c as mergeStateOf, d as stateParts, i as projectHead, l as planLine, n as lostWork, r as planRemove, s as labelOf, t as applyRemove, u as removeRefusal } from "./remove-BkFWGVYo.mjs";
|
|
1
|
+
import { At as walkSummary, Br as WorkflowEditSchema, C as capsHere, Cn as liveStatus, Ct as explainWorkflowText, D as runningScript, Dr as placeFor, E as runningRunIds, En as runStatus, Er as placeFiles, H as isNodeEvent, Hn as END_KINDS, Hr as newWorkflowText, In as loadProject, Jn as planPost, Jr as parseWorkflow, Mn as nestItems, Mr as inside, N as triggersReport, O as detectInstaller, Ot as checkWorkflowText, Pr as LESSON_CAP_TOKENS, S as usageReport, Sn as tellLiveRun, Sr as loadAgents, T as releaseNotes, Tn as pausedOf, U as openStore, Ur as readDocument, V as activeTree, Vr as applyEdits, W as storePath, Xn as sendPost, Yn as runGh, Zr as EdgeInputSchema, _ as keyOf, _n as linkNodes, _t as followEvents, a as planRemove, b as stepPrunes, bn as requestPause, bt as limitsReport, d as planLine, ei as expandEdge, en as nodeSays, f as removeRefusal, g as cleanStep, gt as detachRun, h as applyClean, i as lostWork, jr as workflowDirOf, kn as processAlive, kr as runDirOf, kt as walkChecks, l as labelOf, ln as startFollowUpChecks, lt as answerDecision, mn as DEFAULT_FOLLOW_UPS_RULE, mt as resumable, ni as NodeSchema, o as projectHead, on as checkFollowUpsDue, or as readCondition, p as stateParts, pn as liveSessionOf, pt as detachResume, qn as DISCUSSIONS, qr as renderJsonSchema, r as applyRemove, s as readWorktrees, u as mergeStateOf, ut as pendingDecisions, v as planClean, vn as unlinkNodes, w as planUpgrade, xn as requestStop, y as stepActs, yn as requestContinue, zn as endOf, zr as skillsIn } from "./src-Dr_fTSu-.mjs";
|
|
2
|
+
import { a as acceptItem, c as feedbackActions, i as recordNote, l as openFixRun, n as previewPost, o as lessonPlaceOf, s as placeOf$1, t as runInstall } from "./install-DWtXTq4y.mjs";
|
|
3
|
+
import { _ as dueCause, a as registryShown, c as fetchLatest, d as readUpdateCache, f as refreshUpdateCache, g as changeFollowUp, h as nodeSession, i as registryOf, l as newerVersion, m as writeUpdateCache, o as updateSwitchOff, p as updateCachePath, s as checkDue, v as findFollowUp, y as followUpActions } from "./__build_info__-Bsb1kjgF.mjs";
|
|
4
|
+
import { t as postFollowUpMessage } from "./post-BzA6Qobq.mjs";
|
|
5
|
+
import { t as builtinProblem } from "./builtin-files-2bHf76x-.mjs";
|
|
6
|
+
import { n as newRunId, t as findRun } from "./find-BYLlszqj.mjs";
|
|
9
7
|
import path from "node:path";
|
|
10
8
|
import { z } from "zod";
|
|
11
9
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
12
10
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
13
11
|
import { homedir } from "node:os";
|
|
14
12
|
import { createServer } from "node:http";
|
|
15
|
-
// ../server/src/contract-builder.ts
|
|
16
|
-
/** A new workflow's name, as its file and its `name:` take it. */
|
|
17
|
-
const NewWorkflowSchema = z.strictObject({ name: z.string().min(1) });
|
|
18
|
-
/** The server checks each edit; this file imports only types of the schema. */
|
|
19
|
-
const WorkflowEditsSchema = z.strictObject({ edits: z.array(z.custom()).min(1) });
|
|
20
|
-
/** Earlier text, and the file it was based on, to ask before replacing it. */
|
|
21
|
-
const DraftTextSchema = z.strictObject({
|
|
22
|
-
text: z.string(),
|
|
23
|
-
baseHash: z.string().nullable()
|
|
24
|
-
});
|
|
25
|
-
const SaveWorkflowSchema = z.strictObject({
|
|
26
|
-
/** Write over a file that changed since this draft began. */
|
|
27
|
-
force: z.boolean().optional() });
|
|
28
|
-
const BUILDER_API = {
|
|
29
|
-
builderSchema: {
|
|
30
|
-
method: "GET",
|
|
31
|
-
path: "/api/workflow-schema"
|
|
32
|
-
},
|
|
33
|
-
builderCatalog: {
|
|
34
|
-
method: "GET",
|
|
35
|
-
path: "/api/projects/:id/catalog"
|
|
36
|
-
},
|
|
37
|
-
builderDrafts: {
|
|
38
|
-
method: "GET",
|
|
39
|
-
path: "/api/projects/:id/builder"
|
|
40
|
-
},
|
|
41
|
-
builderView: {
|
|
42
|
-
method: "GET",
|
|
43
|
-
path: "/api/projects/:id/builder/:name"
|
|
44
|
-
},
|
|
45
|
-
builderCreate: {
|
|
46
|
-
method: "POST",
|
|
47
|
-
path: "/api/projects/:id/builder",
|
|
48
|
-
body: NewWorkflowSchema
|
|
49
|
-
},
|
|
50
|
-
builderEdit: {
|
|
51
|
-
method: "POST",
|
|
52
|
-
path: "/api/projects/:id/builder/:name/edits",
|
|
53
|
-
body: WorkflowEditsSchema
|
|
54
|
-
},
|
|
55
|
-
builderRestore: {
|
|
56
|
-
method: "POST",
|
|
57
|
-
path: "/api/projects/:id/builder/:name/text",
|
|
58
|
-
body: DraftTextSchema
|
|
59
|
-
},
|
|
60
|
-
builderDiscard: {
|
|
61
|
-
method: "POST",
|
|
62
|
-
path: "/api/projects/:id/builder/:name/discard"
|
|
63
|
-
},
|
|
64
|
-
builderSave: {
|
|
65
|
-
method: "POST",
|
|
66
|
-
path: "/api/projects/:id/builder/:name/save",
|
|
67
|
-
body: SaveWorkflowSchema
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
// ../server/src/contract-context.ts
|
|
71
|
-
const CONTEXT_API = {
|
|
72
|
-
runContext: {
|
|
73
|
-
method: "GET",
|
|
74
|
-
path: "/api/runs/:id/nodes/:node/context"
|
|
75
|
-
},
|
|
76
|
-
builderContext: {
|
|
77
|
-
method: "GET",
|
|
78
|
-
path: "/api/projects/:id/builder/:name/nodes/:node/context"
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
13
|
// ../server/src/contract-feedback.ts
|
|
82
14
|
const NoteSchema = z.strictObject({
|
|
83
15
|
/** Its first line is the title. */
|
|
@@ -157,6 +89,133 @@ const FEEDBACK_API = {
|
|
|
157
89
|
path: "/api/feedback/:id/split"
|
|
158
90
|
}
|
|
159
91
|
};
|
|
92
|
+
// ../server/src/contract-follow-ups.ts
|
|
93
|
+
/** `open` is due and waiting, the default; `all` adds done and dropped. */
|
|
94
|
+
const FollowUpsQuerySchema = z.strictObject({
|
|
95
|
+
status: z.enum([
|
|
96
|
+
"open",
|
|
97
|
+
"all",
|
|
98
|
+
"due",
|
|
99
|
+
"waiting",
|
|
100
|
+
"done",
|
|
101
|
+
"dropped"
|
|
102
|
+
]).optional(),
|
|
103
|
+
/** Only those for this: `agent`, `me`, `project:<name>` or `person:<name>`. */
|
|
104
|
+
for: z.string().min(1).optional()
|
|
105
|
+
});
|
|
106
|
+
const FollowUpChangeSchema = z.strictObject({ action: z.enum([
|
|
107
|
+
"done",
|
|
108
|
+
"drop",
|
|
109
|
+
"reopen",
|
|
110
|
+
"due"
|
|
111
|
+
]) });
|
|
112
|
+
/**
|
|
113
|
+
* Posts a drafted GitHub comment through the person's own `gh`. `confirm` says
|
|
114
|
+
* yes, and `digest` is the detail's `draftDigest`, which stands for the
|
|
115
|
+
* recipient and text they saw: the route refuses when the draft is no longer
|
|
116
|
+
* that, so nothing else is sent.
|
|
117
|
+
*/
|
|
118
|
+
const FollowUpPostSchema = z.strictObject({
|
|
119
|
+
confirm: z.literal(true),
|
|
120
|
+
digest: z.string().min(1)
|
|
121
|
+
});
|
|
122
|
+
const FOLLOW_UPS_API = {
|
|
123
|
+
followUps: {
|
|
124
|
+
method: "GET",
|
|
125
|
+
path: "/api/projects/:id/follow-ups",
|
|
126
|
+
query: FollowUpsQuerySchema
|
|
127
|
+
},
|
|
128
|
+
followUp: {
|
|
129
|
+
method: "GET",
|
|
130
|
+
path: "/api/projects/:id/follow-ups/:followUp"
|
|
131
|
+
},
|
|
132
|
+
followUpChange: {
|
|
133
|
+
method: "POST",
|
|
134
|
+
path: "/api/projects/:id/follow-ups/:followUp",
|
|
135
|
+
body: FollowUpChangeSchema
|
|
136
|
+
},
|
|
137
|
+
followUpPost: {
|
|
138
|
+
method: "POST",
|
|
139
|
+
path: "/api/projects/:id/follow-ups/:followUp/post",
|
|
140
|
+
body: FollowUpPostSchema
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
// ../server/src/contract-after.ts
|
|
144
|
+
/**
|
|
145
|
+
* @file What the API offers after runs: the feedback loop's routes
|
|
146
|
+
* (contract-feedback.ts) and the follow-ups' (contract-follow-ups.ts), joined
|
|
147
|
+
* so the contract reads one part for both.
|
|
148
|
+
*/
|
|
149
|
+
const AFTER_API = {
|
|
150
|
+
...FEEDBACK_API,
|
|
151
|
+
...FOLLOW_UPS_API
|
|
152
|
+
};
|
|
153
|
+
// ../server/src/contract-builder.ts
|
|
154
|
+
/** A new workflow's name, as its file and its `name:` take it. */
|
|
155
|
+
const NewWorkflowSchema = z.strictObject({ name: z.string().min(1) });
|
|
156
|
+
/** The server checks each edit; this file imports only types of the schema. */
|
|
157
|
+
const WorkflowEditsSchema = z.strictObject({ edits: z.array(z.custom()).min(1) });
|
|
158
|
+
/** Earlier text, and the file it was based on, to ask before replacing it. */
|
|
159
|
+
const DraftTextSchema = z.strictObject({
|
|
160
|
+
text: z.string(),
|
|
161
|
+
baseHash: z.string().nullable()
|
|
162
|
+
});
|
|
163
|
+
const SaveWorkflowSchema = z.strictObject({
|
|
164
|
+
/** Write over a file that changed since this draft began. */
|
|
165
|
+
force: z.boolean().optional() });
|
|
166
|
+
const BUILDER_API = {
|
|
167
|
+
builderSchema: {
|
|
168
|
+
method: "GET",
|
|
169
|
+
path: "/api/workflow-schema"
|
|
170
|
+
},
|
|
171
|
+
builderCatalog: {
|
|
172
|
+
method: "GET",
|
|
173
|
+
path: "/api/projects/:id/catalog"
|
|
174
|
+
},
|
|
175
|
+
builderDrafts: {
|
|
176
|
+
method: "GET",
|
|
177
|
+
path: "/api/projects/:id/builder"
|
|
178
|
+
},
|
|
179
|
+
builderView: {
|
|
180
|
+
method: "GET",
|
|
181
|
+
path: "/api/projects/:id/builder/:name"
|
|
182
|
+
},
|
|
183
|
+
builderCreate: {
|
|
184
|
+
method: "POST",
|
|
185
|
+
path: "/api/projects/:id/builder",
|
|
186
|
+
body: NewWorkflowSchema
|
|
187
|
+
},
|
|
188
|
+
builderEdit: {
|
|
189
|
+
method: "POST",
|
|
190
|
+
path: "/api/projects/:id/builder/:name/edits",
|
|
191
|
+
body: WorkflowEditsSchema
|
|
192
|
+
},
|
|
193
|
+
builderRestore: {
|
|
194
|
+
method: "POST",
|
|
195
|
+
path: "/api/projects/:id/builder/:name/text",
|
|
196
|
+
body: DraftTextSchema
|
|
197
|
+
},
|
|
198
|
+
builderDiscard: {
|
|
199
|
+
method: "POST",
|
|
200
|
+
path: "/api/projects/:id/builder/:name/discard"
|
|
201
|
+
},
|
|
202
|
+
builderSave: {
|
|
203
|
+
method: "POST",
|
|
204
|
+
path: "/api/projects/:id/builder/:name/save",
|
|
205
|
+
body: SaveWorkflowSchema
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
// ../server/src/contract-context.ts
|
|
209
|
+
const CONTEXT_API = {
|
|
210
|
+
runContext: {
|
|
211
|
+
method: "GET",
|
|
212
|
+
path: "/api/runs/:id/nodes/:node/context"
|
|
213
|
+
},
|
|
214
|
+
builderContext: {
|
|
215
|
+
method: "GET",
|
|
216
|
+
path: "/api/projects/:id/builder/:name/nodes/:node/context"
|
|
217
|
+
}
|
|
218
|
+
};
|
|
160
219
|
// ../server/src/contract-links.ts
|
|
161
220
|
/**
|
|
162
221
|
* @file The routes that link two nodes of live runs (RFC-003): list, link and
|
|
@@ -324,7 +383,7 @@ const API = {
|
|
|
324
383
|
},
|
|
325
384
|
...BUILDER_API,
|
|
326
385
|
...CONTEXT_API,
|
|
327
|
-
...
|
|
386
|
+
...AFTER_API,
|
|
328
387
|
...WORKTREES_API,
|
|
329
388
|
...UPDATE_API,
|
|
330
389
|
projects: {
|
|
@@ -448,7 +507,8 @@ const workflowGraph = (workflow) => ({
|
|
|
448
507
|
maxRounds
|
|
449
508
|
})) : [];
|
|
450
509
|
}),
|
|
451
|
-
channels: (workflow.messages ?? []).map(({ between: [a, b] }) => [a, b])
|
|
510
|
+
channels: (workflow.messages ?? []).map(({ between: [a, b] }) => [a, b]),
|
|
511
|
+
...workflow.limits?.budgetTokens === void 0 ? {} : { budgetTokens: workflow.limits.budgetTokens }
|
|
452
512
|
});
|
|
453
513
|
/** The graph of a workflow file, or `null` if it is gone or does not parse. */
|
|
454
514
|
const readWorkflowGraph = (file) => {
|
|
@@ -475,7 +535,8 @@ const runGraph = (events, file) => {
|
|
|
475
535
|
})),
|
|
476
536
|
edges: edges ?? onFile?.edges ?? [],
|
|
477
537
|
channels: channels ?? onFile?.channels ?? [],
|
|
478
|
-
...started?.type === "run.started" && started.budgetUsd !== void 0 ? { budgetUsd: started.budgetUsd } : {}
|
|
538
|
+
...started?.type === "run.started" && started.budgetUsd !== void 0 ? { budgetUsd: started.budgetUsd } : {},
|
|
539
|
+
...onFile?.budgetTokens === void 0 ? {} : { budgetTokens: onFile.budgetTokens }
|
|
479
540
|
};
|
|
480
541
|
};
|
|
481
542
|
// ../server/src/guards.ts
|
|
@@ -2023,7 +2084,7 @@ const applyRoute = async (context, item, driver) => {
|
|
|
2023
2084
|
* workflows would go and how big that file is, the project a fix would be
|
|
2024
2085
|
* proposed in, and the run proposing one now, if any.
|
|
2025
2086
|
*/
|
|
2026
|
-
const detailOf = (store, item) => {
|
|
2087
|
+
const detailOf$1 = (store, item) => {
|
|
2027
2088
|
const registered = store.projects();
|
|
2028
2089
|
const fixRun = openFixRun(store, item);
|
|
2029
2090
|
const actions = feedbackActions(item, fixRun);
|
|
@@ -2126,7 +2187,7 @@ const feedbackRoutes = (context) => {
|
|
|
2126
2187
|
feedbackItem: ({ params }) => {
|
|
2127
2188
|
const found = itemOf(store, params.id);
|
|
2128
2189
|
if (!found.ok) return found.refusal;
|
|
2129
|
-
return ok(detailOf(store, found.item));
|
|
2190
|
+
return ok(detailOf$1(store, found.item));
|
|
2130
2191
|
},
|
|
2131
2192
|
feedbackStatus: ({ params, body }) => {
|
|
2132
2193
|
const found = itemOf(store, params.id);
|
|
@@ -2175,6 +2236,172 @@ const feedbackRoutes = (context) => {
|
|
|
2175
2236
|
}
|
|
2176
2237
|
};
|
|
2177
2238
|
};
|
|
2239
|
+
// ../server/src/follow-up-routes.ts
|
|
2240
|
+
/**
|
|
2241
|
+
* @file The follow-ups' routes (specs/follow-ups.md), through the same core
|
|
2242
|
+
* functions as `ork follow-ups`: a project's list (after the due check, which
|
|
2243
|
+
* reads pull requests through the person's own `gh` and only reports a `gh`
|
|
2244
|
+
* that fails), one follow-up with what to copy and what can be done to it,
|
|
2245
|
+
* done, drop, reopen and due, and posting a drafted GitHub comment only with
|
|
2246
|
+
* the person's confirmation of the draft they saw (its digest). Ork never
|
|
2247
|
+
* runs a task's commands.
|
|
2248
|
+
*/
|
|
2249
|
+
const OPEN = ["due", "waiting"];
|
|
2250
|
+
/** Stands for what a person saw of a draft: its recipient and text. */
|
|
2251
|
+
const digestOf$1 = (f) => f.draft === void 0 ? void 0 : createHash("sha256").update(JSON.stringify([f.draft.to, f.draft.text])).digest("hex");
|
|
2252
|
+
/** The place a project's rule keeps follow-ups in. */
|
|
2253
|
+
const ruleOf = (project) => (loadProject(project.root, placeFiles(project).rules).project?.followUps ?? DEFAULT_FOLLOW_UPS_RULE).store;
|
|
2254
|
+
/** Why a follow-up isn't where the project's rule says, when it isn't. */
|
|
2255
|
+
const fallbackOf = (placed, rule) => {
|
|
2256
|
+
if (placed === rule) return void 0;
|
|
2257
|
+
return rule === "github" ? "GitHub couldn't be used when it was added (gh not signed in, or the issue failed), so Ork's store keeps it." : `It couldn't be written to ${rule} when it was added, so Ork's store keeps it.`;
|
|
2258
|
+
};
|
|
2259
|
+
const deliveryOf = (store, f) => {
|
|
2260
|
+
if (!f.for.startsWith("project:")) return void 0;
|
|
2261
|
+
const name = f.for.slice(8).trim();
|
|
2262
|
+
const target = store.projects().find((p) => p.id === name);
|
|
2263
|
+
if (target === void 0) return {
|
|
2264
|
+
project: name,
|
|
2265
|
+
registered: false
|
|
2266
|
+
};
|
|
2267
|
+
const live = liveSessionOf(store, target.root);
|
|
2268
|
+
return {
|
|
2269
|
+
project: name,
|
|
2270
|
+
registered: true,
|
|
2271
|
+
root: target.root,
|
|
2272
|
+
keptIn: ruleOf(target),
|
|
2273
|
+
...live === void 0 ? {} : { live }
|
|
2274
|
+
};
|
|
2275
|
+
};
|
|
2276
|
+
/** `f`, what can be done to it, where it is kept, and what to copy from it. */
|
|
2277
|
+
const detailOf = (context, project, f) => {
|
|
2278
|
+
const rule = ruleOf(project);
|
|
2279
|
+
const fallback = fallbackOf(f.place, rule);
|
|
2280
|
+
const cause = dueCause(f);
|
|
2281
|
+
const delivery = deliveryOf(context.store, f);
|
|
2282
|
+
const digest = digestOf$1(f);
|
|
2283
|
+
return {
|
|
2284
|
+
followUp: f,
|
|
2285
|
+
actions: followUpActions(f),
|
|
2286
|
+
...cause === void 0 ? {} : { cause },
|
|
2287
|
+
keptIn: {
|
|
2288
|
+
place: f.place,
|
|
2289
|
+
rule,
|
|
2290
|
+
...f.url === void 0 ? {} : { url: f.url },
|
|
2291
|
+
...fallback === void 0 ? {} : { fallback }
|
|
2292
|
+
},
|
|
2293
|
+
...delivery === void 0 ? {} : { delivery },
|
|
2294
|
+
...digest === void 0 ? {} : { draftDigest: digest },
|
|
2295
|
+
copy: {
|
|
2296
|
+
text: f.draft?.text ?? "",
|
|
2297
|
+
commands: (f.steps ?? []).flatMap((step) => step.command === void 0 ? [] : [step.command])
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
};
|
|
2301
|
+
const toItem = (f) => ({
|
|
2302
|
+
id: f.id,
|
|
2303
|
+
project: f.project,
|
|
2304
|
+
title: f.title,
|
|
2305
|
+
for: f.for,
|
|
2306
|
+
kind: f.kind,
|
|
2307
|
+
...f.note === void 0 ? {} : { note: f.note }
|
|
2308
|
+
});
|
|
2309
|
+
/** The due follow-ups of `roots`, most recently changed first. */
|
|
2310
|
+
const dueOf = (store, roots) => {
|
|
2311
|
+
const due = roots.flatMap((root) => store.followUps(root)).filter((f) => f.status === "due").toSorted((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
2312
|
+
return {
|
|
2313
|
+
count: due.length,
|
|
2314
|
+
items: due.slice(0, 20).map((f) => toItem(f))
|
|
2315
|
+
};
|
|
2316
|
+
};
|
|
2317
|
+
const found = (context, params) => {
|
|
2318
|
+
const project = projectOf(context, params.id);
|
|
2319
|
+
if (project === void 0) return {
|
|
2320
|
+
ok: false,
|
|
2321
|
+
refusal: fail(404, `No registered project \`${params.id}\`.`)
|
|
2322
|
+
};
|
|
2323
|
+
if (params.followUp === void 0) return {
|
|
2324
|
+
ok: true,
|
|
2325
|
+
project
|
|
2326
|
+
};
|
|
2327
|
+
const one = findFollowUp(context.store, project.root, params.followUp);
|
|
2328
|
+
if (!one.ok) {
|
|
2329
|
+
const status = one.error.startsWith("No follow-up") ? 404 : 400;
|
|
2330
|
+
return {
|
|
2331
|
+
ok: false,
|
|
2332
|
+
refusal: fail(status, one.error)
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
return {
|
|
2336
|
+
ok: true,
|
|
2337
|
+
project,
|
|
2338
|
+
followUp: one.followUp
|
|
2339
|
+
};
|
|
2340
|
+
};
|
|
2341
|
+
const followUpRoutes = (context) => {
|
|
2342
|
+
const { store } = context;
|
|
2343
|
+
const gh = context.gh ?? runGh;
|
|
2344
|
+
return {
|
|
2345
|
+
followUps: async ({ params, query }) => {
|
|
2346
|
+
const one = found(context, params);
|
|
2347
|
+
if (!one.ok) return one.refusal;
|
|
2348
|
+
const { root } = one.project;
|
|
2349
|
+
const errors = await checkFollowUpsDue({
|
|
2350
|
+
rows: store,
|
|
2351
|
+
project: root,
|
|
2352
|
+
gh
|
|
2353
|
+
}).then((checked) => checked.errors).catch((error) => [error instanceof Error ? error.message : String(error)]);
|
|
2354
|
+
const all = store.followUps(root);
|
|
2355
|
+
const status = query.status ?? "open";
|
|
2356
|
+
const shown = all.filter((f) => {
|
|
2357
|
+
if (query.for !== void 0 && f.for !== query.for) return false;
|
|
2358
|
+
if (status === "all") return true;
|
|
2359
|
+
if (status === "open") return OPEN.some((s) => s === f.status);
|
|
2360
|
+
return f.status === status;
|
|
2361
|
+
});
|
|
2362
|
+
return ok({
|
|
2363
|
+
project: root,
|
|
2364
|
+
followUps: shown,
|
|
2365
|
+
due: all.filter((f) => f.status === "due").length,
|
|
2366
|
+
checker: {
|
|
2367
|
+
available: errors.length === 0,
|
|
2368
|
+
errors
|
|
2369
|
+
}
|
|
2370
|
+
});
|
|
2371
|
+
},
|
|
2372
|
+
followUp: ({ params }) => {
|
|
2373
|
+
const one = found(context, params);
|
|
2374
|
+
if (!one.ok) return one.refusal;
|
|
2375
|
+
return one.followUp === void 0 ? fail(404, "No such follow-up.") : ok(detailOf(context, one.project, one.followUp));
|
|
2376
|
+
},
|
|
2377
|
+
followUpChange: ({ params, body }) => {
|
|
2378
|
+
const one = found(context, params);
|
|
2379
|
+
if (!one.ok) return one.refusal;
|
|
2380
|
+
if (one.followUp === void 0) return fail(404, "No such follow-up.");
|
|
2381
|
+
const changed = changeFollowUp(store, one.followUp, body.action);
|
|
2382
|
+
return changed.ok ? ok(detailOf(context, one.project, changed.followUp)) : fail(409, changed.error);
|
|
2383
|
+
},
|
|
2384
|
+
followUpPost: async ({ params, body }) => {
|
|
2385
|
+
const one = found(context, params);
|
|
2386
|
+
if (!one.ok) return one.refusal;
|
|
2387
|
+
const { followUp } = one;
|
|
2388
|
+
if (followUp === void 0) return fail(404, "No such follow-up.");
|
|
2389
|
+
const check = followUpActions(followUp).post;
|
|
2390
|
+
if (!check.ok) return fail(409, check.error);
|
|
2391
|
+
if (digestOf$1(followUp) !== body.digest) return fail(409, "The draft is no longer the one you confirmed; open the follow-up again.");
|
|
2392
|
+
const posted = await postFollowUpMessage(followUp, {
|
|
2393
|
+
rows: store,
|
|
2394
|
+
gh,
|
|
2395
|
+
confirmed: body.confirm,
|
|
2396
|
+
home: context.home ?? homedir()
|
|
2397
|
+
});
|
|
2398
|
+
return posted.ok ? ok({
|
|
2399
|
+
...posted.url === void 0 ? {} : { url: posted.url },
|
|
2400
|
+
followUp: detailOf(context, one.project, posted.followUp)
|
|
2401
|
+
}) : fail(409, posted.error);
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
};
|
|
2178
2405
|
const ANSI = /\u001B\[[0-?]*[ -/]*[@-~]|\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)|\u001B[@-Z\\-_]/gu;
|
|
2179
2406
|
/** A line as a terminal would end up showing it. */
|
|
2180
2407
|
const plainLine = (raw) => (raw.replaceAll(ANSI, "").split("\r").findLast((part) => part !== "") ?? "").replaceAll(/\s+$/gu, "");
|
|
@@ -2729,6 +2956,7 @@ const apiRoutes = (context, port) => ({
|
|
|
2729
2956
|
...builderRoutes(context),
|
|
2730
2957
|
...contextRoutes(context),
|
|
2731
2958
|
...feedbackRoutes(context),
|
|
2959
|
+
...followUpRoutes(context),
|
|
2732
2960
|
...worktreesRoutes(context),
|
|
2733
2961
|
...updateRoutes(context, port)
|
|
2734
2962
|
});
|
|
@@ -2887,7 +3115,11 @@ const overviewStream = (context, res, url, shutdown) => {
|
|
|
2887
3115
|
const last = {
|
|
2888
3116
|
runs: "",
|
|
2889
3117
|
decisions: "",
|
|
2890
|
-
version: ""
|
|
3118
|
+
version: "",
|
|
3119
|
+
followUps: JSON.stringify({
|
|
3120
|
+
count: 0,
|
|
3121
|
+
items: []
|
|
3122
|
+
})
|
|
2891
3123
|
};
|
|
2892
3124
|
const tick = () => {
|
|
2893
3125
|
const filter = root === void 0 ? {} : { project: root };
|
|
@@ -2902,7 +3134,8 @@ const overviewStream = (context, res, url, shutdown) => {
|
|
|
2902
3134
|
for (const [name, value] of [
|
|
2903
3135
|
["runs", runs],
|
|
2904
3136
|
["decisions", decisions],
|
|
2905
|
-
["version", context.updates.info()]
|
|
3137
|
+
["version", context.updates.info()],
|
|
3138
|
+
["followUps", dueOf(context.store, root === void 0 ? context.store.projects().map((p) => p.root) : [root])]
|
|
2906
3139
|
]) {
|
|
2907
3140
|
const text = JSON.stringify(value);
|
|
2908
3141
|
if (text === last[name]) continue;
|
|
@@ -3060,6 +3293,8 @@ const runningServer = (file) => {
|
|
|
3060
3293
|
*/
|
|
3061
3294
|
/** The port `ork ui` serves on unless told otherwise. */
|
|
3062
3295
|
const DEFAULT_PORT = 4417;
|
|
3296
|
+
/** The overview stream sends the due follow-ups itself; nothing more here. */
|
|
3297
|
+
const noticed = () => {};
|
|
3063
3298
|
const listen = (server, port) => new Promise((resolve, reject) => {
|
|
3064
3299
|
server.once("error", (error) => {
|
|
3065
3300
|
reject(error.code === "EADDRINUSE" ? /* @__PURE__ */ new Error(`Port ${String(port)} is in use; start Ork's UI on another with \`--port <n>\`.`) : error);
|
|
@@ -3121,6 +3356,15 @@ const startServer = async (options) => {
|
|
|
3121
3356
|
token,
|
|
3122
3357
|
startedAt
|
|
3123
3358
|
});
|
|
3359
|
+
const stopChecks = startFollowUpChecks({
|
|
3360
|
+
rows: store,
|
|
3361
|
+
gh: options.gh ?? runGh,
|
|
3362
|
+
projects: () => store.projects().map((project) => project.root),
|
|
3363
|
+
onDue: noticed,
|
|
3364
|
+
onError: (error) => {
|
|
3365
|
+
log(`ork ui: follow-ups: ${error}`);
|
|
3366
|
+
}
|
|
3367
|
+
});
|
|
3124
3368
|
let closed;
|
|
3125
3369
|
return {
|
|
3126
3370
|
port,
|
|
@@ -3133,6 +3377,7 @@ const startServer = async (options) => {
|
|
|
3133
3377
|
close: () => {
|
|
3134
3378
|
closed ??= new Promise((resolve) => {
|
|
3135
3379
|
shutdown.abort();
|
|
3380
|
+
stopChecks();
|
|
3136
3381
|
updates.close();
|
|
3137
3382
|
server.close(() => {
|
|
3138
3383
|
store.close();
|