@krapht/ork 1.0.0-alpha.14 → 1.0.0-alpha.16
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 +10 -4
- package/dist/__build_info__-DcRdK0Du.mjs +359 -0
- package/dist/apply-Mb0fNpCX.mjs +17 -0
- package/dist/{attach-CdtGX9se.mjs → attach-7lB5_jpr.mjs} +5 -4
- package/dist/{store-CleF7KeS.mjs → attach-B-FeE1dJ.mjs} +60 -25
- package/dist/bin.mjs +328 -1734
- package/dist/brief-BvMtIFio.mjs +58 -0
- package/dist/{builtin-files-Bdq23IIi.mjs → builtin-files-BRSOObAv.mjs} +1 -1
- package/dist/command-tree-BkSYWmYJ.mjs +63 -0
- package/dist/{completion--ywde-56.mjs → completion-Cgb2qPPh.mjs} +45 -6
- package/dist/context-BN9A2Nnu.mjs +106 -0
- package/dist/{find-BVX-bL4u.mjs → find-mLYsR0uC.mjs} +2 -2
- package/dist/flags-CdgIH1L6.mjs +230 -0
- package/dist/follow-ups-DxCsOby1.mjs +496 -0
- package/dist/{format-CvTE5CZQ.mjs → format-hiDvtNGM.mjs} +35 -8
- package/dist/{help-groups-CpR2UzSn.mjs → help-groups-hB6kY34d.mjs} +5 -63
- package/dist/{in-a-run-cL5N9gpH.mjs → in-a-run-a447hxq8.mjs} +1 -1
- package/dist/{__build_info__-B82-dGJY.mjs → install-23g_1ngs.mjs} +3 -227
- package/dist/{limits-Dhe--wmX.mjs → limits-C_Xz_t3C.mjs} +1 -1
- package/dist/{mcp-CsmboyXv.mjs → mcp-CWng9I9n.mjs} +6 -6
- package/dist/{once-CrwI-ZdN.mjs → once-lvwcWfVf.mjs} +3 -3
- package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
- package/dist/{pause-DssrYcda.mjs → pause-Cy11KrXK.mjs} +3 -3
- package/dist/post-C9soCx7i.mjs +46 -0
- package/dist/print-BaWiWf4v.mjs +19 -0
- package/dist/project-records-BRiyQzUo.mjs +230 -0
- package/dist/{relay-DASRgsTQ.mjs → relay-C0M1TWMB.mjs} +1 -1
- package/dist/run-B-j3Qxqc.mjs +1464 -0
- package/dist/{run-D8divGCs.mjs → run-D_u9V_Ve.mjs} +1 -1
- package/dist/{send-CXv8bx0i.mjs → send-qBxj0JbJ.mjs} +2 -2
- package/dist/{src-njdtejgZ.mjs → src-CXBmaGwS.mjs} +213 -75
- package/dist/{src-C7z_6fhn.mjs → src-DHDQ0itC.mjs} +106 -50
- package/dist/{src-tsuZRu0H.mjs → src-DN-RPsil.mjs} +5423 -4467
- package/dist/{src-8Mul0695.mjs → src-nXLIwDrt.mjs} +325 -81
- package/dist/store-Dyr1mP8K.mjs +16 -0
- package/dist/ui/assets/app-BPSCxb_l.css +1 -0
- package/dist/ui/assets/app-CnNRowFn.js +111 -0
- package/dist/ui/assets/{index-D-_uPI2v.js → index-CVzwYlz3.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-CNO1M0qT.mjs → ui-DLrSt5ny.mjs} +3 -3
- package/dist/{usage-BVXi0iFY.mjs → usage-CxmpYYO4.mjs} +61 -107
- package/dist/{context-Y_I4Mofq.mjs → validate-BVrT43GO.mjs} +35 -108
- package/dist/{watch-DVNIciHp.mjs → watch-mkLm4t9D.mjs} +19 -5
- package/dist/{workflows-B30QkwTk.mjs → workflows-Bvl2Fquu.mjs} +2 -2
- package/dist/{worktrees-DD4_TpkH.mjs → worktrees-DOOCoS2w.mjs} +49 -20
- package/package.json +8 -4
- package/dist/ui/assets/app-D_nAPnHM.css +0 -1
- package/dist/ui/assets/app-DqBAtXSZ.js +0 -111
|
@@ -1,79 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { $n as DISCUSSIONS, Cn as linkNodes, D as triggersReport, Dn as requestStop, En as requestPause, F as activeTree, Fn as processAlive, Gn as endOf, Gt as applyClean, Hn as loadProject, I as isNodeEvent, Ir as runDirOf, Jn as END_KINDS, Jr as applyEdits, Jt as planClean, Kr as skillsIn, Kt as cleanStep, L as openStore, Mn as runStatus, Nr as placeFiles, On as tellLiveRun, Pr as placeFor, R as storePath, Rn as nestItems, Rr as workflowDirOf, S as detectInstaller, St as walkSummary, Tn as requestContinue, Vr as LESSON_CAP_TOKENS, Xr as readDocument, Xt as stepPrunes, Yr as newWorkflowText, Yt as stepActs, _ as capsHere, a as planRemove, an as nodeSays, b as runningRunIds, bn as DEFAULT_FOLLOW_UPS_RULE, bt as checkWorkflowText, d as planLine, ei as renderJsonSchema, er as planPost, f as removeRefusal, fn as checkFollowUpsDue, fr as readCondition, g as usageReport, hn as startFollowUpChecks, ht as explainWorkflowText, i as lostWork, ii as EdgeInputSchema, jn as pausedOf, kn as liveStatus, kr as loadAgents, l as labelOf, li as NodeSchema, lt as detachRun, nr as sendPost, nt as answerDecision, o as projectHead, ot as detachResume, p as stateParts, pt as limitsReport, qr as WorkflowEditSchema, qt as keyOf, r as applyRemove, rt as pendingDecisions, s as readWorktrees, si as expandEdge, st as resumable, ti as parseWorkflow, tr as runGh, u as mergeStateOf, ut as followEvents, v as planUpgrade, wn as unlinkNodes, x as runningScript, xt as walkChecks, y as releaseNotes, yn as liveSessionOf, zr as inside } from "./src-DN-RPsil.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-23g_1ngs.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__-DcRdK0Du.mjs";
|
|
4
|
+
import { t as postFollowUpMessage } from "./post-C9soCx7i.mjs";
|
|
5
|
+
import { t as builtinProblem } from "./builtin-files-BRSOObAv.mjs";
|
|
6
|
+
import { n as newRunId, t as findRun } from "./find-mLYsR0uC.mjs";
|
|
5
7
|
import path from "node:path";
|
|
6
8
|
import { z } from "zod";
|
|
7
9
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
8
10
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
9
11
|
import { homedir } from "node:os";
|
|
10
12
|
import { createServer } from "node:http";
|
|
11
|
-
// ../server/src/contract-builder.ts
|
|
12
|
-
/** A new workflow's name, as its file and its `name:` take it. */
|
|
13
|
-
const NewWorkflowSchema = z.strictObject({ name: z.string().min(1) });
|
|
14
|
-
/** The server checks each edit; this file imports only types of the schema. */
|
|
15
|
-
const WorkflowEditsSchema = z.strictObject({ edits: z.array(z.custom()).min(1) });
|
|
16
|
-
/** Earlier text, and the file it was based on, to ask before replacing it. */
|
|
17
|
-
const DraftTextSchema = z.strictObject({
|
|
18
|
-
text: z.string(),
|
|
19
|
-
baseHash: z.string().nullable()
|
|
20
|
-
});
|
|
21
|
-
const SaveWorkflowSchema = z.strictObject({
|
|
22
|
-
/** Write over a file that changed since this draft began. */
|
|
23
|
-
force: z.boolean().optional() });
|
|
24
|
-
const BUILDER_API = {
|
|
25
|
-
builderSchema: {
|
|
26
|
-
method: "GET",
|
|
27
|
-
path: "/api/workflow-schema"
|
|
28
|
-
},
|
|
29
|
-
builderCatalog: {
|
|
30
|
-
method: "GET",
|
|
31
|
-
path: "/api/projects/:id/catalog"
|
|
32
|
-
},
|
|
33
|
-
builderDrafts: {
|
|
34
|
-
method: "GET",
|
|
35
|
-
path: "/api/projects/:id/builder"
|
|
36
|
-
},
|
|
37
|
-
builderView: {
|
|
38
|
-
method: "GET",
|
|
39
|
-
path: "/api/projects/:id/builder/:name"
|
|
40
|
-
},
|
|
41
|
-
builderCreate: {
|
|
42
|
-
method: "POST",
|
|
43
|
-
path: "/api/projects/:id/builder",
|
|
44
|
-
body: NewWorkflowSchema
|
|
45
|
-
},
|
|
46
|
-
builderEdit: {
|
|
47
|
-
method: "POST",
|
|
48
|
-
path: "/api/projects/:id/builder/:name/edits",
|
|
49
|
-
body: WorkflowEditsSchema
|
|
50
|
-
},
|
|
51
|
-
builderRestore: {
|
|
52
|
-
method: "POST",
|
|
53
|
-
path: "/api/projects/:id/builder/:name/text",
|
|
54
|
-
body: DraftTextSchema
|
|
55
|
-
},
|
|
56
|
-
builderDiscard: {
|
|
57
|
-
method: "POST",
|
|
58
|
-
path: "/api/projects/:id/builder/:name/discard"
|
|
59
|
-
},
|
|
60
|
-
builderSave: {
|
|
61
|
-
method: "POST",
|
|
62
|
-
path: "/api/projects/:id/builder/:name/save",
|
|
63
|
-
body: SaveWorkflowSchema
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
// ../server/src/contract-context.ts
|
|
67
|
-
const CONTEXT_API = {
|
|
68
|
-
runContext: {
|
|
69
|
-
method: "GET",
|
|
70
|
-
path: "/api/runs/:id/nodes/:node/context"
|
|
71
|
-
},
|
|
72
|
-
builderContext: {
|
|
73
|
-
method: "GET",
|
|
74
|
-
path: "/api/projects/:id/builder/:name/nodes/:node/context"
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
13
|
// ../server/src/contract-feedback.ts
|
|
78
14
|
const NoteSchema = z.strictObject({
|
|
79
15
|
/** Its first line is the title. */
|
|
@@ -153,6 +89,133 @@ const FEEDBACK_API = {
|
|
|
153
89
|
path: "/api/feedback/:id/split"
|
|
154
90
|
}
|
|
155
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
|
+
};
|
|
156
219
|
// ../server/src/contract-links.ts
|
|
157
220
|
/**
|
|
158
221
|
* @file The routes that link two nodes of live runs (RFC-003): list, link and
|
|
@@ -320,7 +383,7 @@ const API = {
|
|
|
320
383
|
},
|
|
321
384
|
...BUILDER_API,
|
|
322
385
|
...CONTEXT_API,
|
|
323
|
-
...
|
|
386
|
+
...AFTER_API,
|
|
324
387
|
...WORKTREES_API,
|
|
325
388
|
...UPDATE_API,
|
|
326
389
|
projects: {
|
|
@@ -1045,17 +1108,14 @@ const checkTool = (context) => tool("check", "Checks a workflow as `ork validate
|
|
|
1045
1108
|
return answer({
|
|
1046
1109
|
errors: count("error"),
|
|
1047
1110
|
warnings: count("warning"),
|
|
1048
|
-
diagnostics: diagnostics.map(({ severity, code, message, line, column }) =>
|
|
1111
|
+
diagnostics: diagnostics.map(({ severity, code, message, file, line, column }) => Object.assign({
|
|
1049
1112
|
severity,
|
|
1050
1113
|
code,
|
|
1051
1114
|
message
|
|
1052
|
-
} : {
|
|
1053
|
-
severity,
|
|
1054
|
-
code,
|
|
1055
|
-
message,
|
|
1115
|
+
}, file === void 0 ? {} : { file }, line === void 0 ? {} : {
|
|
1056
1116
|
line,
|
|
1057
1117
|
column
|
|
1058
|
-
}),
|
|
1118
|
+
})),
|
|
1059
1119
|
walk: walk === void 0 ? null : walkSummary(walk),
|
|
1060
1120
|
...args.paths === true && walk !== void 0 ? { endings: walk.endings.slice(0, MOST_ENDINGS) } : {}
|
|
1061
1121
|
});
|
|
@@ -2021,7 +2081,7 @@ const applyRoute = async (context, item, driver) => {
|
|
|
2021
2081
|
* workflows would go and how big that file is, the project a fix would be
|
|
2022
2082
|
* proposed in, and the run proposing one now, if any.
|
|
2023
2083
|
*/
|
|
2024
|
-
const detailOf = (store, item) => {
|
|
2084
|
+
const detailOf$1 = (store, item) => {
|
|
2025
2085
|
const registered = store.projects();
|
|
2026
2086
|
const fixRun = openFixRun(store, item);
|
|
2027
2087
|
const actions = feedbackActions(item, fixRun);
|
|
@@ -2124,7 +2184,7 @@ const feedbackRoutes = (context) => {
|
|
|
2124
2184
|
feedbackItem: ({ params }) => {
|
|
2125
2185
|
const found = itemOf(store, params.id);
|
|
2126
2186
|
if (!found.ok) return found.refusal;
|
|
2127
|
-
return ok(detailOf(store, found.item));
|
|
2187
|
+
return ok(detailOf$1(store, found.item));
|
|
2128
2188
|
},
|
|
2129
2189
|
feedbackStatus: ({ params, body }) => {
|
|
2130
2190
|
const found = itemOf(store, params.id);
|
|
@@ -2173,6 +2233,172 @@ const feedbackRoutes = (context) => {
|
|
|
2173
2233
|
}
|
|
2174
2234
|
};
|
|
2175
2235
|
};
|
|
2236
|
+
// ../server/src/follow-up-routes.ts
|
|
2237
|
+
/**
|
|
2238
|
+
* @file The follow-ups' routes (specs/follow-ups.md), through the same core
|
|
2239
|
+
* functions as `ork follow-ups`: a project's list (after the due check, which
|
|
2240
|
+
* reads pull requests through the person's own `gh` and only reports a `gh`
|
|
2241
|
+
* that fails), one follow-up with what to copy and what can be done to it,
|
|
2242
|
+
* done, drop, reopen and due, and posting a drafted GitHub comment only with
|
|
2243
|
+
* the person's confirmation of the draft they saw (its digest). Ork never
|
|
2244
|
+
* runs a task's commands.
|
|
2245
|
+
*/
|
|
2246
|
+
const OPEN = ["due", "waiting"];
|
|
2247
|
+
/** Stands for what a person saw of a draft: its recipient and text. */
|
|
2248
|
+
const digestOf$1 = (f) => f.draft === void 0 ? void 0 : createHash("sha256").update(JSON.stringify([f.draft.to, f.draft.text])).digest("hex");
|
|
2249
|
+
/** The place a project's rule keeps follow-ups in. */
|
|
2250
|
+
const ruleOf = (project) => (loadProject(project.root, placeFiles(project).rules).project?.followUps ?? DEFAULT_FOLLOW_UPS_RULE).store;
|
|
2251
|
+
/** Why a follow-up isn't where the project's rule says, when it isn't. */
|
|
2252
|
+
const fallbackOf = (placed, rule) => {
|
|
2253
|
+
if (placed === rule) return void 0;
|
|
2254
|
+
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.`;
|
|
2255
|
+
};
|
|
2256
|
+
const deliveryOf = (store, f) => {
|
|
2257
|
+
if (!f.for.startsWith("project:")) return void 0;
|
|
2258
|
+
const name = f.for.slice(8).trim();
|
|
2259
|
+
const target = store.projects().find((p) => p.id === name);
|
|
2260
|
+
if (target === void 0) return {
|
|
2261
|
+
project: name,
|
|
2262
|
+
registered: false
|
|
2263
|
+
};
|
|
2264
|
+
const live = liveSessionOf(store, target.root);
|
|
2265
|
+
return {
|
|
2266
|
+
project: name,
|
|
2267
|
+
registered: true,
|
|
2268
|
+
root: target.root,
|
|
2269
|
+
keptIn: ruleOf(target),
|
|
2270
|
+
...live === void 0 ? {} : { live }
|
|
2271
|
+
};
|
|
2272
|
+
};
|
|
2273
|
+
/** `f`, what can be done to it, where it is kept, and what to copy from it. */
|
|
2274
|
+
const detailOf = (context, project, f) => {
|
|
2275
|
+
const rule = ruleOf(project);
|
|
2276
|
+
const fallback = fallbackOf(f.place, rule);
|
|
2277
|
+
const cause = dueCause(f);
|
|
2278
|
+
const delivery = deliveryOf(context.store, f);
|
|
2279
|
+
const digest = digestOf$1(f);
|
|
2280
|
+
return {
|
|
2281
|
+
followUp: f,
|
|
2282
|
+
actions: followUpActions(f),
|
|
2283
|
+
...cause === void 0 ? {} : { cause },
|
|
2284
|
+
keptIn: {
|
|
2285
|
+
place: f.place,
|
|
2286
|
+
rule,
|
|
2287
|
+
...f.url === void 0 ? {} : { url: f.url },
|
|
2288
|
+
...fallback === void 0 ? {} : { fallback }
|
|
2289
|
+
},
|
|
2290
|
+
...delivery === void 0 ? {} : { delivery },
|
|
2291
|
+
...digest === void 0 ? {} : { draftDigest: digest },
|
|
2292
|
+
copy: {
|
|
2293
|
+
text: f.draft?.text ?? "",
|
|
2294
|
+
commands: (f.steps ?? []).flatMap((step) => step.command === void 0 ? [] : [step.command])
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
};
|
|
2298
|
+
const toItem = (f) => ({
|
|
2299
|
+
id: f.id,
|
|
2300
|
+
project: f.project,
|
|
2301
|
+
title: f.title,
|
|
2302
|
+
for: f.for,
|
|
2303
|
+
kind: f.kind,
|
|
2304
|
+
...f.note === void 0 ? {} : { note: f.note }
|
|
2305
|
+
});
|
|
2306
|
+
/** The due follow-ups of `roots`, most recently changed first. */
|
|
2307
|
+
const dueOf = (store, roots) => {
|
|
2308
|
+
const due = roots.flatMap((root) => store.followUps(root)).filter((f) => f.status === "due").toSorted((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
2309
|
+
return {
|
|
2310
|
+
count: due.length,
|
|
2311
|
+
items: due.slice(0, 20).map((f) => toItem(f))
|
|
2312
|
+
};
|
|
2313
|
+
};
|
|
2314
|
+
const found = (context, params) => {
|
|
2315
|
+
const project = projectOf(context, params.id);
|
|
2316
|
+
if (project === void 0) return {
|
|
2317
|
+
ok: false,
|
|
2318
|
+
refusal: fail(404, `No registered project \`${params.id}\`.`)
|
|
2319
|
+
};
|
|
2320
|
+
if (params.followUp === void 0) return {
|
|
2321
|
+
ok: true,
|
|
2322
|
+
project
|
|
2323
|
+
};
|
|
2324
|
+
const one = findFollowUp(context.store, project.root, params.followUp);
|
|
2325
|
+
if (!one.ok) {
|
|
2326
|
+
const status = one.error.startsWith("No follow-up") ? 404 : 400;
|
|
2327
|
+
return {
|
|
2328
|
+
ok: false,
|
|
2329
|
+
refusal: fail(status, one.error)
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
return {
|
|
2333
|
+
ok: true,
|
|
2334
|
+
project,
|
|
2335
|
+
followUp: one.followUp
|
|
2336
|
+
};
|
|
2337
|
+
};
|
|
2338
|
+
const followUpRoutes = (context) => {
|
|
2339
|
+
const { store } = context;
|
|
2340
|
+
const gh = context.gh ?? runGh;
|
|
2341
|
+
return {
|
|
2342
|
+
followUps: async ({ params, query }) => {
|
|
2343
|
+
const one = found(context, params);
|
|
2344
|
+
if (!one.ok) return one.refusal;
|
|
2345
|
+
const { root } = one.project;
|
|
2346
|
+
const errors = await checkFollowUpsDue({
|
|
2347
|
+
rows: store,
|
|
2348
|
+
project: root,
|
|
2349
|
+
gh
|
|
2350
|
+
}).then((checked) => checked.errors).catch((error) => [error instanceof Error ? error.message : String(error)]);
|
|
2351
|
+
const all = store.followUps(root);
|
|
2352
|
+
const status = query.status ?? "open";
|
|
2353
|
+
const shown = all.filter((f) => {
|
|
2354
|
+
if (query.for !== void 0 && f.for !== query.for) return false;
|
|
2355
|
+
if (status === "all") return true;
|
|
2356
|
+
if (status === "open") return OPEN.some((s) => s === f.status);
|
|
2357
|
+
return f.status === status;
|
|
2358
|
+
});
|
|
2359
|
+
return ok({
|
|
2360
|
+
project: root,
|
|
2361
|
+
followUps: shown,
|
|
2362
|
+
due: all.filter((f) => f.status === "due").length,
|
|
2363
|
+
checker: {
|
|
2364
|
+
available: errors.length === 0,
|
|
2365
|
+
errors
|
|
2366
|
+
}
|
|
2367
|
+
});
|
|
2368
|
+
},
|
|
2369
|
+
followUp: ({ params }) => {
|
|
2370
|
+
const one = found(context, params);
|
|
2371
|
+
if (!one.ok) return one.refusal;
|
|
2372
|
+
return one.followUp === void 0 ? fail(404, "No such follow-up.") : ok(detailOf(context, one.project, one.followUp));
|
|
2373
|
+
},
|
|
2374
|
+
followUpChange: ({ params, body }) => {
|
|
2375
|
+
const one = found(context, params);
|
|
2376
|
+
if (!one.ok) return one.refusal;
|
|
2377
|
+
if (one.followUp === void 0) return fail(404, "No such follow-up.");
|
|
2378
|
+
const changed = changeFollowUp(store, one.followUp, body.action);
|
|
2379
|
+
return changed.ok ? ok(detailOf(context, one.project, changed.followUp)) : fail(409, changed.error);
|
|
2380
|
+
},
|
|
2381
|
+
followUpPost: async ({ params, body }) => {
|
|
2382
|
+
const one = found(context, params);
|
|
2383
|
+
if (!one.ok) return one.refusal;
|
|
2384
|
+
const { followUp } = one;
|
|
2385
|
+
if (followUp === void 0) return fail(404, "No such follow-up.");
|
|
2386
|
+
const check = followUpActions(followUp).post;
|
|
2387
|
+
if (!check.ok) return fail(409, check.error);
|
|
2388
|
+
if (digestOf$1(followUp) !== body.digest) return fail(409, "The draft is no longer the one you confirmed; open the follow-up again.");
|
|
2389
|
+
const posted = await postFollowUpMessage(followUp, {
|
|
2390
|
+
rows: store,
|
|
2391
|
+
gh,
|
|
2392
|
+
confirmed: body.confirm,
|
|
2393
|
+
home: context.home ?? homedir()
|
|
2394
|
+
});
|
|
2395
|
+
return posted.ok ? ok({
|
|
2396
|
+
...posted.url === void 0 ? {} : { url: posted.url },
|
|
2397
|
+
followUp: detailOf(context, one.project, posted.followUp)
|
|
2398
|
+
}) : fail(409, posted.error);
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
};
|
|
2176
2402
|
const ANSI = /\u001B\[[0-?]*[ -/]*[@-~]|\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)|\u001B[@-Z\\-_]/gu;
|
|
2177
2403
|
/** A line as a terminal would end up showing it. */
|
|
2178
2404
|
const plainLine = (raw) => (raw.replaceAll(ANSI, "").split("\r").findLast((part) => part !== "") ?? "").replaceAll(/\s+$/gu, "");
|
|
@@ -2727,6 +2953,7 @@ const apiRoutes = (context, port) => ({
|
|
|
2727
2953
|
...builderRoutes(context),
|
|
2728
2954
|
...contextRoutes(context),
|
|
2729
2955
|
...feedbackRoutes(context),
|
|
2956
|
+
...followUpRoutes(context),
|
|
2730
2957
|
...worktreesRoutes(context),
|
|
2731
2958
|
...updateRoutes(context, port)
|
|
2732
2959
|
});
|
|
@@ -2885,7 +3112,11 @@ const overviewStream = (context, res, url, shutdown) => {
|
|
|
2885
3112
|
const last = {
|
|
2886
3113
|
runs: "",
|
|
2887
3114
|
decisions: "",
|
|
2888
|
-
version: ""
|
|
3115
|
+
version: "",
|
|
3116
|
+
followUps: JSON.stringify({
|
|
3117
|
+
count: 0,
|
|
3118
|
+
items: []
|
|
3119
|
+
})
|
|
2889
3120
|
};
|
|
2890
3121
|
const tick = () => {
|
|
2891
3122
|
const filter = root === void 0 ? {} : { project: root };
|
|
@@ -2900,7 +3131,8 @@ const overviewStream = (context, res, url, shutdown) => {
|
|
|
2900
3131
|
for (const [name, value] of [
|
|
2901
3132
|
["runs", runs],
|
|
2902
3133
|
["decisions", decisions],
|
|
2903
|
-
["version", context.updates.info()]
|
|
3134
|
+
["version", context.updates.info()],
|
|
3135
|
+
["followUps", dueOf(context.store, root === void 0 ? context.store.projects().map((p) => p.root) : [root])]
|
|
2904
3136
|
]) {
|
|
2905
3137
|
const text = JSON.stringify(value);
|
|
2906
3138
|
if (text === last[name]) continue;
|
|
@@ -3058,6 +3290,8 @@ const runningServer = (file) => {
|
|
|
3058
3290
|
*/
|
|
3059
3291
|
/** The port `ork ui` serves on unless told otherwise. */
|
|
3060
3292
|
const DEFAULT_PORT = 4417;
|
|
3293
|
+
/** The overview stream sends the due follow-ups itself; nothing more here. */
|
|
3294
|
+
const noticed = () => {};
|
|
3061
3295
|
const listen = (server, port) => new Promise((resolve, reject) => {
|
|
3062
3296
|
server.once("error", (error) => {
|
|
3063
3297
|
reject(error.code === "EADDRINUSE" ? /* @__PURE__ */ new Error(`Port ${String(port)} is in use; start Ork's UI on another with \`--port <n>\`.`) : error);
|
|
@@ -3119,6 +3353,15 @@ const startServer = async (options) => {
|
|
|
3119
3353
|
token,
|
|
3120
3354
|
startedAt
|
|
3121
3355
|
});
|
|
3356
|
+
const stopChecks = startFollowUpChecks({
|
|
3357
|
+
rows: store,
|
|
3358
|
+
gh: options.gh ?? runGh,
|
|
3359
|
+
projects: () => store.projects().map((project) => project.root),
|
|
3360
|
+
onDue: noticed,
|
|
3361
|
+
onError: (error) => {
|
|
3362
|
+
log(`ork ui: follow-ups: ${error}`);
|
|
3363
|
+
}
|
|
3364
|
+
});
|
|
3122
3365
|
let closed;
|
|
3123
3366
|
return {
|
|
3124
3367
|
port,
|
|
@@ -3131,6 +3374,7 @@ const startServer = async (options) => {
|
|
|
3131
3374
|
close: () => {
|
|
3132
3375
|
closed ??= new Promise((resolve) => {
|
|
3133
3376
|
shutdown.abort();
|
|
3377
|
+
stopChecks();
|
|
3134
3378
|
updates.close();
|
|
3135
3379
|
server.close(() => {
|
|
3136
3380
|
store.close();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { L as openStore } from "./src-DN-RPsil.mjs";
|
|
2
|
+
// src/store.ts
|
|
3
|
+
/**
|
|
4
|
+
* @file Opening the user's store (`~/.ork/state.db`) for the length of one
|
|
5
|
+
* command, and closing it after, whatever happens.
|
|
6
|
+
*/
|
|
7
|
+
/** Runs `body` with the user's store open, and closes it after. */
|
|
8
|
+
const withStore = async (body) => {
|
|
9
|
+
const store = openStore();
|
|
10
|
+
try {
|
|
11
|
+
await body(store);
|
|
12
|
+
} finally {
|
|
13
|
+
store.close();
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export { withStore as t };
|