@drisp/cli 0.7.12 → 0.7.14
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 +6 -0
- package/dist/{WorkflowInstallWizard-VPBOZJY5.js → WorkflowInstallWizard-MVTO43MW.js} +2 -2
- package/dist/{chunk-ZKL7NFOA.js → chunk-3LF5QYZK.js} +2 -2
- package/dist/{chunk-SACVOS6T.js → chunk-GGAPZULS.js} +388 -20
- package/dist/{chunk-UUUCDIHI.js → chunk-HCTFAAGU.js} +10310 -2468
- package/dist/cli.js +2809 -128
- package/dist/dashboard-daemon.js +3 -3
- package/dist/runner.js +3 -3
- package/dist/session-control-relay.js +24 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
RUN_EXIT_CODE,
|
|
10
10
|
ServiceNotEnabledError,
|
|
11
11
|
approvalAnswer,
|
|
12
|
+
assignmentPageSchema,
|
|
13
|
+
assignmentReportSchema,
|
|
14
|
+
assignmentRevisionSchema,
|
|
15
|
+
assignmentSchema,
|
|
12
16
|
attachRuntimeEventLoop,
|
|
13
17
|
attemptForecast,
|
|
14
18
|
behindMain,
|
|
@@ -20,18 +24,30 @@ import {
|
|
|
20
24
|
buildPostByToolUseId,
|
|
21
25
|
buildProbeConfigs,
|
|
22
26
|
calibrationSchema,
|
|
27
|
+
cancelProgress,
|
|
23
28
|
centerAnsi,
|
|
24
29
|
childWorktreePath,
|
|
25
30
|
classifyFailure,
|
|
31
|
+
commandProgress,
|
|
26
32
|
commandSchema,
|
|
27
33
|
commitBuildFiles,
|
|
28
34
|
compactText,
|
|
35
|
+
completionPackageSchema,
|
|
29
36
|
computeInputRows,
|
|
30
37
|
connectAlreadyConnectedSchema,
|
|
31
38
|
connectCodeResponseSchema,
|
|
32
39
|
connectPendingResponseSchema,
|
|
33
40
|
connectRedeemResponseSchema,
|
|
34
41
|
connectStartResponseSchema,
|
|
42
|
+
controlProgress,
|
|
43
|
+
controlRequestSchema,
|
|
44
|
+
coordinationActivityPageSchema,
|
|
45
|
+
coordinationBriefSchema,
|
|
46
|
+
coordinationCommandResultSchema,
|
|
47
|
+
coordinationCommandSchema,
|
|
48
|
+
coordinationInboxEventSchema,
|
|
49
|
+
coordinationSpendSchema,
|
|
50
|
+
coordinatorSchema,
|
|
35
51
|
createFeedMapper,
|
|
36
52
|
createLocalFeedSink,
|
|
37
53
|
createRuntime,
|
|
@@ -39,10 +55,26 @@ import {
|
|
|
39
55
|
credentialHelperKey,
|
|
40
56
|
cursorToVisualPosition,
|
|
41
57
|
defaultWorktreesRoot,
|
|
58
|
+
describeChecks,
|
|
42
59
|
detectClaudeVersion,
|
|
43
60
|
eligibilityReason,
|
|
44
61
|
ensureBuildWorktree,
|
|
45
62
|
ensureChildWorktree,
|
|
63
|
+
executionActivationSchema,
|
|
64
|
+
executionCancellationSchema,
|
|
65
|
+
executionControlState,
|
|
66
|
+
executionHierarchySchema,
|
|
67
|
+
executionMessageSchema,
|
|
68
|
+
executionOutcomeSchema,
|
|
69
|
+
executionPageSchema,
|
|
70
|
+
executionQuestionPageSchema,
|
|
71
|
+
executionQuestionSchema,
|
|
72
|
+
executionSchema,
|
|
73
|
+
executionStepsSchema,
|
|
74
|
+
executionWorkflowPageSchema,
|
|
75
|
+
executionWorkflowRevisionPageSchema,
|
|
76
|
+
executionWorkflowRevisionSchema,
|
|
77
|
+
executionWorkflowSchema,
|
|
46
78
|
expandHome,
|
|
47
79
|
extractFriendlyServerName,
|
|
48
80
|
extractPermissionSnapshot,
|
|
@@ -71,10 +103,12 @@ import {
|
|
|
71
103
|
historyRecordSchema,
|
|
72
104
|
hubHttpUrl,
|
|
73
105
|
identityHubOrigin,
|
|
106
|
+
inboxCursorSchema,
|
|
74
107
|
isBashToolResponse,
|
|
75
108
|
isCommandPrefix,
|
|
76
109
|
isGitHubRepository,
|
|
77
110
|
isScopedPermissionsRequest,
|
|
111
|
+
judgeChecks,
|
|
78
112
|
lastCommitTouching,
|
|
79
113
|
listAwaitingAttentionRuns,
|
|
80
114
|
listHarnessAdapters,
|
|
@@ -85,9 +119,11 @@ import {
|
|
|
85
119
|
lookupCredential,
|
|
86
120
|
machineInstallationId,
|
|
87
121
|
makeSkippedProbe,
|
|
122
|
+
maxClientCancelBoundaryGraceMs,
|
|
88
123
|
mergeFeedItems,
|
|
89
124
|
mergeGuidance,
|
|
90
125
|
messageGlyphs,
|
|
126
|
+
messageProgress,
|
|
91
127
|
overriddenIdentityPath,
|
|
92
128
|
parseFrontmatter,
|
|
93
129
|
parseToolName,
|
|
@@ -97,6 +133,9 @@ import {
|
|
|
97
133
|
probeSkipReason,
|
|
98
134
|
processRegistry,
|
|
99
135
|
progressGlyphs,
|
|
136
|
+
qaRoundPageSchema,
|
|
137
|
+
readCheckList,
|
|
138
|
+
recoveryEpisodeSchema,
|
|
100
139
|
register,
|
|
101
140
|
registerCleanupOnExit,
|
|
102
141
|
releaseMcpAsset,
|
|
@@ -112,8 +151,12 @@ import {
|
|
|
112
151
|
resolveHookForwarderCommand,
|
|
113
152
|
resolveRuntimeAuthOverlay,
|
|
114
153
|
resolveToolColumn,
|
|
154
|
+
reviewRoundPageSchema,
|
|
115
155
|
runClaudeAuthStatus,
|
|
116
156
|
runConfiguredRunner,
|
|
157
|
+
runControlCommandResultSchema,
|
|
158
|
+
runControlCommandSchema,
|
|
159
|
+
runControlRecordSchema,
|
|
117
160
|
runExec,
|
|
118
161
|
runMachineRunners,
|
|
119
162
|
runProbe,
|
|
@@ -130,21 +173,131 @@ import {
|
|
|
130
173
|
runnerSoftwareVersion,
|
|
131
174
|
runnerUpdateRequestSchema,
|
|
132
175
|
scopeFingerprint,
|
|
176
|
+
selectWorkPath,
|
|
133
177
|
serviceInstallationId,
|
|
134
178
|
sessionsDir,
|
|
135
179
|
sliceAnsi,
|
|
136
180
|
spaces,
|
|
137
181
|
startDashboardDecisionDrain,
|
|
138
182
|
startWorkflowExecution,
|
|
183
|
+
supportedOperations,
|
|
139
184
|
supportsSessionApproval,
|
|
140
185
|
teamDefaultRequestSchema,
|
|
141
186
|
teamDefaultSchema,
|
|
142
187
|
teamIdentityPath,
|
|
143
188
|
todoGlyphSet,
|
|
189
|
+
trackerImportPreviewSchema,
|
|
190
|
+
trackerIssueSchema,
|
|
191
|
+
trackerOperationSchema,
|
|
192
|
+
trackerSnapshotSchema,
|
|
144
193
|
visualPositionToOffset,
|
|
194
|
+
workActivityPageSchema,
|
|
195
|
+
workAttachmentCheckPageSchema,
|
|
196
|
+
workAttachmentPageSchema,
|
|
197
|
+
workAttachmentSchema,
|
|
198
|
+
workAttentionActivityPageSchema,
|
|
199
|
+
workAttentionCommandResultSchema,
|
|
200
|
+
workAttentionCommandSchema,
|
|
201
|
+
workAttentionRequestPageSchema,
|
|
202
|
+
workAttentionRequestSchema,
|
|
203
|
+
workAuthoritySchema,
|
|
204
|
+
workCandidatePageSchema,
|
|
205
|
+
workCandidateSchema,
|
|
206
|
+
workCandidateSelectionSchema,
|
|
207
|
+
workClientContract,
|
|
208
|
+
workClientContractHeader,
|
|
209
|
+
workClientUnsupported,
|
|
210
|
+
workCommandResultSchema,
|
|
211
|
+
workCommandSchema,
|
|
212
|
+
workCommentPageSchema,
|
|
213
|
+
workCommentRevisionPageSchema,
|
|
214
|
+
workCommentSchema,
|
|
215
|
+
workDeliveryMigrationPageSchema,
|
|
216
|
+
workDeliveryWorkflowPageSchema,
|
|
217
|
+
workDeliveryWorkflowRevisionPageSchema,
|
|
218
|
+
workDeliveryWorkflowRevisionSchema,
|
|
219
|
+
workDeliveryWorkflowSchema,
|
|
220
|
+
workDependencyGateSchema,
|
|
221
|
+
workDeploymentActivityPageSchema,
|
|
222
|
+
workDeploymentCommandResultSchema,
|
|
223
|
+
workDeploymentCommandSchema,
|
|
224
|
+
workDeploymentPageSchema,
|
|
225
|
+
workDeploymentSchema,
|
|
226
|
+
workDiscussionActivityPageSchema,
|
|
227
|
+
workDiscussionCommandResultSchema,
|
|
228
|
+
workDiscussionCommandSchema,
|
|
229
|
+
workEvidenceActivityPageSchema,
|
|
230
|
+
workEvidenceCommandResultSchema,
|
|
231
|
+
workEvidenceCommandSchema,
|
|
232
|
+
workEvidencePageSchema,
|
|
233
|
+
workEvidenceSchema,
|
|
234
|
+
workExecutionBindingPageSchema,
|
|
235
|
+
workExecutionBindingSchema,
|
|
236
|
+
workExecutionPolicySchema,
|
|
237
|
+
workExecutionReservationPageSchema,
|
|
238
|
+
workExecutionReservationSchema,
|
|
239
|
+
workImportBatchPageSchema,
|
|
240
|
+
workImportBatchSchema,
|
|
241
|
+
workImportCommandResultSchema,
|
|
242
|
+
workImportCommandSchema,
|
|
243
|
+
workImportComponentPageSchema,
|
|
244
|
+
workImportItemPageSchema,
|
|
245
|
+
workImportOutcomePageSchema,
|
|
246
|
+
workImportSnapshotSchema,
|
|
247
|
+
workIssueAssigneePageSchema,
|
|
248
|
+
workIssueLabelPageSchema,
|
|
249
|
+
workIssueMembershipSchema,
|
|
250
|
+
workIssuePageSchema,
|
|
251
|
+
workIssueRelationPageSchema,
|
|
252
|
+
workIssueRelationSchema,
|
|
253
|
+
workIssueReservationSchema,
|
|
254
|
+
workIssueSchema,
|
|
255
|
+
workIssueTransitionsSchema,
|
|
256
|
+
workLabelIssuePageSchema,
|
|
257
|
+
workLabelPageSchema,
|
|
258
|
+
workLabelSchema,
|
|
259
|
+
workLegacyAliasSchema,
|
|
260
|
+
workLifecycleEventPageSchema,
|
|
261
|
+
workMembershipActivityPageSchema,
|
|
262
|
+
workMembershipCommandResultSchema,
|
|
263
|
+
workMembershipCommandSchema,
|
|
264
|
+
workPageSchema,
|
|
265
|
+
workPendingRelationActivityPageSchema,
|
|
266
|
+
workPendingRelationCommandResultSchema,
|
|
267
|
+
workPendingRelationCommandSchema,
|
|
268
|
+
workPendingRelationPageSchema,
|
|
269
|
+
workPendingRelationSchema,
|
|
270
|
+
workPlanPageSchema,
|
|
271
|
+
workPlanSchema,
|
|
272
|
+
workProductionTrailPageSchema,
|
|
273
|
+
workRelationActivityPageSchema,
|
|
274
|
+
workRelationCommandResultSchema,
|
|
275
|
+
workRelationCommandSchema,
|
|
276
|
+
workReservationActivityPageSchema,
|
|
277
|
+
workReservationCommandResultSchema,
|
|
278
|
+
workReservationCommandSchema,
|
|
279
|
+
workScopeRevisionPageSchema,
|
|
280
|
+
workSourceActivityPageSchema,
|
|
281
|
+
workSourceAliasPageSchema,
|
|
282
|
+
workSourceAssociationPageSchema,
|
|
283
|
+
workSourceCommandResultSchema,
|
|
284
|
+
workSourceCommandSchema,
|
|
285
|
+
workSourceKeyLifecycleSchema,
|
|
286
|
+
workSourcePrincipalPageSchema,
|
|
287
|
+
workSourcePrincipalSchema,
|
|
288
|
+
workSourceReferencePageSchema,
|
|
289
|
+
workSourceReferenceSchema,
|
|
290
|
+
workSourceSpacePageSchema,
|
|
291
|
+
workSourceSpaceSchema,
|
|
292
|
+
workThreadSchema,
|
|
293
|
+
workTranscriptHistorySchema,
|
|
294
|
+
workTranscriptLinkPageSchema,
|
|
295
|
+
workTranscriptLinkSchema,
|
|
296
|
+
workVerificationAttemptPageSchema,
|
|
297
|
+
workVerificationAttemptSchema,
|
|
145
298
|
wrapText,
|
|
146
299
|
writeIdentityOnce
|
|
147
|
-
} from "./chunk-
|
|
300
|
+
} from "./chunk-HCTFAAGU.js";
|
|
148
301
|
import {
|
|
149
302
|
generateId as generateId2
|
|
150
303
|
} from "./chunk-PQIB66SG.js";
|
|
@@ -162,6 +315,7 @@ import {
|
|
|
162
315
|
CANNOT_PASS_TEMPLATE,
|
|
163
316
|
DESCRIPTION_TEMPLATE,
|
|
164
317
|
GATES,
|
|
318
|
+
GATE_NAMES,
|
|
165
319
|
LAYOUT_MARKERS,
|
|
166
320
|
MERGE_SIGN_OFF,
|
|
167
321
|
NO_GATE_TEXT_TEMPLATE,
|
|
@@ -172,6 +326,7 @@ import {
|
|
|
172
326
|
WORKFLOW_MD_TEMPLATE,
|
|
173
327
|
collectMcpServersWithOptions,
|
|
174
328
|
createSteerQueue,
|
|
329
|
+
describeIgnoredGate,
|
|
175
330
|
deserializeRunMemory,
|
|
176
331
|
external_exports,
|
|
177
332
|
findMarketplaceRepoDir,
|
|
@@ -185,6 +340,7 @@ import {
|
|
|
185
340
|
listMarketplaceWorkflows,
|
|
186
341
|
listMarketplaceWorkflowsFromRepo,
|
|
187
342
|
listWorkflows,
|
|
343
|
+
parseGateFlags,
|
|
188
344
|
parseWorkflowConfig,
|
|
189
345
|
projectConfigPath,
|
|
190
346
|
pullMarketplaceRepo,
|
|
@@ -196,6 +352,7 @@ import {
|
|
|
196
352
|
resolveWorkflow,
|
|
197
353
|
resolveWorkflowInstall,
|
|
198
354
|
resolveWorkflowMarketplaceSource,
|
|
355
|
+
tightenGates,
|
|
199
356
|
updateWorkflow,
|
|
200
357
|
updateWorkflows,
|
|
201
358
|
verifyRendered,
|
|
@@ -203,7 +360,7 @@ import {
|
|
|
203
360
|
workflowRegistryDir,
|
|
204
361
|
writeGlobalConfig,
|
|
205
362
|
writeProjectConfig
|
|
206
|
-
} from "./chunk-
|
|
363
|
+
} from "./chunk-GGAPZULS.js";
|
|
207
364
|
import {
|
|
208
365
|
__toESM
|
|
209
366
|
} from "./chunk-G3PMV62Z.js";
|
|
@@ -211,10 +368,10 @@ import {
|
|
|
211
368
|
// src/app/entry/cli.tsx
|
|
212
369
|
import { render } from "ink";
|
|
213
370
|
import meow from "meow";
|
|
214
|
-
import
|
|
371
|
+
import fs15 from "fs";
|
|
215
372
|
import { createRequire } from "module";
|
|
216
|
-
import
|
|
217
|
-
import
|
|
373
|
+
import os6 from "os";
|
|
374
|
+
import path16 from "path";
|
|
218
375
|
import { fileURLToPath } from "url";
|
|
219
376
|
|
|
220
377
|
// src/app/shell/sessionUiState.ts
|
|
@@ -638,7 +795,7 @@ function reduceSessionUiState(state, action, ctx) {
|
|
|
638
795
|
|
|
639
796
|
// src/app/shell/sessionUiController.ts
|
|
640
797
|
function createSessionUiController(update, context) {
|
|
641
|
-
const
|
|
798
|
+
const dispatch2 = (...actions) => update((state) => {
|
|
642
799
|
const ctx = context();
|
|
643
800
|
return resolveSessionUiState(
|
|
644
801
|
actions.reduce(
|
|
@@ -649,61 +806,61 @@ function createSessionUiController(update, context) {
|
|
|
649
806
|
);
|
|
650
807
|
});
|
|
651
808
|
return {
|
|
652
|
-
focus: (focusMode) =>
|
|
653
|
-
cycleFocus: () =>
|
|
809
|
+
focus: (focusMode) => dispatch2({ type: "set_focus_mode", focusMode }),
|
|
810
|
+
cycleFocus: () => dispatch2({ type: "cycle_focus" }),
|
|
654
811
|
input: (mode) => {
|
|
655
812
|
switch (mode) {
|
|
656
813
|
case "cancel":
|
|
657
|
-
|
|
814
|
+
dispatch2({ type: "cancel_input" });
|
|
658
815
|
break;
|
|
659
816
|
case "command-open":
|
|
660
|
-
|
|
817
|
+
dispatch2({ type: "open_command_input" });
|
|
661
818
|
break;
|
|
662
819
|
case "search-open":
|
|
663
|
-
|
|
820
|
+
dispatch2({ type: "open_search_input" });
|
|
664
821
|
break;
|
|
665
822
|
case "normal-open":
|
|
666
|
-
|
|
823
|
+
dispatch2({ type: "open_normal_input" });
|
|
667
824
|
break;
|
|
668
825
|
case "search":
|
|
669
826
|
case "command":
|
|
670
827
|
case "normal":
|
|
671
|
-
|
|
828
|
+
dispatch2({ type: "set_input_mode", inputMode: mode });
|
|
672
829
|
}
|
|
673
830
|
},
|
|
674
|
-
cycleHints: () =>
|
|
675
|
-
hideOverlay: () =>
|
|
831
|
+
cycleHints: () => dispatch2({ type: "cycle_hints_forced" }),
|
|
832
|
+
hideOverlay: () => dispatch2({ type: "set_show_run_overlay", show: false }),
|
|
676
833
|
search: {
|
|
677
|
-
edit: (query) =>
|
|
678
|
-
submit: (query, firstMatchIndex) =>
|
|
679
|
-
step: (direction, matches) =>
|
|
680
|
-
clear: () =>
|
|
834
|
+
edit: (query) => dispatch2({ type: "set_search_query", query }),
|
|
835
|
+
submit: (query, firstMatchIndex) => dispatch2({ type: "submit_search_query", query, firstMatchIndex }),
|
|
836
|
+
step: (direction, matches) => dispatch2({ type: "step_search_match", direction, matches }),
|
|
837
|
+
clear: () => dispatch2({ type: "clear_search_and_jump_tail" })
|
|
681
838
|
},
|
|
682
839
|
navigate: (panel, destination) => {
|
|
683
840
|
if (typeof destination === "number")
|
|
684
|
-
|
|
841
|
+
dispatch2(
|
|
685
842
|
panel === "feed" ? { type: "move_feed_cursor", delta: destination } : { type: "scroll_message_viewport", delta: destination }
|
|
686
843
|
);
|
|
687
844
|
else if (panel === "feed")
|
|
688
|
-
|
|
845
|
+
dispatch2({
|
|
689
846
|
type: destination === "top" ? "jump_feed_top" : "jump_feed_tail"
|
|
690
847
|
});
|
|
691
848
|
else
|
|
692
|
-
|
|
849
|
+
dispatch2({
|
|
693
850
|
type: destination === "top" ? "jump_message_top" : "jump_message_tail"
|
|
694
851
|
});
|
|
695
852
|
},
|
|
696
853
|
// Wheel navigation selects its panel and moves it in the same update.
|
|
697
|
-
scrollPanel: (panel, delta) =>
|
|
854
|
+
scrollPanel: (panel, delta) => dispatch2(
|
|
698
855
|
{ type: "set_focus_mode", focusMode: panel },
|
|
699
856
|
panel === "feed" ? { type: "move_feed_cursor", delta } : { type: "scroll_message_viewport", delta }
|
|
700
857
|
),
|
|
701
|
-
reveal: (cursor) =>
|
|
702
|
-
messageTab: (tab) =>
|
|
858
|
+
reveal: (cursor) => dispatch2({ type: "reveal_feed_entry", cursor }),
|
|
859
|
+
messageTab: (tab) => dispatch2({ type: "set_message_tab", tab }),
|
|
703
860
|
todo: {
|
|
704
|
-
toggle: () =>
|
|
705
|
-
move: (delta) =>
|
|
706
|
-
show: () =>
|
|
861
|
+
toggle: () => dispatch2({ type: "toggle_todo_visible" }),
|
|
862
|
+
move: (delta) => dispatch2({ type: "move_todo_cursor", delta }),
|
|
863
|
+
show: () => dispatch2({ type: "set_todo_visible", visible: true })
|
|
707
864
|
}
|
|
708
865
|
};
|
|
709
866
|
}
|
|
@@ -2807,7 +2964,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2807
2964
|
}
|
|
2808
2965
|
setIsRunning(false);
|
|
2809
2966
|
}, [base, cancelCurrentRun]);
|
|
2810
|
-
const
|
|
2967
|
+
const spawn2 = useCallback6(
|
|
2811
2968
|
async (prompt, continuation, _configOverride) => {
|
|
2812
2969
|
await cancelCurrentRun();
|
|
2813
2970
|
const latest = input.store?.getLatestRun();
|
|
@@ -2879,7 +3036,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2879
3036
|
}, []);
|
|
2880
3037
|
return {
|
|
2881
3038
|
...base,
|
|
2882
|
-
startTurn:
|
|
3039
|
+
startTurn: spawn2,
|
|
2883
3040
|
isRunning,
|
|
2884
3041
|
interrupt,
|
|
2885
3042
|
kill,
|
|
@@ -3787,7 +3944,7 @@ function textInputReducer(state, action) {
|
|
|
3787
3944
|
}
|
|
3788
3945
|
function useTextInput(options = {}) {
|
|
3789
3946
|
const { onChange, onSubmit, isActive = true } = options;
|
|
3790
|
-
const [state,
|
|
3947
|
+
const [state, dispatch2] = useReducer(textInputReducer, {
|
|
3791
3948
|
value: "",
|
|
3792
3949
|
cursorOffset: 0
|
|
3793
3950
|
});
|
|
@@ -3806,7 +3963,7 @@ function useTextInput(options = {}) {
|
|
|
3806
3963
|
onChangeRef.current?.(state.value);
|
|
3807
3964
|
}, [state.value]);
|
|
3808
3965
|
const setValue = useCallback7((newValue) => {
|
|
3809
|
-
|
|
3966
|
+
dispatch2({ type: "set-value", value: newValue });
|
|
3810
3967
|
}, []);
|
|
3811
3968
|
const { stdin } = useStdin();
|
|
3812
3969
|
const isForwardDeleteRef = useRef5(false);
|
|
@@ -3846,47 +4003,47 @@ function useTextInput(options = {}) {
|
|
|
3846
4003
|
if (key.return) {
|
|
3847
4004
|
const { value: val, cursorOffset: cur } = stateRef.current;
|
|
3848
4005
|
if (cur > 0 && val[cur - 1] === "\\") {
|
|
3849
|
-
|
|
4006
|
+
dispatch2({ type: "newline-escape" });
|
|
3850
4007
|
} else {
|
|
3851
4008
|
onSubmitRef.current?.(val);
|
|
3852
4009
|
}
|
|
3853
4010
|
return;
|
|
3854
4011
|
}
|
|
3855
4012
|
if (key.leftArrow) {
|
|
3856
|
-
|
|
4013
|
+
dispatch2(key.ctrl ? { type: "move-home" } : { type: "move-left" });
|
|
3857
4014
|
return;
|
|
3858
4015
|
}
|
|
3859
4016
|
if (key.rightArrow) {
|
|
3860
|
-
|
|
4017
|
+
dispatch2(key.ctrl ? { type: "move-end" } : { type: "move-right" });
|
|
3861
4018
|
return;
|
|
3862
4019
|
}
|
|
3863
4020
|
if (key.home) {
|
|
3864
|
-
|
|
4021
|
+
dispatch2({ type: "move-home" });
|
|
3865
4022
|
return;
|
|
3866
4023
|
}
|
|
3867
4024
|
if (key.end) {
|
|
3868
|
-
|
|
4025
|
+
dispatch2({ type: "move-end" });
|
|
3869
4026
|
return;
|
|
3870
4027
|
}
|
|
3871
4028
|
if (key.ctrl) {
|
|
3872
|
-
if (input === "a")
|
|
3873
|
-
else if (input === "e")
|
|
3874
|
-
else if (input === "w")
|
|
3875
|
-
else if (input === "u")
|
|
3876
|
-
else if (input === "d")
|
|
4029
|
+
if (input === "a") dispatch2({ type: "move-home" });
|
|
4030
|
+
else if (input === "e") dispatch2({ type: "move-end" });
|
|
4031
|
+
else if (input === "w") dispatch2({ type: "delete-word-back" });
|
|
4032
|
+
else if (input === "u") dispatch2({ type: "clear-line" });
|
|
4033
|
+
else if (input === "d") dispatch2({ type: "delete-forward" });
|
|
3877
4034
|
return;
|
|
3878
4035
|
}
|
|
3879
4036
|
if (key.backspace || key.delete) {
|
|
3880
4037
|
if (isForwardDeleteRef.current) {
|
|
3881
4038
|
isForwardDeleteRef.current = false;
|
|
3882
|
-
|
|
4039
|
+
dispatch2({ type: "delete-forward" });
|
|
3883
4040
|
} else {
|
|
3884
|
-
|
|
4041
|
+
dispatch2({ type: "backspace" });
|
|
3885
4042
|
}
|
|
3886
4043
|
return;
|
|
3887
4044
|
}
|
|
3888
4045
|
if (input && !key.meta) {
|
|
3889
|
-
|
|
4046
|
+
dispatch2({ type: "insert", char: input });
|
|
3890
4047
|
}
|
|
3891
4048
|
} finally {
|
|
3892
4049
|
done();
|
|
@@ -3898,7 +4055,7 @@ function useTextInput(options = {}) {
|
|
|
3898
4055
|
value: state.value,
|
|
3899
4056
|
cursorOffset: state.cursorOffset,
|
|
3900
4057
|
setValue,
|
|
3901
|
-
dispatch
|
|
4058
|
+
dispatch: dispatch2
|
|
3902
4059
|
};
|
|
3903
4060
|
}
|
|
3904
4061
|
|
|
@@ -4003,7 +4160,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
|
|
|
4003
4160
|
commandSuggestionsEnabled,
|
|
4004
4161
|
wrapSuggestionLine
|
|
4005
4162
|
}, ref) {
|
|
4006
|
-
const { value, cursorOffset, setValue, dispatch } = useTextInput({
|
|
4163
|
+
const { value, cursorOffset, setValue, dispatch: dispatch2 } = useTextInput({
|
|
4007
4164
|
onChange,
|
|
4008
4165
|
onSubmit,
|
|
4009
4166
|
isActive: isInputActive
|
|
@@ -4128,7 +4285,7 @@ var ShellInputImpl = forwardRef(function ShellInput({
|
|
|
4128
4285
|
const recalled = historyBackRef.current?.(currentValue);
|
|
4129
4286
|
if (recalled !== void 0) applyProgrammaticValue(recalled);
|
|
4130
4287
|
} else {
|
|
4131
|
-
|
|
4288
|
+
dispatch2({ type: "move-up", width: inputContentWidth });
|
|
4132
4289
|
}
|
|
4133
4290
|
return;
|
|
4134
4291
|
}
|
|
@@ -4142,11 +4299,11 @@ var ShellInputImpl = forwardRef(function ShellInput({
|
|
|
4142
4299
|
const recalled = historyForwardRef.current?.();
|
|
4143
4300
|
if (recalled !== void 0) applyProgrammaticValue(recalled);
|
|
4144
4301
|
} else {
|
|
4145
|
-
|
|
4302
|
+
dispatch2({ type: "move-down", width: inputContentWidth });
|
|
4146
4303
|
}
|
|
4147
4304
|
}
|
|
4148
4305
|
},
|
|
4149
|
-
[applyProgrammaticValue,
|
|
4306
|
+
[applyProgrammaticValue, dispatch2, inputContentWidth, showSuggestions]
|
|
4150
4307
|
);
|
|
4151
4308
|
useInput7(handleArrows, { isActive: isInputActive });
|
|
4152
4309
|
const lines = renderInputLines(
|
|
@@ -5333,14 +5490,14 @@ function extractWebSearch(_input, response) {
|
|
|
5333
5490
|
return { type: "text", content: extractTextContent(response), maxLines: 10 };
|
|
5334
5491
|
}
|
|
5335
5492
|
function extractNotebookEdit(input, _response) {
|
|
5336
|
-
const
|
|
5493
|
+
const path17 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
|
|
5337
5494
|
const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
|
|
5338
5495
|
const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
|
|
5339
|
-
if (!source) return { type: "text", content: `${mode} cell in ${
|
|
5496
|
+
if (!source) return { type: "text", content: `${mode} cell in ${path17}` };
|
|
5340
5497
|
return {
|
|
5341
5498
|
type: "code",
|
|
5342
5499
|
content: source,
|
|
5343
|
-
language: detectLanguage(
|
|
5500
|
+
language: detectLanguage(path17),
|
|
5344
5501
|
maxLines: 10
|
|
5345
5502
|
};
|
|
5346
5503
|
}
|
|
@@ -5866,10 +6023,10 @@ function extractToolSubject(toolName, toolInput) {
|
|
|
5866
6023
|
const parts = [];
|
|
5867
6024
|
const pattern = str(input, "pattern");
|
|
5868
6025
|
const glob = str(input, "glob");
|
|
5869
|
-
const
|
|
6026
|
+
const path17 = str(input, "path");
|
|
5870
6027
|
if (pattern) parts.push(`"${pattern}"`);
|
|
5871
6028
|
if (glob) parts.push(glob);
|
|
5872
|
-
if (
|
|
6029
|
+
if (path17) parts.push(`in ${path17}`);
|
|
5873
6030
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
5874
6031
|
}
|
|
5875
6032
|
case "Skill":
|
|
@@ -5902,9 +6059,13 @@ function eventLabel(event) {
|
|
|
5902
6059
|
return event.data.scope === "subagent" ? "Subagent" : "Agent";
|
|
5903
6060
|
case "user.prompt":
|
|
5904
6061
|
return "User";
|
|
6062
|
+
case "system.wake":
|
|
6063
|
+
return "Wake";
|
|
5905
6064
|
case "subagent.start":
|
|
5906
6065
|
case "subagent.stop":
|
|
5907
6066
|
return `Subagent \xB7 ${event.data.agent_type}`;
|
|
6067
|
+
case "subagent.task":
|
|
6068
|
+
return `Subagent \xB7 ${event.data.subagent_type ?? event.data.task_id}`;
|
|
5908
6069
|
case "run.start":
|
|
5909
6070
|
return "Run Start";
|
|
5910
6071
|
case "run.end":
|
|
@@ -6014,11 +6175,17 @@ function renderDetailLines(event, width, pairedPostEvent, theme = darkTheme) {
|
|
|
6014
6175
|
const content = renderMarkdownToLines(event.data.message, cw);
|
|
6015
6176
|
return buildResult(header, content, false);
|
|
6016
6177
|
}
|
|
6017
|
-
case "user.prompt":
|
|
6178
|
+
case "user.prompt":
|
|
6179
|
+
case "system.wake": {
|
|
6018
6180
|
const header = buildCompactHeader(event, width, { theme });
|
|
6019
6181
|
const content = renderMarkdownToLines(event.data.prompt, cw);
|
|
6020
6182
|
return buildResult(header, content, false);
|
|
6021
6183
|
}
|
|
6184
|
+
case "subagent.task": {
|
|
6185
|
+
const header = buildCompactHeader(event, width, { theme });
|
|
6186
|
+
const json2 = JSON.stringify(event.data, null, 2);
|
|
6187
|
+
return buildResult(header, highlightCode(json2, cw, "json"), true);
|
|
6188
|
+
}
|
|
6022
6189
|
case "tool.post":
|
|
6023
6190
|
case "tool.delta":
|
|
6024
6191
|
case "tool.failure":
|
|
@@ -8440,8 +8607,8 @@ function formatStatsSnapshot(snapshot) {
|
|
|
8440
8607
|
function truncateSessionId(id, maxWidth) {
|
|
8441
8608
|
if (id.length <= maxWidth) return id;
|
|
8442
8609
|
if (maxWidth >= 12) {
|
|
8443
|
-
const
|
|
8444
|
-
return id.slice(0, maxWidth - 7) + "\u2026" +
|
|
8610
|
+
const tail2 = id.slice(-6);
|
|
8611
|
+
return id.slice(0, maxWidth - 7) + "\u2026" + tail2;
|
|
8445
8612
|
}
|
|
8446
8613
|
const alphanumeric = id.replace(/[^a-zA-Z0-9]/g, "").slice(-4);
|
|
8447
8614
|
return "S" + (alphanumeric || "\u2013");
|
|
@@ -9951,10 +10118,11 @@ function deriveStartupTimeoutFailure(args) {
|
|
|
9951
10118
|
|
|
9952
10119
|
// src/app/shell/resolveAllowedTools.ts
|
|
9953
10120
|
var BASELINE_ALLOWED_TOOLS = ["mcp__*"];
|
|
9954
|
-
function resolveAllowedTools(harness, allowedTools) {
|
|
10121
|
+
function resolveAllowedTools(harness, allowedTools, workflowAllowedTools = []) {
|
|
9955
10122
|
const extras = [
|
|
9956
10123
|
...BASELINE_ALLOWED_TOOLS,
|
|
9957
|
-
...resolveHarnessAdapter(harness).capabilities.extraAllowedTools
|
|
10124
|
+
...resolveHarnessAdapter(harness).capabilities.extraAllowedTools,
|
|
10125
|
+
...workflowAllowedTools
|
|
9958
10126
|
];
|
|
9959
10127
|
const merged = [...allowedTools ?? []];
|
|
9960
10128
|
for (const tool of extras) {
|
|
@@ -11776,7 +11944,8 @@ function App({
|
|
|
11776
11944
|
workflow: runtimeState.workflow,
|
|
11777
11945
|
allowedTools: resolveAllowedTools(
|
|
11778
11946
|
runtimeState.harness,
|
|
11779
|
-
runtimeState.isolation?.allowedTools
|
|
11947
|
+
runtimeState.isolation?.allowedTools,
|
|
11948
|
+
runtimeState.workflow?.allowedTools
|
|
11780
11949
|
),
|
|
11781
11950
|
athenaSessionId,
|
|
11782
11951
|
children: /* @__PURE__ */ jsx23(
|
|
@@ -12206,6 +12375,19 @@ async function runExecCommand(input, deps = {}) {
|
|
|
12206
12375
|
);
|
|
12207
12376
|
return RUN_EXIT_CODE.USAGE;
|
|
12208
12377
|
}
|
|
12378
|
+
let buildGates;
|
|
12379
|
+
try {
|
|
12380
|
+
buildGates = parseGateFlags(input.flags.gates);
|
|
12381
|
+
} catch (error) {
|
|
12382
|
+
logError(`Error: ${error.message}`);
|
|
12383
|
+
return RUN_EXIT_CODE.USAGE;
|
|
12384
|
+
}
|
|
12385
|
+
const gates = tightenGates(input.runtimeConfig.gates, buildGates);
|
|
12386
|
+
for (const gate of gates.ignored) {
|
|
12387
|
+
logError(
|
|
12388
|
+
`Warning: ${describeIgnoredGate({ gate, source: "--gate", floor: "configured" })}`
|
|
12389
|
+
);
|
|
12390
|
+
}
|
|
12209
12391
|
let continueResolution;
|
|
12210
12392
|
try {
|
|
12211
12393
|
continueResolution = resolveResumeTarget({
|
|
@@ -12258,6 +12440,16 @@ async function runExecCommand(input, deps = {}) {
|
|
|
12258
12440
|
ephemeral: input.flags.ephemeral,
|
|
12259
12441
|
timeoutMs: input.flags.timeoutMs,
|
|
12260
12442
|
permissionGraceMs: graceMs ?? input.runtimeConfig.permissionGraceMs,
|
|
12443
|
+
gates: gates.gates,
|
|
12444
|
+
gateProvenance: {
|
|
12445
|
+
...input.runtimeConfig.settingsProvenance?.gates,
|
|
12446
|
+
// A gate --gate tightened was set by this Build.
|
|
12447
|
+
...Object.fromEntries(
|
|
12448
|
+
GATE_NAMES.filter(
|
|
12449
|
+
(name) => gates.gates[name] !== input.runtimeConfig.gates[name]
|
|
12450
|
+
).map((name) => [name, "build"])
|
|
12451
|
+
)
|
|
12452
|
+
},
|
|
12261
12453
|
...answer !== void 0 ? { storedAnswer: localAnswerDecision(answer) } : {},
|
|
12262
12454
|
// Reporting-only summary: strip to name + source layer so secret-bearing
|
|
12263
12455
|
// MCP env/command/args and skill paths never reach the startup notice or
|
|
@@ -13100,7 +13292,7 @@ var thisMachine = (installationId) => ({
|
|
|
13100
13292
|
os: `${platform()} ${release()}`.slice(0, 64),
|
|
13101
13293
|
softwareVersion: runnerSoftwareVersion
|
|
13102
13294
|
});
|
|
13103
|
-
var post = (origin,
|
|
13295
|
+
var post = (origin, path17, body) => fetch(new URL(path17, origin), {
|
|
13104
13296
|
method: "POST",
|
|
13105
13297
|
headers: { "Content-Type": "application/json" },
|
|
13106
13298
|
body: JSON.stringify(body)
|
|
@@ -13119,8 +13311,8 @@ async function alreadyConnected(existing, { forTeam, ownIdentity }) {
|
|
|
13119
13311
|
const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
|
|
13120
13312
|
const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
|
|
13121
13313
|
if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
|
|
13122
|
-
const
|
|
13123
|
-
return { status: "identity_unusable", ...runner, problem, ...
|
|
13314
|
+
const path17 = (own ?? forTeam(existing.orgId)[0])?.path;
|
|
13315
|
+
return { status: "identity_unusable", ...runner, problem, ...path17 ? { path: path17 } : {} };
|
|
13124
13316
|
}
|
|
13125
13317
|
async function connectWithCode({
|
|
13126
13318
|
code,
|
|
@@ -13192,20 +13384,20 @@ async function saveIdentity(origin, credential, { single, forTeam }, directory)
|
|
|
13192
13384
|
name: credential.name
|
|
13193
13385
|
});
|
|
13194
13386
|
const replaced = [];
|
|
13195
|
-
for (const { path:
|
|
13196
|
-
const aside = `${
|
|
13197
|
-
await rename(
|
|
13387
|
+
for (const { path: path18 } of forTeam(credential.orgId)) {
|
|
13388
|
+
const aside = `${path18}.replaced-${Date.now()}`;
|
|
13389
|
+
await rename(path18, aside);
|
|
13198
13390
|
replaced.push(aside);
|
|
13199
13391
|
}
|
|
13200
|
-
const
|
|
13392
|
+
const path17 = single ?? teamIdentityPath(origin, credential.orgId, directory);
|
|
13201
13393
|
try {
|
|
13202
|
-
await writeIdentityOnce(
|
|
13394
|
+
await writeIdentityOnce(path17, JSON.stringify(config, null, 2) + "\n");
|
|
13203
13395
|
} catch (error) {
|
|
13204
13396
|
if (error.code === "EEXIST")
|
|
13205
|
-
throw new Error(`Another Connect wrote a Runner identity for this Team at ${
|
|
13397
|
+
throw new Error(`Another Connect wrote a Runner identity for this Team at ${path17} meanwhile; it was left unchanged.`);
|
|
13206
13398
|
throw error;
|
|
13207
13399
|
}
|
|
13208
|
-
return { status: "connected", config, path:
|
|
13400
|
+
return { status: "connected", config, path: path17, replaced };
|
|
13209
13401
|
}
|
|
13210
13402
|
function pathSegment(value) {
|
|
13211
13403
|
const segment = encodeURIComponent(value);
|
|
@@ -13245,7 +13437,7 @@ async function refuseExisting(target) {
|
|
|
13245
13437
|
throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
|
|
13246
13438
|
}
|
|
13247
13439
|
async function disconnectRunner({
|
|
13248
|
-
identity: { path:
|
|
13440
|
+
identity: { path: path17, config },
|
|
13249
13441
|
serviceManager,
|
|
13250
13442
|
othersRemain
|
|
13251
13443
|
}) {
|
|
@@ -13278,7 +13470,7 @@ async function disconnectRunner({
|
|
|
13278
13470
|
await restore();
|
|
13279
13471
|
throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
|
|
13280
13472
|
}
|
|
13281
|
-
await forgetRunner(
|
|
13473
|
+
await forgetRunner(path17);
|
|
13282
13474
|
if (othersRemain) await restore();
|
|
13283
13475
|
else await serviceManager?.remove();
|
|
13284
13476
|
return result;
|
|
@@ -13431,18 +13623,18 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13431
13623
|
`This machine is already connected to this Team as ${outcome.name}, but ${why}. ` + (code ? `The code was not used: connect a new Runner in its place with ${retry}. ` : `Connect a new Runner in its place with a Connect code from Settings \u2192 Runners: ${retry}. `) + `The old ${outcome.name} stays listed in Settings \u2192 Runners, offline.`
|
|
13432
13624
|
);
|
|
13433
13625
|
}
|
|
13434
|
-
const { config, path:
|
|
13626
|
+
const { config, path: path17, replaced } = outcome;
|
|
13435
13627
|
for (const aside of replaced)
|
|
13436
13628
|
process.stdout.write(
|
|
13437
13629
|
`The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
|
|
13438
13630
|
`
|
|
13439
13631
|
);
|
|
13440
13632
|
process.stdout.write(
|
|
13441
|
-
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${
|
|
13633
|
+
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${path17}.
|
|
13442
13634
|
`
|
|
13443
13635
|
);
|
|
13444
13636
|
if (services) {
|
|
13445
|
-
const service = await ensureService(services, config,
|
|
13637
|
+
const service = await ensureService(services, config, path17);
|
|
13446
13638
|
process.stdout.write(`${serviceStarted(service)}
|
|
13447
13639
|
`);
|
|
13448
13640
|
return 0;
|
|
@@ -13451,7 +13643,7 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13451
13643
|
process.stdout.write(
|
|
13452
13644
|
"No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
|
|
13453
13645
|
);
|
|
13454
|
-
await runConfiguredRunner(
|
|
13646
|
+
await runConfiguredRunner(path17, options.signal);
|
|
13455
13647
|
return 0;
|
|
13456
13648
|
}
|
|
13457
13649
|
case "disconnect": {
|
|
@@ -13522,9 +13714,9 @@ async function runRunnerCommandV2(input, options = {}) {
|
|
|
13522
13714
|
configured: true,
|
|
13523
13715
|
// One entry per Team this machine serves; credentials are never printed.
|
|
13524
13716
|
teams: identities.map(
|
|
13525
|
-
({ path:
|
|
13717
|
+
({ path: path17, config: { token: _token, ...config } }) => ({
|
|
13526
13718
|
...config,
|
|
13527
|
-
path:
|
|
13719
|
+
path: path17
|
|
13528
13720
|
})
|
|
13529
13721
|
),
|
|
13530
13722
|
service: service ? {
|
|
@@ -13832,7 +14024,368 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
13832
14024
|
import fs7 from "fs";
|
|
13833
14025
|
import path7 from "path";
|
|
13834
14026
|
|
|
14027
|
+
// ../../packages/client/src/work-flows.ts
|
|
14028
|
+
var WorkFlowError = class extends Error {
|
|
14029
|
+
constructor(code, details = {}) {
|
|
14030
|
+
super(code);
|
|
14031
|
+
this.code = code;
|
|
14032
|
+
this.details = details;
|
|
14033
|
+
}
|
|
14034
|
+
code;
|
|
14035
|
+
details;
|
|
14036
|
+
};
|
|
14037
|
+
var execution = (result) => {
|
|
14038
|
+
const o = result.outcome;
|
|
14039
|
+
if (o.kind === "execution" || o.kind === "assignment" || o.kind === "package" || o.kind === "effect") return o.execution;
|
|
14040
|
+
throw new WorkFlowError("unexpected_outcome", { kind: o.kind });
|
|
14041
|
+
};
|
|
14042
|
+
var WorkFlows = class {
|
|
14043
|
+
constructor(client2, teamId, commandId = () => crypto.randomUUID(), pageSize = 20) {
|
|
14044
|
+
this.client = client2;
|
|
14045
|
+
this.teamId = teamId;
|
|
14046
|
+
this.commandId = commandId;
|
|
14047
|
+
this.pageSize = pageSize;
|
|
14048
|
+
}
|
|
14049
|
+
client;
|
|
14050
|
+
teamId;
|
|
14051
|
+
commandId;
|
|
14052
|
+
pageSize;
|
|
14053
|
+
/**
|
|
14054
|
+
* The Team's path. A Hub without the authority route (`not_found` with HTTP 404) predates this contract and has no
|
|
14055
|
+
* execution switch, so it is `legacy`; every other failure is thrown.
|
|
14056
|
+
*/
|
|
14057
|
+
async path() {
|
|
14058
|
+
try {
|
|
14059
|
+
return selectWorkPath(await this.client.workAuthority());
|
|
14060
|
+
} catch (error) {
|
|
14061
|
+
if (error instanceof ClientError && error.code === "not_found" && error.status === 404) return selectWorkPath(null);
|
|
14062
|
+
throw error;
|
|
14063
|
+
}
|
|
14064
|
+
}
|
|
14065
|
+
async operations() {
|
|
14066
|
+
return supportedOperations(await this.path());
|
|
14067
|
+
}
|
|
14068
|
+
/** Refuses (before any write) an operation the Team's path does not give canonical clients. */
|
|
14069
|
+
async require(operation) {
|
|
14070
|
+
const path17 = await this.path();
|
|
14071
|
+
if (path17.kind === "unsupported") throw new WorkFlowError(path17.code, { minimum: path17.minimum, current: path17.current });
|
|
14072
|
+
if (!supportedOperations(path17).includes(operation))
|
|
14073
|
+
throw new WorkFlowError(path17.execution === "legacy" && path17.work === "core" && !operation.startsWith("work.") && operation !== "history.read" ? "execution_authority_legacy" : "work_authority_legacy", { path: path17.kind, operation });
|
|
14074
|
+
}
|
|
14075
|
+
/** Names an operation the clients deliberately do not offer, with the reason; `undefined` when it is offered. */
|
|
14076
|
+
static unsupported(type) {
|
|
14077
|
+
return workClientUnsupported[type];
|
|
14078
|
+
}
|
|
14079
|
+
async send(body, commandId) {
|
|
14080
|
+
return this.client.coordinationCommand({ version: 1, teamId: this.teamId, commandId: commandId ?? this.commandId(), ...body });
|
|
14081
|
+
}
|
|
14082
|
+
// ── Reads: canonical Work records, one page of each collection ──
|
|
14083
|
+
async readIssue(id) {
|
|
14084
|
+
await this.require("work.read");
|
|
14085
|
+
const page = { limit: this.pageSize };
|
|
14086
|
+
const issue = await this.client.workIssue(id);
|
|
14087
|
+
const [occupancy, executions, attention, evidence, history, transcripts] = await Promise.all([
|
|
14088
|
+
this.client.workIssueReservation(id),
|
|
14089
|
+
this.client.workIssueExecutions(id, page),
|
|
14090
|
+
this.client.workIssueAttention(id, page),
|
|
14091
|
+
this.client.workIssueEvidence(id, page),
|
|
14092
|
+
this.client.workIssueActivity(id, page),
|
|
14093
|
+
this.client.workThreadTranscriptLinks(issue.threadId, page)
|
|
14094
|
+
]);
|
|
14095
|
+
return { issue, occupancy, executions, attention, evidence, history, transcripts };
|
|
14096
|
+
}
|
|
14097
|
+
async readPlan(id) {
|
|
14098
|
+
await this.require("work.read");
|
|
14099
|
+
const page = { limit: this.pageSize };
|
|
14100
|
+
const [plan, issues, executions, history] = await Promise.all([
|
|
14101
|
+
this.client.workPlan(id),
|
|
14102
|
+
this.client.workIssues({ ...page, planId: id }),
|
|
14103
|
+
this.client.workPlanExecutions(id, page),
|
|
14104
|
+
this.client.workPlanActivity(id, page)
|
|
14105
|
+
]);
|
|
14106
|
+
return { plan, issues, executions, history };
|
|
14107
|
+
}
|
|
14108
|
+
// ── Start, follow-up, inspect ──
|
|
14109
|
+
/**
|
|
14110
|
+
* Starts a canonical execution of an Issue (through its reservation) or a Plan. Never a legacy Build: under legacy
|
|
14111
|
+
* execution authority this refuses before writing. An occupied Issue answers with its owner.
|
|
14112
|
+
*/
|
|
14113
|
+
async start(scope, options = {}) {
|
|
14114
|
+
await this.require("execution.start");
|
|
14115
|
+
try {
|
|
14116
|
+
const result = await this.send({ type: "execution.start", target: scope, payload: {
|
|
14117
|
+
...options.contract ? { contract: options.contract } : {},
|
|
14118
|
+
...options.limits ? { limits: options.limits } : {},
|
|
14119
|
+
...options.activation ? { activation: options.activation } : {},
|
|
14120
|
+
...options.expectedScopeRevision ? { expectedScopeRevision: options.expectedScopeRevision } : {}
|
|
14121
|
+
} }, options.commandId);
|
|
14122
|
+
return { kind: "started", command: commandProgress(result), replayed: result.replayed, execution: execution(result) };
|
|
14123
|
+
} catch (error) {
|
|
14124
|
+
if (error instanceof ClientError && error.code === "issue_run_live")
|
|
14125
|
+
return { kind: "occupied", owner: error.details.owner ?? {}, reservation: error.details.reservation ?? null };
|
|
14126
|
+
throw error;
|
|
14127
|
+
}
|
|
14128
|
+
}
|
|
14129
|
+
/** The open execution that owns a scope now: the Issue's reservation owner, or the Plan's newest open execution. */
|
|
14130
|
+
async owner(scope) {
|
|
14131
|
+
if (scope.kind === "issue") {
|
|
14132
|
+
const occupancy = await this.client.workIssueReservation(scope.id);
|
|
14133
|
+
const owner = occupancy.reservation?.owner;
|
|
14134
|
+
if (!occupancy.occupied || !owner) throw new WorkFlowError("not_occupied", { issueId: scope.id });
|
|
14135
|
+
if (owner.kind !== "execution") throw new WorkFlowError("legacy_owner", { runId: owner.runId });
|
|
14136
|
+
return this.client.workExecution(owner.executionId);
|
|
14137
|
+
}
|
|
14138
|
+
let after = null;
|
|
14139
|
+
let open;
|
|
14140
|
+
do {
|
|
14141
|
+
const page = await this.client.workPlanExecutions(scope.id, { after, limit: 100 });
|
|
14142
|
+
open = page.items.filter((e) => e.closure === null).at(-1) ?? open;
|
|
14143
|
+
after = page.nextCursor;
|
|
14144
|
+
} while (after);
|
|
14145
|
+
if (!open) throw new WorkFlowError("not_occupied", { planId: scope.id });
|
|
14146
|
+
return open;
|
|
14147
|
+
}
|
|
14148
|
+
/** Durable input for the coordinator of the scope's (or the named) open execution, recorded in its inbox. */
|
|
14149
|
+
async followUp(target, body, commandId) {
|
|
14150
|
+
await this.require("execution.follow_up");
|
|
14151
|
+
const e = target.kind === "execution" ? await this.client.workExecution(target.id) : await this.owner(target);
|
|
14152
|
+
const result = await this.send({
|
|
14153
|
+
type: "execution.message",
|
|
14154
|
+
target: { kind: "execution", id: e.id },
|
|
14155
|
+
expectedGeneration: e.generation,
|
|
14156
|
+
payload: { body }
|
|
14157
|
+
}, commandId);
|
|
14158
|
+
return { command: commandProgress(result), replayed: result.replayed, executionId: e.id, threadId: e.threadId };
|
|
14159
|
+
}
|
|
14160
|
+
/** Progress, outcomes, controls, questions, messages, specialists and their transcripts: the first page of each. */
|
|
14161
|
+
async inspect(executionId) {
|
|
14162
|
+
await this.require("execution.inspect");
|
|
14163
|
+
const page = { limit: this.pageSize };
|
|
14164
|
+
const e = await this.client.workExecution(executionId);
|
|
14165
|
+
const [outcome, controls, questions, messages, assignments, packages, reports, children, history, transcripts, recoveries] = await Promise.all([
|
|
14166
|
+
this.client.workExecutionOutcome(e.id),
|
|
14167
|
+
this.client.workExecutionControls(e.id, page),
|
|
14168
|
+
this.client.workExecutionQuestions(e.id, page),
|
|
14169
|
+
this.client.workExecutionMessages(e.id, page),
|
|
14170
|
+
this.client.workExecutionAssignments(e.id, page),
|
|
14171
|
+
this.client.workExecutionPackages(e.id, page),
|
|
14172
|
+
this.client.workExecutionReports(e.id, page),
|
|
14173
|
+
this.client.workExecutionChildren(e.id, page),
|
|
14174
|
+
this.client.workExecutionActivity(e.id, page),
|
|
14175
|
+
this.client.workThreadTranscriptLinks(e.threadId, page),
|
|
14176
|
+
this.client.workExecutionRecoveries(e.id, page)
|
|
14177
|
+
]);
|
|
14178
|
+
const cancellation = await this.cancellation(e.id);
|
|
14179
|
+
return {
|
|
14180
|
+
execution: e,
|
|
14181
|
+
outcome,
|
|
14182
|
+
recoveries,
|
|
14183
|
+
cancellation,
|
|
14184
|
+
cancel: cancellation ? cancelProgress(e, cancellation) : null,
|
|
14185
|
+
controls: { items: controls.items.map(controlProgress), nextCursor: controls.nextCursor },
|
|
14186
|
+
questions,
|
|
14187
|
+
messages: { items: messages.items.map((m) => ({ ...m, progress: messageProgress(m) })), nextCursor: messages.nextCursor },
|
|
14188
|
+
assignments,
|
|
14189
|
+
packages,
|
|
14190
|
+
reports,
|
|
14191
|
+
children,
|
|
14192
|
+
history,
|
|
14193
|
+
transcripts
|
|
14194
|
+
};
|
|
14195
|
+
}
|
|
14196
|
+
/** The newest cancellation, or null when the execution was never cancelled (the read's own `not_found`). */
|
|
14197
|
+
async cancellation(executionId) {
|
|
14198
|
+
try {
|
|
14199
|
+
return await this.client.workExecutionCancellation(executionId);
|
|
14200
|
+
} catch (error) {
|
|
14201
|
+
if (error instanceof ClientError && error.code === "not_found") return null;
|
|
14202
|
+
throw error;
|
|
14203
|
+
}
|
|
14204
|
+
}
|
|
14205
|
+
// ── Controls: requested versus effective ──
|
|
14206
|
+
/**
|
|
14207
|
+
* Holds, releases, pauses, resumes or cancels an execution at the generation the caller observed (read now when not
|
|
14208
|
+
* given). The outcome is `requested` until the Hub records the control effective.
|
|
14209
|
+
*/
|
|
14210
|
+
async control(executionId, control, options = {}) {
|
|
14211
|
+
await this.require(`execution.${control}`);
|
|
14212
|
+
const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
|
|
14213
|
+
const reason = options.reason ?? null;
|
|
14214
|
+
if (options.boundaryGraceMs !== void 0 && !(Number.isInteger(options.boundaryGraceMs) && options.boundaryGraceMs >= 0 && options.boundaryGraceMs <= maxClientCancelBoundaryGraceMs))
|
|
14215
|
+
throw new WorkFlowError("invalid_request", { field: "boundaryGraceMs", max: maxClientCancelBoundaryGraceMs });
|
|
14216
|
+
const payload = control === "cancel" ? { reason: reason ?? "Cancelled by a member", ...options.boundaryGraceMs !== void 0 ? { boundaryGraceMs: options.boundaryGraceMs } : {} } : { reason };
|
|
14217
|
+
const result = await this.send({
|
|
14218
|
+
type: `execution.${control}`,
|
|
14219
|
+
target: { kind: "execution", id: executionId },
|
|
14220
|
+
expectedGeneration: generation,
|
|
14221
|
+
payload
|
|
14222
|
+
}, options.commandId);
|
|
14223
|
+
const e = execution(result);
|
|
14224
|
+
const kind = control === "unhold" ? "unhold" : control;
|
|
14225
|
+
const requests = await this.client.workExecutionControls(e.id, { limit: this.pageSize });
|
|
14226
|
+
const request = requests.items.filter((r) => r.control === kind && r.commandId === result.commandId).at(-1);
|
|
14227
|
+
const outcome = {
|
|
14228
|
+
control,
|
|
14229
|
+
command: commandProgress(result),
|
|
14230
|
+
replayed: result.replayed,
|
|
14231
|
+
state: executionControlState(e, control),
|
|
14232
|
+
execution: e,
|
|
14233
|
+
request: request ? controlProgress(request) : null
|
|
14234
|
+
};
|
|
14235
|
+
if (control === "cancel") {
|
|
14236
|
+
const cancellation = await this.cancellation(e.id);
|
|
14237
|
+
outcome.cancellation = cancellation;
|
|
14238
|
+
outcome.cancel = cancelProgress(e, cancellation);
|
|
14239
|
+
}
|
|
14240
|
+
return outcome;
|
|
14241
|
+
}
|
|
14242
|
+
/** Publishes the next version of a specialist assignment (`expectedVersion` its current one). */
|
|
14243
|
+
async revise(assignmentId, changes, reason, options = {}) {
|
|
14244
|
+
await this.require("assignment.revise");
|
|
14245
|
+
const a = await this.client.workAssignment(assignmentId);
|
|
14246
|
+
const result = await this.send({
|
|
14247
|
+
type: "assignment.revise",
|
|
14248
|
+
target: { kind: "assignment", id: assignmentId },
|
|
14249
|
+
expectedGeneration: a.generation,
|
|
14250
|
+
expectedVersion: options.expectedVersion ?? a.assignmentVersion,
|
|
14251
|
+
payload: { changes, reason }
|
|
14252
|
+
}, options.commandId);
|
|
14253
|
+
return result;
|
|
14254
|
+
}
|
|
14255
|
+
// ── Recovery of interrupted work (DRI-325) ──
|
|
14256
|
+
/** Settles an effect of uncertain outcome by inspection, naming what was inspected. Never repeats the effect. */
|
|
14257
|
+
async reconcile(effectId, outcome, evidence, options) {
|
|
14258
|
+
await this.require("effect.reconcile");
|
|
14259
|
+
const result = await this.send({
|
|
14260
|
+
type: "effect.reconcile",
|
|
14261
|
+
target: { kind: "effect", id: effectId },
|
|
14262
|
+
expectedGeneration: options.expectedGeneration,
|
|
14263
|
+
payload: { outcome, evidence }
|
|
14264
|
+
}, options.commandId);
|
|
14265
|
+
return { command: commandProgress(result), replayed: result.replayed, execution: execution(result) };
|
|
14266
|
+
}
|
|
14267
|
+
/** Replaces an interrupted specialist Run of an open assignment with `runId`, once the old one is proven stopped. */
|
|
14268
|
+
async recover(assignmentId, input) {
|
|
14269
|
+
await this.require("assignment.recover");
|
|
14270
|
+
const a = await this.client.workAssignment(assignmentId);
|
|
14271
|
+
const { commandId, runId, ...rest } = input;
|
|
14272
|
+
const result = await this.send({
|
|
14273
|
+
type: "assignment.recover",
|
|
14274
|
+
target: { kind: "assignment", id: assignmentId },
|
|
14275
|
+
expectedGeneration: a.generation,
|
|
14276
|
+
payload: { runId: runId ?? crypto.randomUUID(), ...rest }
|
|
14277
|
+
}, commandId);
|
|
14278
|
+
return result;
|
|
14279
|
+
}
|
|
14280
|
+
/**
|
|
14281
|
+
* A member closes a cancelling execution as cancelled (or an escalated one as failed) once every Run is stopped,
|
|
14282
|
+
* acknowledging exactly the effects still outstanding (the cancellation's `blockers.effectIds`); the acknowledgement
|
|
14283
|
+
* waives their reconciliation, never the proof of stop. `effect.reconcile` settles one by inspection instead.
|
|
14284
|
+
*/
|
|
14285
|
+
async close(executionId, outcome, options = {}) {
|
|
14286
|
+
await this.require("execution.close");
|
|
14287
|
+
const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
|
|
14288
|
+
const acknowledgement = options.acknowledge?.length ? { reason: options.reason ?? "Acknowledged by a member", effectIds: options.acknowledge } : null;
|
|
14289
|
+
const result = await this.send({
|
|
14290
|
+
type: "execution.close",
|
|
14291
|
+
target: { kind: "execution", id: executionId },
|
|
14292
|
+
expectedGeneration: generation,
|
|
14293
|
+
payload: { outcome, acknowledgement }
|
|
14294
|
+
}, options.commandId);
|
|
14295
|
+
const e = execution(result);
|
|
14296
|
+
const cancellation = outcome === "cancelled" ? await this.cancellation(e.id) : null;
|
|
14297
|
+
return {
|
|
14298
|
+
command: commandProgress(result),
|
|
14299
|
+
replayed: result.replayed,
|
|
14300
|
+
execution: e,
|
|
14301
|
+
...outcome === "cancelled" ? { cancel: cancelProgress(e, cancellation), cancellation } : {}
|
|
14302
|
+
};
|
|
14303
|
+
}
|
|
14304
|
+
// ── Questions and acceptance ──
|
|
14305
|
+
/** A member answers any question; a coordinator's credential answers a technical one addressed to it (version from its brief). */
|
|
14306
|
+
async answer(questionId, answer, options = {}) {
|
|
14307
|
+
await this.require("question.answer");
|
|
14308
|
+
const version2 = options.expectedVersion ?? (options.asRun ? (await this.client.workBrief()).questions?.find((q) => q.id === questionId)?.version : (await this.client.workQuestion(questionId)).version);
|
|
14309
|
+
if (version2 === void 0) throw new WorkFlowError("question_not_addressed", { questionId });
|
|
14310
|
+
const result = await this.send({
|
|
14311
|
+
type: "question.answer",
|
|
14312
|
+
target: { kind: "question", id: questionId },
|
|
14313
|
+
expectedVersion: version2,
|
|
14314
|
+
payload: { answer }
|
|
14315
|
+
}, options.commandId);
|
|
14316
|
+
if (result.outcome.kind !== "question") throw new WorkFlowError("unexpected_outcome", { kind: result.outcome.kind });
|
|
14317
|
+
return { command: commandProgress(result), replayed: result.replayed, question: result.outcome.question, delivery: result.outcome.question.answer?.delivery ?? null };
|
|
14318
|
+
}
|
|
14319
|
+
/** Forwards a question one level up: the addressed coordinator's own credential only (a member's is `forbidden`). */
|
|
14320
|
+
async forward(questionId, note, options = {}) {
|
|
14321
|
+
await this.require("question.forward");
|
|
14322
|
+
const version2 = options.expectedVersion ?? (await this.client.workBrief()).questions?.find((q) => q.id === questionId)?.version;
|
|
14323
|
+
if (version2 === void 0) throw new WorkFlowError("question_not_addressed", { questionId });
|
|
14324
|
+
return this.send({ type: "question.forward", target: { kind: "question", id: questionId }, expectedVersion: version2, payload: { note } }, options.commandId);
|
|
14325
|
+
}
|
|
14326
|
+
/** A member accepts or rejects a root execution's package (its contract names the authority). */
|
|
14327
|
+
async decide(executionId, packageId, decision, reason, options = {}) {
|
|
14328
|
+
await this.require("execution.decide");
|
|
14329
|
+
const generation = options.expectedGeneration ?? (await this.client.workExecution(executionId)).generation;
|
|
14330
|
+
const result = await this.send({
|
|
14331
|
+
type: "execution.decide",
|
|
14332
|
+
target: { kind: "execution", id: executionId },
|
|
14333
|
+
expectedGeneration: generation,
|
|
14334
|
+
payload: { packageId, decision, reason }
|
|
14335
|
+
}, options.commandId);
|
|
14336
|
+
return { command: commandProgress(result), replayed: result.replayed, execution: execution(result), outcome: await this.client.workExecutionOutcome(executionId) };
|
|
14337
|
+
}
|
|
14338
|
+
// ── A coordinated Run's own credential ──
|
|
14339
|
+
async brief() {
|
|
14340
|
+
await this.require("run.brief");
|
|
14341
|
+
return this.client.workBrief();
|
|
14342
|
+
}
|
|
14343
|
+
/**
|
|
14344
|
+
* The specialist acknowledges a revision once its own in-flight work is reconciled. `toolUseId` is the harness tool
|
|
14345
|
+
* call carrying this command (the Runner's PreToolUse context): the Hub excludes it from the in-flight check.
|
|
14346
|
+
*/
|
|
14347
|
+
async incorporate(assignmentId, version2, options) {
|
|
14348
|
+
await this.require("assignment.incorporate");
|
|
14349
|
+
return this.send({
|
|
14350
|
+
type: "assignment.incorporate",
|
|
14351
|
+
target: { kind: "assignment", id: assignmentId },
|
|
14352
|
+
expectedGeneration: options.generation,
|
|
14353
|
+
payload: { version: version2, ...options.toolUseId ? { toolUseId: options.toolUseId } : {} }
|
|
14354
|
+
}, options.commandId);
|
|
14355
|
+
}
|
|
14356
|
+
};
|
|
14357
|
+
|
|
13835
14358
|
// ../../packages/client/src/index.ts
|
|
14359
|
+
function discussionQuery(page) {
|
|
14360
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14361
|
+
if (page.parentId) query.set("parentId", page.parentId);
|
|
14362
|
+
if (page.commentId) query.set("commentId", page.commentId);
|
|
14363
|
+
const text = query.toString();
|
|
14364
|
+
return text ? `?${text}` : "";
|
|
14365
|
+
}
|
|
14366
|
+
function relationQuery(page) {
|
|
14367
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14368
|
+
if (page.kind) query.set("kind", page.kind);
|
|
14369
|
+
if (page.state) query.set("state", page.state);
|
|
14370
|
+
const text = query.toString();
|
|
14371
|
+
return text ? `?${text}` : "";
|
|
14372
|
+
}
|
|
14373
|
+
function workQuery(page = {}) {
|
|
14374
|
+
const query = new URLSearchParams();
|
|
14375
|
+
if (page.after) query.set("after", page.after);
|
|
14376
|
+
if (page.limit !== void 0) query.set("limit", String(page.limit));
|
|
14377
|
+
if (page.planId) query.set("planId", page.planId);
|
|
14378
|
+
if (page.lifecycle) query.set("lifecycle", page.lifecycle);
|
|
14379
|
+
const text = query.toString();
|
|
14380
|
+
return text ? `?${text}` : "";
|
|
14381
|
+
}
|
|
14382
|
+
function sourceQuery(page) {
|
|
14383
|
+
const query = new URLSearchParams();
|
|
14384
|
+
for (const [key, value] of Object.entries(page))
|
|
14385
|
+
if (value !== void 0 && value !== null && value !== "") query.set(key, String(value));
|
|
14386
|
+
const text = query.toString();
|
|
14387
|
+
return text ? `?${text}` : "";
|
|
14388
|
+
}
|
|
13836
14389
|
var runArtifactSchema = external_exports.object({ name: external_exports.string(), seq: external_exports.number().int(), json: external_exports.json() });
|
|
13837
14390
|
var githubRepositorySchema = external_exports.object({
|
|
13838
14391
|
installationId: external_exports.number().int().positive(),
|
|
@@ -13842,13 +14395,15 @@ var githubRepositorySchema = external_exports.object({
|
|
|
13842
14395
|
defaultBranch: external_exports.string()
|
|
13843
14396
|
});
|
|
13844
14397
|
var ClientError = class extends Error {
|
|
13845
|
-
constructor(code, status) {
|
|
14398
|
+
constructor(code, status, details = {}) {
|
|
13846
14399
|
super(code);
|
|
13847
14400
|
this.code = code;
|
|
13848
14401
|
this.status = status;
|
|
14402
|
+
this.details = details;
|
|
13849
14403
|
}
|
|
13850
14404
|
code;
|
|
13851
14405
|
status;
|
|
14406
|
+
details;
|
|
13852
14407
|
};
|
|
13853
14408
|
var DrispClient = class _DrispClient {
|
|
13854
14409
|
constructor(baseUrl, orgId, token) {
|
|
@@ -13873,26 +14428,710 @@ var DrispClient = class _DrispClient {
|
|
|
13873
14428
|
if (!match) throw new Error("invalid_hub_url");
|
|
13874
14429
|
return new _DrispClient(url.origin, decodeURIComponent(match[1]), async () => token);
|
|
13875
14430
|
}
|
|
13876
|
-
async request(
|
|
14431
|
+
async request(path17, body) {
|
|
13877
14432
|
const response = await fetch(
|
|
13878
|
-
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${
|
|
14433
|
+
`${this.baseUrl}/v2/orgs/${encodeURIComponent(this.orgId)}${path17}`,
|
|
13879
14434
|
{
|
|
13880
14435
|
method: body === void 0 ? "GET" : "POST",
|
|
13881
14436
|
headers: {
|
|
13882
14437
|
Authorization: `Bearer ${await this.token()}`,
|
|
13883
|
-
"Content-Type": "application/json"
|
|
14438
|
+
"Content-Type": "application/json",
|
|
14439
|
+
// DRI-342: the client contract this build speaks; a Hub refuses one below its minimum (`client_upgrade_required`).
|
|
14440
|
+
[workClientContractHeader]: String(workClientContract)
|
|
13884
14441
|
},
|
|
13885
14442
|
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
13886
14443
|
}
|
|
13887
14444
|
);
|
|
13888
14445
|
const result = await response.json();
|
|
13889
|
-
if (!response.ok)
|
|
13890
|
-
|
|
14446
|
+
if (!response.ok) {
|
|
14447
|
+
const { error, ...details } = result;
|
|
14448
|
+
throw new ClientError(error ?? `HTTP ${response.status}`, response.status, details);
|
|
14449
|
+
}
|
|
13891
14450
|
return result;
|
|
13892
14451
|
}
|
|
13893
14452
|
submit(command) {
|
|
13894
14453
|
return this.request("/commands", commandSchema.parse(command));
|
|
13895
14454
|
}
|
|
14455
|
+
async tracker() {
|
|
14456
|
+
return trackerSnapshotSchema.parse(await this.request("/tracker"));
|
|
14457
|
+
}
|
|
14458
|
+
async trackerIssue(id) {
|
|
14459
|
+
return trackerIssueSchema.parse(await this.request(`/tracker/issues/${encodeURIComponent(id)}`));
|
|
14460
|
+
}
|
|
14461
|
+
trackerOperation(operation) {
|
|
14462
|
+
return this.request("/tracker", trackerOperationSchema.parse(operation));
|
|
14463
|
+
}
|
|
14464
|
+
async previewLinearImport(input) {
|
|
14465
|
+
return trackerImportPreviewSchema.parse(await this.trackerOperation({ kind: "import.preview", input }));
|
|
14466
|
+
}
|
|
14467
|
+
async commitLinearImport(previewId) {
|
|
14468
|
+
return trackerSnapshotSchema.parse(await this.trackerOperation({ kind: "import.commit", previewId }));
|
|
14469
|
+
}
|
|
14470
|
+
startIssue(id, command) {
|
|
14471
|
+
return this.request(`/tracker/issues/${encodeURIComponent(id)}/start`, commandSchema.parse(command));
|
|
14472
|
+
}
|
|
14473
|
+
// ── Attention requests and execution policy (DRI-300) ──
|
|
14474
|
+
/**
|
|
14475
|
+
* Sends one attention or execution-policy command (`attention.request`, `attention.resolve`, `attention.sync_run`,
|
|
14476
|
+
* `execution_policy.hold`, `execution_policy.resume`, `execution_policy.set_agent_eligibility`) on the Work
|
|
14477
|
+
* command route, with its receipts and replay. Resolving attention never answers a Run: answer through `submit`.
|
|
14478
|
+
*/
|
|
14479
|
+
async workAttentionCommand(command) {
|
|
14480
|
+
return workAttentionCommandResultSchema.parse(await this.request("/work/commands", workAttentionCommandSchema.parse(command)));
|
|
14481
|
+
}
|
|
14482
|
+
/** An Issue's attention requests, oldest first; `state` narrows to open (its Needs Human) or resolved ones. */
|
|
14483
|
+
async workIssueAttention(id, page = {}) {
|
|
14484
|
+
const query = workQuery(page);
|
|
14485
|
+
const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
|
|
14486
|
+
return workAttentionRequestPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/attention${query}${state}`));
|
|
14487
|
+
}
|
|
14488
|
+
async workAttentionRequest(id) {
|
|
14489
|
+
return workAttentionRequestSchema.parse(await this.request(`/work/attention-requests/${encodeURIComponent(id)}`));
|
|
14490
|
+
}
|
|
14491
|
+
async workAttentionActivity(id, page = {}) {
|
|
14492
|
+
return workAttentionActivityPageSchema.parse(
|
|
14493
|
+
await this.request(`/work/attention-requests/${encodeURIComponent(id)}/activity${workQuery(page)}`)
|
|
14494
|
+
);
|
|
14495
|
+
}
|
|
14496
|
+
async workExecutionPolicy(issueId) {
|
|
14497
|
+
return workExecutionPolicySchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/execution-policy`));
|
|
14498
|
+
}
|
|
14499
|
+
async workExecutionPolicyActivity(issueId, page = {}) {
|
|
14500
|
+
return workAttentionActivityPageSchema.parse(
|
|
14501
|
+
await this.request(`/work/issues/${encodeURIComponent(issueId)}/execution-policy/activity${workQuery(page)}`)
|
|
14502
|
+
);
|
|
14503
|
+
}
|
|
14504
|
+
// ── end DRI-300 ──
|
|
14505
|
+
/**
|
|
14506
|
+
* Sends one versioned Team command to the Work core (DRI-294). The caller retains `commandId` for retries: an
|
|
14507
|
+
* identical retry returns the original result with `replayed: true`; a changed payload is `idempotency_conflict`.
|
|
14508
|
+
*/
|
|
14509
|
+
async workCommand(command) {
|
|
14510
|
+
return workCommandResultSchema.parse(await this.request("/work/commands", workCommandSchema.parse(command)));
|
|
14511
|
+
}
|
|
14512
|
+
async workIssue(id) {
|
|
14513
|
+
return workIssueSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}`));
|
|
14514
|
+
}
|
|
14515
|
+
/** Issues by number: active only unless `lifecycle` asks for `archived`, `deleted` or `all` (DRI-304). */
|
|
14516
|
+
async workIssues(page = {}) {
|
|
14517
|
+
return workIssuePageSchema.parse(await this.request(`/work/issues${workQuery(page)}`));
|
|
14518
|
+
}
|
|
14519
|
+
async workIssueActivity(id, page = {}) {
|
|
14520
|
+
return workActivityPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/activity${workQuery(page)}`));
|
|
14521
|
+
}
|
|
14522
|
+
async workIssueScopeRevisions(id, page = {}) {
|
|
14523
|
+
return workScopeRevisionPageSchema.parse(
|
|
14524
|
+
await this.request(`/work/issues/${encodeURIComponent(id)}/scope-revisions${workQuery(page)}`)
|
|
14525
|
+
);
|
|
14526
|
+
}
|
|
14527
|
+
async workPlan(id) {
|
|
14528
|
+
return workPlanSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}`));
|
|
14529
|
+
}
|
|
14530
|
+
/** Plans by number (DRI-296): active only unless `lifecycle` asks for `archived`, `deleted` or `all` (DRI-304). */
|
|
14531
|
+
async workPlans(page = {}) {
|
|
14532
|
+
return workPlanPageSchema.parse(await this.request(`/work/plans${workQuery(page)}`));
|
|
14533
|
+
}
|
|
14534
|
+
async workPlanActivity(id, page = {}) {
|
|
14535
|
+
return workActivityPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}/activity${workQuery(page)}`));
|
|
14536
|
+
}
|
|
14537
|
+
// ── Sub-Issue trees (DRI-296); `plan.archive`, `plan.unarchive`, `issue.set_plan`, `issue.set_parent` go through `workCommand` ──
|
|
14538
|
+
/** An Issue's direct children, by number. */
|
|
14539
|
+
async workIssueChildren(id, page = {}) {
|
|
14540
|
+
return workIssuePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/children${workQuery(page)}`));
|
|
14541
|
+
}
|
|
14542
|
+
/** An Issue's ancestors, nearest first; each page continues from the last ancestor returned. */
|
|
14543
|
+
async workIssueAncestors(id, page = {}) {
|
|
14544
|
+
return workIssuePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/ancestors${workQuery(page)}`));
|
|
14545
|
+
}
|
|
14546
|
+
// ── end DRI-296 ──
|
|
14547
|
+
// ── Lifecycle (DRI-304); `issue.archive`, `issue.delete`, `issue.restore`, `plan.delete`, `plan.restore` go through `workCommand` ──
|
|
14548
|
+
/** An Issue's lifecycle events (completion, cancellation, reopening, archival, tombstone, restoration), oldest first. */
|
|
14549
|
+
async workIssueLifecycle(id, page = {}) {
|
|
14550
|
+
return workLifecycleEventPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/lifecycle${workQuery(page)}`));
|
|
14551
|
+
}
|
|
14552
|
+
async workPlanLifecycle(id, page = {}) {
|
|
14553
|
+
return workLifecycleEventPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(id)}/lifecycle${workQuery(page)}`));
|
|
14554
|
+
}
|
|
14555
|
+
/** Who holds a complete source key and whether that holder is tombstoned: the contract imports consume. */
|
|
14556
|
+
async workSourceKeyLifecycle(key) {
|
|
14557
|
+
return workSourceKeyLifecycleSchema.parse(await this.request(`/work/source-references/lifecycle?${new URLSearchParams(key)}`));
|
|
14558
|
+
}
|
|
14559
|
+
// ── end DRI-304 ──
|
|
14560
|
+
/**
|
|
14561
|
+
* Sends one delivery workflow command (`workflow.create`, `workflow.publish`, `workflow.activate`; DRI-297). Team
|
|
14562
|
+
* admin authority; the same envelope, receipts and replay as `workCommand`.
|
|
14563
|
+
*/
|
|
14564
|
+
async workflowCommand(command) {
|
|
14565
|
+
return workCommandResultSchema.parse(await this.request("/work/delivery-workflows/commands", workCommandSchema.parse(command)));
|
|
14566
|
+
}
|
|
14567
|
+
async deliveryWorkflows(page = {}) {
|
|
14568
|
+
return workDeliveryWorkflowPageSchema.parse(await this.request(`/work/delivery-workflows${workQuery(page)}`));
|
|
14569
|
+
}
|
|
14570
|
+
/** One delivery workflow; `active` names the Team's active one. */
|
|
14571
|
+
async deliveryWorkflow(id = "active") {
|
|
14572
|
+
return workDeliveryWorkflowSchema.parse(await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}`));
|
|
14573
|
+
}
|
|
14574
|
+
async deliveryWorkflowRevision(id, revision) {
|
|
14575
|
+
return workDeliveryWorkflowRevisionSchema.parse(
|
|
14576
|
+
await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}/revisions/${revision}`)
|
|
14577
|
+
);
|
|
14578
|
+
}
|
|
14579
|
+
async deliveryWorkflowRevisions(id, page = {}) {
|
|
14580
|
+
return workDeliveryWorkflowRevisionPageSchema.parse(
|
|
14581
|
+
await this.request(`/work/delivery-workflows/${encodeURIComponent(id)}/revisions${workQuery(page)}`)
|
|
14582
|
+
);
|
|
14583
|
+
}
|
|
14584
|
+
async deliveryMigrations(page = {}) {
|
|
14585
|
+
return workDeliveryMigrationPageSchema.parse(await this.request(`/work/delivery-migrations${workQuery(page)}`));
|
|
14586
|
+
}
|
|
14587
|
+
/** The moves out of an Issue's current status, each evaluated for the caller by the Hub's gate evaluator. */
|
|
14588
|
+
async workIssueTransitions(id) {
|
|
14589
|
+
return workIssueTransitionsSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/transitions`));
|
|
14590
|
+
}
|
|
14591
|
+
// ── Source identities and attribution (DRI-302) ──
|
|
14592
|
+
/**
|
|
14593
|
+
* Sends one source command (`source_space.register`, `source_principal.record`, `source_principal.map`,
|
|
14594
|
+
* `source_reference.record`, `observe`, `associate`, `alias_key`) on the Work command route, with its receipts.
|
|
14595
|
+
*/
|
|
14596
|
+
async workSourceCommand(command) {
|
|
14597
|
+
return workSourceCommandResultSchema.parse(await this.request("/work/commands", workSourceCommandSchema.parse(command)));
|
|
14598
|
+
}
|
|
14599
|
+
async workSourceSpaces(page = {}) {
|
|
14600
|
+
return workSourceSpacePageSchema.parse(await this.request(`/work/source-spaces${workQuery(page)}`));
|
|
14601
|
+
}
|
|
14602
|
+
async workSourceSpace(id) {
|
|
14603
|
+
return workSourceSpaceSchema.parse(await this.request(`/work/source-spaces/${encodeURIComponent(id)}`));
|
|
14604
|
+
}
|
|
14605
|
+
async workSourcePrincipals(page = {}) {
|
|
14606
|
+
return workSourcePrincipalPageSchema.parse(await this.request(`/work/source-principals${sourceQuery(page)}`));
|
|
14607
|
+
}
|
|
14608
|
+
async workSourcePrincipal(id) {
|
|
14609
|
+
return workSourcePrincipalSchema.parse(await this.request(`/work/source-principals/${encodeURIComponent(id)}`));
|
|
14610
|
+
}
|
|
14611
|
+
/** References, optionally of one space or carrying a readable identifier now or formerly (an alias, not identity). */
|
|
14612
|
+
async workSourceReferences(page = {}) {
|
|
14613
|
+
return workSourceReferencePageSchema.parse(await this.request(`/work/source-references${sourceQuery(page)}`));
|
|
14614
|
+
}
|
|
14615
|
+
async workSourceReference(id) {
|
|
14616
|
+
return workSourceReferenceSchema.parse(await this.request(`/work/source-references/${encodeURIComponent(id)}`));
|
|
14617
|
+
}
|
|
14618
|
+
/** The reference holding a complete source key (canonical or alias) in this Team; `not_found` otherwise. */
|
|
14619
|
+
async workResolveSource(key) {
|
|
14620
|
+
return workSourceReferenceSchema.parse(await this.request(`/work/source-references/resolve?${new URLSearchParams(key)}`));
|
|
14621
|
+
}
|
|
14622
|
+
async workSourceAliases(id, page = {}) {
|
|
14623
|
+
return workSourceAliasPageSchema.parse(await this.request(`/work/source-references/${encodeURIComponent(id)}/aliases${workQuery(page)}`));
|
|
14624
|
+
}
|
|
14625
|
+
async workSourceAssociations(id, page = {}) {
|
|
14626
|
+
return workSourceAssociationPageSchema.parse(
|
|
14627
|
+
await this.request(`/work/source-references/${encodeURIComponent(id)}/associations${workQuery(page)}`)
|
|
14628
|
+
);
|
|
14629
|
+
}
|
|
14630
|
+
async workSourceActivity(subject, page = {}) {
|
|
14631
|
+
const collection = { source_space: "source-spaces", source_principal: "source-principals", source_reference: "source-references" }[subject.kind];
|
|
14632
|
+
return workSourceActivityPageSchema.parse(
|
|
14633
|
+
await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
|
|
14634
|
+
);
|
|
14635
|
+
}
|
|
14636
|
+
/** The source references associated with an Issue or Plan now. */
|
|
14637
|
+
async workOwnerSourceReferences(owner, page = {}) {
|
|
14638
|
+
return workSourceReferencePageSchema.parse(
|
|
14639
|
+
await this.request(`/work/${owner.kind}s/${encodeURIComponent(owner.id)}/source-references${workQuery(page)}`)
|
|
14640
|
+
);
|
|
14641
|
+
}
|
|
14642
|
+
/** Every association change to or from an Issue or Plan, including references since moved elsewhere. */
|
|
14643
|
+
async workOwnerSourceHistory(owner, page = {}) {
|
|
14644
|
+
return workSourceAssociationPageSchema.parse(
|
|
14645
|
+
await this.request(`/work/${owner.kind}s/${encodeURIComponent(owner.id)}/source-history${workQuery(page)}`)
|
|
14646
|
+
);
|
|
14647
|
+
}
|
|
14648
|
+
// ── end DRI-302 ──
|
|
14649
|
+
// ── Assignees and Team labels (DRI-295) ──
|
|
14650
|
+
/**
|
|
14651
|
+
* Sends one membership or label command (`issue_membership.assign`, `issue_membership.unassign`,
|
|
14652
|
+
* `issue_membership.add_label`, `issue_membership.remove_label` at the Issue's membership version; `label.create`,
|
|
14653
|
+
* `label.edit`) on the Work command route, with its receipts and replay.
|
|
14654
|
+
*/
|
|
14655
|
+
async workMembershipCommand(command) {
|
|
14656
|
+
return workMembershipCommandResultSchema.parse(await this.request("/work/commands", workMembershipCommandSchema.parse(command)));
|
|
14657
|
+
}
|
|
14658
|
+
/** The Issue's membership version (what a membership command names) and its current counts. */
|
|
14659
|
+
async workIssueMembership(id) {
|
|
14660
|
+
return workIssueMembershipSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/membership`));
|
|
14661
|
+
}
|
|
14662
|
+
async workIssueMembershipActivity(id, page = {}) {
|
|
14663
|
+
return workMembershipActivityPageSchema.parse(
|
|
14664
|
+
await this.request(`/work/issues/${encodeURIComponent(id)}/membership/activity${workQuery(page)}`)
|
|
14665
|
+
);
|
|
14666
|
+
}
|
|
14667
|
+
/** An Issue's current assignees, oldest first; `state: 'all'` includes removed periods. */
|
|
14668
|
+
async workIssueAssignees(id, page = {}) {
|
|
14669
|
+
const query = workQuery(page);
|
|
14670
|
+
const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
|
|
14671
|
+
return workIssueAssigneePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/assignees${query}${state}`));
|
|
14672
|
+
}
|
|
14673
|
+
/** An Issue's label memberships, each with the label's current record; `state: 'all'` includes removed ones. */
|
|
14674
|
+
async workIssueLabels(id, page = {}) {
|
|
14675
|
+
const query = workQuery(page);
|
|
14676
|
+
const state = page.state ? `${query ? "&" : "?"}state=${page.state}` : "";
|
|
14677
|
+
return workIssueLabelPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/labels${query}${state}`));
|
|
14678
|
+
}
|
|
14679
|
+
async workLabels(page = {}) {
|
|
14680
|
+
return workLabelPageSchema.parse(await this.request(`/work/labels${workQuery(page)}`));
|
|
14681
|
+
}
|
|
14682
|
+
async workLabel(id) {
|
|
14683
|
+
return workLabelSchema.parse(await this.request(`/work/labels/${encodeURIComponent(id)}`));
|
|
14684
|
+
}
|
|
14685
|
+
/** Issues currently carrying the label, by number. */
|
|
14686
|
+
async workLabelIssues(id, page = {}) {
|
|
14687
|
+
return workLabelIssuePageSchema.parse(await this.request(`/work/labels/${encodeURIComponent(id)}/issues${workQuery(page)}`));
|
|
14688
|
+
}
|
|
14689
|
+
async workLabelActivity(id, page = {}) {
|
|
14690
|
+
return workMembershipActivityPageSchema.parse(
|
|
14691
|
+
await this.request(`/work/labels/${encodeURIComponent(id)}/activity${workQuery(page)}`)
|
|
14692
|
+
);
|
|
14693
|
+
}
|
|
14694
|
+
// ── end DRI-295 ──
|
|
14695
|
+
// ── Delivery candidates and evidence (DRI-298) ──
|
|
14696
|
+
/**
|
|
14697
|
+
* Sends one candidate or evidence command (`candidate.propose`, `candidate.select` at the selection's version,
|
|
14698
|
+
* `evidence.record`, `evidence.retract` and `evidence.accept` at the evidence's version) on the Work command route,
|
|
14699
|
+
* with its receipts and replay. Recorded evidence may advance the Issue automatically; the result says how.
|
|
14700
|
+
*/
|
|
14701
|
+
async workEvidenceCommand(command) {
|
|
14702
|
+
return workEvidenceCommandResultSchema.parse(await this.request("/work/commands", workEvidenceCommandSchema.parse(command)));
|
|
14703
|
+
}
|
|
14704
|
+
async workIssueCandidates(id, page = {}) {
|
|
14705
|
+
return workCandidatePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/candidates${workQuery(page)}`));
|
|
14706
|
+
}
|
|
14707
|
+
/** The Issue's current-candidate selection and its version. */
|
|
14708
|
+
async workIssueCandidate(id) {
|
|
14709
|
+
return workCandidateSelectionSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/candidate`));
|
|
14710
|
+
}
|
|
14711
|
+
async workIssueCandidateActivity(id, page = {}) {
|
|
14712
|
+
return workEvidenceActivityPageSchema.parse(
|
|
14713
|
+
await this.request(`/work/issues/${encodeURIComponent(id)}/candidate/activity${workQuery(page)}`)
|
|
14714
|
+
);
|
|
14715
|
+
}
|
|
14716
|
+
/** An Issue's evidence, oldest first, each with its applicability now; narrow by candidate or kind. */
|
|
14717
|
+
async workIssueEvidence(id, page = {}) {
|
|
14718
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14719
|
+
if (page.candidateId) query.set("candidateId", page.candidateId);
|
|
14720
|
+
if (page.kind) query.set("kind", page.kind);
|
|
14721
|
+
const text = query.toString();
|
|
14722
|
+
return workEvidencePageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/evidence${text ? `?${text}` : ""}`));
|
|
14723
|
+
}
|
|
14724
|
+
async workCandidate(id) {
|
|
14725
|
+
return workCandidateSchema.parse(await this.request(`/work/candidates/${encodeURIComponent(id)}`));
|
|
14726
|
+
}
|
|
14727
|
+
async workCandidateEvidence(id, page = {}) {
|
|
14728
|
+
return workEvidencePageSchema.parse(await this.request(`/work/candidates/${encodeURIComponent(id)}/evidence${workQuery(page)}`));
|
|
14729
|
+
}
|
|
14730
|
+
async workEvidence(id) {
|
|
14731
|
+
return workEvidenceSchema.parse(await this.request(`/work/evidence/${encodeURIComponent(id)}`));
|
|
14732
|
+
}
|
|
14733
|
+
/** A candidate's or an evidence record's history, oldest first. */
|
|
14734
|
+
async workEvidenceActivity(subject, page = {}) {
|
|
14735
|
+
const collection = subject.kind === "candidate" ? "candidates" : "evidence";
|
|
14736
|
+
return workEvidenceActivityPageSchema.parse(
|
|
14737
|
+
await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
|
|
14738
|
+
);
|
|
14739
|
+
}
|
|
14740
|
+
// ── end DRI-298 ──
|
|
14741
|
+
// ── Deployments, verification attempts and production confirmation (DRI-299) ──
|
|
14742
|
+
/**
|
|
14743
|
+
* Sends one deployment or verification command (`deployment.record`, `deployment.retire` at the deployment's
|
|
14744
|
+
* version, `verification.start`) on the Work command route. QA results and production confirmation are evidence
|
|
14745
|
+
* commands (`workEvidenceCommand` with `evidence.record` naming the attempt, or `production.confirm`).
|
|
14746
|
+
*/
|
|
14747
|
+
async workDeploymentCommand(command) {
|
|
14748
|
+
return workDeploymentCommandResultSchema.parse(await this.request("/work/commands", workDeploymentCommandSchema.parse(command)));
|
|
14749
|
+
}
|
|
14750
|
+
/** An Issue's deployments in recording order; narrow by candidate, environment or lifecycle. */
|
|
14751
|
+
async workIssueDeployments(id, page = {}) {
|
|
14752
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14753
|
+
for (const key of ["candidateId", "environment", "lifecycle"]) if (page[key]) query.set(key, page[key]);
|
|
14754
|
+
const text = query.toString();
|
|
14755
|
+
return workDeploymentPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/deployments${text ? `?${text}` : ""}`));
|
|
14756
|
+
}
|
|
14757
|
+
/** An Issue's verification attempts in start order, each with its outcome and standing now. */
|
|
14758
|
+
async workIssueVerificationAttempts(id, page = {}) {
|
|
14759
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14760
|
+
if (page.deploymentId) query.set("deploymentId", page.deploymentId);
|
|
14761
|
+
const text = query.toString();
|
|
14762
|
+
return workVerificationAttemptPageSchema.parse(
|
|
14763
|
+
await this.request(`/work/issues/${encodeURIComponent(id)}/verification-attempts${text ? `?${text}` : ""}`)
|
|
14764
|
+
);
|
|
14765
|
+
}
|
|
14766
|
+
/** An Issue's staging-to-production trail: its production confirmations with the records they name. */
|
|
14767
|
+
async workIssueProductionTrail(id, page = {}) {
|
|
14768
|
+
return workProductionTrailPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/production-trail${workQuery(page)}`));
|
|
14769
|
+
}
|
|
14770
|
+
async workDeployment(id) {
|
|
14771
|
+
return workDeploymentSchema.parse(await this.request(`/work/deployments/${encodeURIComponent(id)}`));
|
|
14772
|
+
}
|
|
14773
|
+
async workDeploymentAttempts(id, page = {}) {
|
|
14774
|
+
return workVerificationAttemptPageSchema.parse(await this.request(`/work/deployments/${encodeURIComponent(id)}/attempts${workQuery(page)}`));
|
|
14775
|
+
}
|
|
14776
|
+
async workVerificationAttempt(id) {
|
|
14777
|
+
return workVerificationAttemptSchema.parse(await this.request(`/work/verification-attempts/${encodeURIComponent(id)}`));
|
|
14778
|
+
}
|
|
14779
|
+
/** A deployment's or a verification attempt's history, oldest first. */
|
|
14780
|
+
async workDeploymentActivity(subject, page = {}) {
|
|
14781
|
+
const collection = subject.kind === "deployment" ? "deployments" : "verification-attempts";
|
|
14782
|
+
return workDeploymentActivityPageSchema.parse(
|
|
14783
|
+
await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
|
|
14784
|
+
);
|
|
14785
|
+
}
|
|
14786
|
+
// ── end DRI-299 ──
|
|
14787
|
+
// ── Dependencies, duplicates and related Issues (DRI-301) ──
|
|
14788
|
+
/** Sends one relation command (`issue_relation.add`, `issue_relation.remove`) on the Work command route. */
|
|
14789
|
+
async workRelationCommand(command) {
|
|
14790
|
+
return workRelationCommandResultSchema.parse(await this.request("/work/commands", workRelationCommandSchema.parse(command)));
|
|
14791
|
+
}
|
|
14792
|
+
/**
|
|
14793
|
+
* An Issue's own relations, oldest first: its prerequisites, its canonical Issue and its related links (either side);
|
|
14794
|
+
* `kind` narrows to one, `state: 'all'` includes removed periods.
|
|
14795
|
+
*/
|
|
14796
|
+
async workIssueRelations(id, page = {}) {
|
|
14797
|
+
return workIssueRelationPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/relations${relationQuery(page)}`));
|
|
14798
|
+
}
|
|
14799
|
+
/** The Issues blocked by this one (`dependents`) or marked duplicates of it (`duplicates`), oldest first. */
|
|
14800
|
+
async workIssueInverseRelations(id, kind, page = {}) {
|
|
14801
|
+
return workIssueRelationPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/${kind}${relationQuery(page)}`));
|
|
14802
|
+
}
|
|
14803
|
+
/** Whether every prerequisite of the Issue is satisfied now, and a page of the unsatisfied ones. */
|
|
14804
|
+
async workDependencyGate(id, page = {}) {
|
|
14805
|
+
return workDependencyGateSchema.parse(await this.request(`/work/issues/${encodeURIComponent(id)}/dependency-gate${workQuery(page)}`));
|
|
14806
|
+
}
|
|
14807
|
+
async workRelation(id) {
|
|
14808
|
+
return workIssueRelationSchema.parse(await this.request(`/work/relations/${encodeURIComponent(id)}`));
|
|
14809
|
+
}
|
|
14810
|
+
async workRelationActivity(id, page = {}) {
|
|
14811
|
+
return workRelationActivityPageSchema.parse(await this.request(`/work/relations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
|
|
14812
|
+
}
|
|
14813
|
+
// ── end DRI-301 ──
|
|
14814
|
+
// ── Pending imported relations (DRI-308) ──
|
|
14815
|
+
/** Sends `pending_relation.dismiss` or `pending_relation.resolve` on the Work command route. */
|
|
14816
|
+
async workPendingRelationCommand(command) {
|
|
14817
|
+
return workPendingRelationCommandResultSchema.parse(await this.request("/work/commands", workPendingRelationCommandSchema.parse(command)));
|
|
14818
|
+
}
|
|
14819
|
+
/** The pending imported relations naming an Issue (`issue`) or recorded by an import batch (`import`), oldest first. */
|
|
14820
|
+
async workPendingRelations(of, page = {}) {
|
|
14821
|
+
const query = new URLSearchParams(workQuery(page).slice(1));
|
|
14822
|
+
if (page.disposition) query.set("disposition", page.disposition);
|
|
14823
|
+
const text = query.toString();
|
|
14824
|
+
return workPendingRelationPageSchema.parse(await this.request(
|
|
14825
|
+
`/work/${of.kind === "issue" ? "issues" : "imports"}/${encodeURIComponent(of.id)}/pending-relations${text ? `?${text}` : ""}`
|
|
14826
|
+
));
|
|
14827
|
+
}
|
|
14828
|
+
async workPendingRelation(id) {
|
|
14829
|
+
return workPendingRelationSchema.parse(await this.request(`/work/pending-relations/${encodeURIComponent(id)}`));
|
|
14830
|
+
}
|
|
14831
|
+
async workPendingRelationActivity(id, page = {}) {
|
|
14832
|
+
return workPendingRelationActivityPageSchema.parse(await this.request(`/work/pending-relations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
|
|
14833
|
+
}
|
|
14834
|
+
// ── end DRI-308 ──
|
|
14835
|
+
// ── Execution reservations and canonical admission (DRI-310) ──
|
|
14836
|
+
/**
|
|
14837
|
+
* Sends one reservation command (`legacy_alias.record`, Team admin) on the Work command route, with its receipts
|
|
14838
|
+
* and replay. DRI-314 retired the provisional lifecycle commands: Coordination's own (`coordinationCommand`) start,
|
|
14839
|
+
* bind and close executions.
|
|
14840
|
+
*/
|
|
14841
|
+
async workReservationCommand(command) {
|
|
14842
|
+
return workReservationCommandResultSchema.parse(await this.request("/work/commands", workReservationCommandSchema.parse(command)));
|
|
14843
|
+
}
|
|
14844
|
+
/** An Issue's occupancy: its ownership generation and held reservation, if any. */
|
|
14845
|
+
async workIssueReservation(issueId) {
|
|
14846
|
+
return workIssueReservationSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/reservation`));
|
|
14847
|
+
}
|
|
14848
|
+
/** Every reservation an Issue has had, oldest first. */
|
|
14849
|
+
async workIssueReservations(issueId, page = {}) {
|
|
14850
|
+
return workExecutionReservationPageSchema.parse(
|
|
14851
|
+
await this.request(`/work/issues/${encodeURIComponent(issueId)}/reservations${workQuery(page)}`)
|
|
14852
|
+
);
|
|
14853
|
+
}
|
|
14854
|
+
async workReservation(id) {
|
|
14855
|
+
return workExecutionReservationSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}`));
|
|
14856
|
+
}
|
|
14857
|
+
async workReservationBindings(id, page = {}) {
|
|
14858
|
+
return workExecutionBindingPageSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}/bindings${workQuery(page)}`));
|
|
14859
|
+
}
|
|
14860
|
+
async workReservationActivity(id, page = {}) {
|
|
14861
|
+
return workReservationActivityPageSchema.parse(await this.request(`/work/reservations/${encodeURIComponent(id)}/activity${workQuery(page)}`));
|
|
14862
|
+
}
|
|
14863
|
+
/** A Run's one authoritative binding; `not_found` for a Run admitted outside any canonical Issue. */
|
|
14864
|
+
async workRunBinding(runId) {
|
|
14865
|
+
return workExecutionBindingSchema.parse(await this.request(`/work/runs/${encodeURIComponent(runId)}/binding`));
|
|
14866
|
+
}
|
|
14867
|
+
/** Run control's record of a dispatched coordinated Run: grants, fencing refusal, receipts and physical stop (DRI-340). */
|
|
14868
|
+
async workRunControl(runId) {
|
|
14869
|
+
return runControlRecordSchema.parse(await this.request(`/work/runs/${encodeURIComponent(runId)}/control`));
|
|
14870
|
+
}
|
|
14871
|
+
/** `run_control.acknowledge_lost` (Team admin): a Run whose stop is unproven is accepted as no longer running (DRI-340). */
|
|
14872
|
+
async runControlCommand(command) {
|
|
14873
|
+
return runControlCommandResultSchema.parse(await this.request("/work/commands", runControlCommandSchema.parse(command)));
|
|
14874
|
+
}
|
|
14875
|
+
/** The Issue an exact legacy reference resolves to; anything else is `not_found`. */
|
|
14876
|
+
async workResolveLegacyAlias(alias) {
|
|
14877
|
+
return workLegacyAliasSchema.parse(await this.request(`/work/legacy-aliases/resolve?${new URLSearchParams({ alias })}`));
|
|
14878
|
+
}
|
|
14879
|
+
// ── end DRI-310 ──
|
|
14880
|
+
// ── Coordination (DRI-314) ──
|
|
14881
|
+
/**
|
|
14882
|
+
* Sends one Coordination command on the Work command route, with its receipts and replay: a member's
|
|
14883
|
+
* `execution.start`, `.control`, `.cancel`, `.close`, `.decide`, `.amend`, `.message`, or the provisional synthetic
|
|
14884
|
+
* driver's `coordinator.*` and `run.fact` (Team admin). A stale generation is `stale_generation`.
|
|
14885
|
+
*/
|
|
14886
|
+
async coordinationCommand(command) {
|
|
14887
|
+
return coordinationCommandResultSchema.parse(await this.request("/work/commands", coordinationCommandSchema.parse(command)));
|
|
14888
|
+
}
|
|
14889
|
+
async workExecution(id) {
|
|
14890
|
+
return executionSchema.parse(await this.request(`/work/executions/${encodeURIComponent(id)}`));
|
|
14891
|
+
}
|
|
14892
|
+
/** Every execution of an Issue, oldest first: its predecessors and the current one. */
|
|
14893
|
+
async workIssueExecutions(issueId, page = {}) {
|
|
14894
|
+
return executionPageSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/executions${workQuery(page)}`));
|
|
14895
|
+
}
|
|
14896
|
+
async workCoordinator(id) {
|
|
14897
|
+
return coordinatorSchema.parse(await this.request(`/work/coordinators/${encodeURIComponent(id)}`));
|
|
14898
|
+
}
|
|
14899
|
+
/** Measured spend across every execution of an Issue. */
|
|
14900
|
+
async workIssueSpend(issueId) {
|
|
14901
|
+
return coordinationSpendSchema.parse(await this.request(`/work/issues/${encodeURIComponent(issueId)}/spend`));
|
|
14902
|
+
}
|
|
14903
|
+
// ── Coordinator activation and specialist assignment (DRI-317) ──
|
|
14904
|
+
/**
|
|
14905
|
+
* With a coordinated Run's scoped credential (its grant's token as the bearer): what that Run may read about its own
|
|
14906
|
+
* work. The coordinator sees its execution's assignments, packages and reports; a specialist its own assignment.
|
|
14907
|
+
* A member's token is `forbidden`. Commands go through `coordinationCommand` with the same credential.
|
|
14908
|
+
*/
|
|
14909
|
+
async workBrief() {
|
|
14910
|
+
return coordinationBriefSchema.parse(await this.request("/work/brief"));
|
|
14911
|
+
}
|
|
14912
|
+
/** An execution's coordinator activation: its Run, binding and coordinator Workflow. */
|
|
14913
|
+
async workExecutionActivation(executionId) {
|
|
14914
|
+
return executionActivationSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/activation`));
|
|
14915
|
+
}
|
|
14916
|
+
/** The specialist reports exposed on an execution's canonical Thread, oldest first. */
|
|
14917
|
+
async workExecutionReports(executionId, page = {}) {
|
|
14918
|
+
return workPageSchema(assignmentReportSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/reports${workQuery(page)}`));
|
|
14919
|
+
}
|
|
14920
|
+
// ── end DRI-317 ──
|
|
14921
|
+
// ── Coordinator wake-up (DRI-318) ──
|
|
14922
|
+
/** An execution's coordinator inbox, every event oldest first, with each event's acknowledgement. */
|
|
14923
|
+
async workExecutionInbox(executionId, page = {}) {
|
|
14924
|
+
return workPageSchema(coordinationInboxEventSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/inbox${workQuery(page)}`));
|
|
14925
|
+
}
|
|
14926
|
+
/** An execution's InboxCursor: acknowledged through, newest event, wake-ups, the activation asleep. */
|
|
14927
|
+
async workExecutionCursor(executionId) {
|
|
14928
|
+
return inboxCursorSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/cursor`));
|
|
14929
|
+
}
|
|
14930
|
+
// ── end DRI-318 ──
|
|
14931
|
+
// ── Recovery of interrupted work (DRI-325) ──
|
|
14932
|
+
/** An execution's recovery episodes, oldest first, each with its append-only steps. */
|
|
14933
|
+
async workExecutionRecoveries(executionId, page = {}) {
|
|
14934
|
+
return workPageSchema(recoveryEpisodeSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/recoveries${workQuery(page)}`));
|
|
14935
|
+
}
|
|
14936
|
+
// ── end DRI-325 ──
|
|
14937
|
+
// ── Hold, pause and revise (DRI-323) ──
|
|
14938
|
+
/** An execution's control requests, oldest first: requested versus effective, each specialist Run's part, a hold's wait. */
|
|
14939
|
+
async workExecutionControls(executionId, page = {}) {
|
|
14940
|
+
return workPageSchema(controlRequestSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/controls${workQuery(page)}`));
|
|
14941
|
+
}
|
|
14942
|
+
/** An assignment's published revisions, oldest first, each with its message and incorporation. */
|
|
14943
|
+
async workAssignmentRevisions(assignmentId, page = {}) {
|
|
14944
|
+
return workPageSchema(assignmentRevisionSchema).parse(await this.request(`/work/assignments/${encodeURIComponent(assignmentId)}/revisions${workQuery(page)}`));
|
|
14945
|
+
}
|
|
14946
|
+
// ── end DRI-323 ──
|
|
14947
|
+
// ── DRI-324: an execution's newest cancellation ──
|
|
14948
|
+
/** Each admitted Run's tool-boundary hold and stop, every effect issued (finished, unknown), what remains and what blocks closure. */
|
|
14949
|
+
async workExecutionCancellation(executionId) {
|
|
14950
|
+
return executionCancellationSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/cancellation`));
|
|
14951
|
+
}
|
|
14952
|
+
// ── Execution workflows (DRI-319): the Team library and a pinned execution's steps ──
|
|
14953
|
+
/** Publishes an execution workflow revision (Team admin) on its own route, with the Work receipts and replay. */
|
|
14954
|
+
async executionWorkflowCommand(command) {
|
|
14955
|
+
return coordinationCommandResultSchema.parse(await this.request("/work/execution-workflows/commands", coordinationCommandSchema.parse(command)));
|
|
14956
|
+
}
|
|
14957
|
+
async executionWorkflows(page = {}) {
|
|
14958
|
+
return executionWorkflowPageSchema.parse(await this.request(`/work/execution-workflows${workQuery(page)}`));
|
|
14959
|
+
}
|
|
14960
|
+
async executionWorkflow(id) {
|
|
14961
|
+
return executionWorkflowSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}`));
|
|
14962
|
+
}
|
|
14963
|
+
async executionWorkflowRevisions(id, page = {}) {
|
|
14964
|
+
return executionWorkflowRevisionPageSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}/revisions${workQuery(page)}`));
|
|
14965
|
+
}
|
|
14966
|
+
async executionWorkflowRevision(id, revision) {
|
|
14967
|
+
return executionWorkflowRevisionSchema.parse(await this.request(`/work/execution-workflows/${encodeURIComponent(id)}/revisions/${revision}`));
|
|
14968
|
+
}
|
|
14969
|
+
/** The pinned workflow revision of an execution and every step's state. */
|
|
14970
|
+
async workExecutionSteps(executionId) {
|
|
14971
|
+
return executionStepsSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/steps`));
|
|
14972
|
+
}
|
|
14973
|
+
// ── end DRI-319 ──
|
|
14974
|
+
// ── Review and repair loop (DRI-320) ──
|
|
14975
|
+
/** An execution's review rounds, oldest first: each verdict, the candidate it evaluated, the findings. */
|
|
14976
|
+
async workExecutionReviews(executionId, page = {}) {
|
|
14977
|
+
return reviewRoundPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/reviews${workQuery(page)}`));
|
|
14978
|
+
}
|
|
14979
|
+
// ── end DRI-320 ──
|
|
14980
|
+
// ── Messages and receipts (DRI-322) ──
|
|
14981
|
+
/**
|
|
14982
|
+
* An execution's messages, oldest first: a specialist's with its target and every receipt (received, delivered,
|
|
14983
|
+
* incorporated, rejected, unconfirmed), never relabelled. Send with `coordinationCommand` (`message.send`).
|
|
14984
|
+
*/
|
|
14985
|
+
async workExecutionMessages(executionId, page = {}) {
|
|
14986
|
+
return workPageSchema(executionMessageSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/messages${workQuery(page)}`));
|
|
14987
|
+
}
|
|
14988
|
+
// ── end DRI-322 ──
|
|
14989
|
+
// ── DRI-329: an execution's place in the delegation hierarchy ──
|
|
14990
|
+
/** Depth below its root, remaining delegation depth, subtree spend and a page of child executions with state and spend. */
|
|
14991
|
+
async workExecutionHierarchy(executionId, page = {}) {
|
|
14992
|
+
return executionHierarchySchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/hierarchy${workQuery(page)}`));
|
|
14993
|
+
}
|
|
14994
|
+
/** The child executions an execution's assignments control, oldest first. */
|
|
14995
|
+
async workExecutionChildren(executionId, page = {}) {
|
|
14996
|
+
return executionPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/children${workQuery(page)}`));
|
|
14997
|
+
}
|
|
14998
|
+
// ── end DRI-329 ──
|
|
14999
|
+
// ── QA steps and repair (DRI-321) ──
|
|
15000
|
+
/** An execution's QA rounds, oldest first: attempt, deployment, candidate, outcome, reproduction. */
|
|
15001
|
+
async workExecutionQa(executionId, page = {}) {
|
|
15002
|
+
return qaRoundPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/qa${workQuery(page)}`));
|
|
15003
|
+
}
|
|
15004
|
+
// ── end DRI-321 ──
|
|
15005
|
+
// ── Questions routed to their authority (DRI-330) ──
|
|
15006
|
+
/** Every question visible on an execution (raised in it or routed through it), in the order it reached it. Answer with `coordinationCommand`. */
|
|
15007
|
+
async workExecutionQuestions(executionId, page = {}) {
|
|
15008
|
+
return executionQuestionPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/questions${workQuery(page)}`));
|
|
15009
|
+
}
|
|
15010
|
+
/** One question: its authority, addressee, route, answer and its delivery, resolution. */
|
|
15011
|
+
async workQuestion(questionId) {
|
|
15012
|
+
return executionQuestionSchema.parse(await this.request(`/work/questions/${encodeURIComponent(questionId)}`));
|
|
15013
|
+
}
|
|
15014
|
+
// ── end DRI-330 ──
|
|
15015
|
+
// ── Child acceptance and delivery readiness (DRI-335) ──
|
|
15016
|
+
/** An execution's outcomes as distinct facts: finished, awaiting acceptance, accepted, closure, and Work's delivery readiness. */
|
|
15017
|
+
async workExecutionOutcome(executionId) {
|
|
15018
|
+
return executionOutcomeSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/outcome`));
|
|
15019
|
+
}
|
|
15020
|
+
/** An execution's completion packages, oldest first, each with the versioned assignment contract it answers. */
|
|
15021
|
+
async workExecutionPackages(executionId, page = {}) {
|
|
15022
|
+
return workPageSchema(completionPackageSchema).parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/packages${workQuery(page)}`));
|
|
15023
|
+
}
|
|
15024
|
+
// ── end DRI-335 ──
|
|
15025
|
+
// ── Client integration (DRI-342) ──
|
|
15026
|
+
/** Every execution of a Plan, oldest first. */
|
|
15027
|
+
async workPlanExecutions(planId, page = {}) {
|
|
15028
|
+
return executionPageSchema.parse(await this.request(`/work/plans/${encodeURIComponent(planId)}/executions${workQuery(page)}`));
|
|
15029
|
+
}
|
|
15030
|
+
/** One assignment. */
|
|
15031
|
+
async workAssignment(id) {
|
|
15032
|
+
return assignmentSchema.parse(await this.request(`/work/assignments/${encodeURIComponent(id)}`));
|
|
15033
|
+
}
|
|
15034
|
+
/** A page of an execution's assignments, oldest first. */
|
|
15035
|
+
async workExecutionAssignments(executionId, page = {}) {
|
|
15036
|
+
return assignmentPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/assignments${workQuery(page)}`));
|
|
15037
|
+
}
|
|
15038
|
+
/** A page of an execution's Activity, oldest first: its durable history. */
|
|
15039
|
+
async workExecutionActivity(executionId, page = {}) {
|
|
15040
|
+
return coordinationActivityPageSchema.parse(await this.request(`/work/executions/${encodeURIComponent(executionId)}/activity${workQuery(page)}`));
|
|
15041
|
+
}
|
|
15042
|
+
/**
|
|
15043
|
+
* The Team's authority record: who owns work-domain writes and starts, and the client contract the Hub serves.
|
|
15044
|
+
* Readable whether or not the Work core is on. A Hub that predates it answers `not_found`; see `WorkFlows.path`.
|
|
15045
|
+
*/
|
|
15046
|
+
async workAuthority() {
|
|
15047
|
+
return workAuthoritySchema.parse(await this.request("/work/authority"));
|
|
15048
|
+
}
|
|
15049
|
+
// ── end DRI-342 ──
|
|
15050
|
+
// ── end DRI-314 ──
|
|
15051
|
+
// ── Discussion on Work Threads (DRI-303) ──
|
|
15052
|
+
/**
|
|
15053
|
+
* Sends one discussion command (`comment.add`, `comment.edit`, `comment.delete`, `attachment.link`,
|
|
15054
|
+
* `attachment.check`, `attachment.remove`, `transcript.link`) on the Work command route, with its receipts and replay.
|
|
15055
|
+
*/
|
|
15056
|
+
async workDiscussionCommand(command) {
|
|
15057
|
+
return workDiscussionCommandResultSchema.parse(await this.request("/work/commands", workDiscussionCommandSchema.parse(command)));
|
|
15058
|
+
}
|
|
15059
|
+
/** The Thread's binding: the one Issue or Plan it belongs to. */
|
|
15060
|
+
async workThread(id) {
|
|
15061
|
+
return workThreadSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}`));
|
|
15062
|
+
}
|
|
15063
|
+
/** A Thread's comments, oldest first, deleted ones as tombstones; `parentId` pages the replies of one comment. */
|
|
15064
|
+
async workThreadComments(id, page = {}) {
|
|
15065
|
+
return workCommentPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/comments${discussionQuery(page)}`));
|
|
15066
|
+
}
|
|
15067
|
+
/** A Thread's link-only attachments, oldest first; `commentId` narrows to one comment's. */
|
|
15068
|
+
async workThreadAttachments(id, page = {}) {
|
|
15069
|
+
return workAttachmentPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/attachments${discussionQuery(page)}`));
|
|
15070
|
+
}
|
|
15071
|
+
/** A Thread's transcript links, each `visible` or `restricted` by the caller's access to its Run. */
|
|
15072
|
+
async workThreadTranscriptLinks(id, page = {}) {
|
|
15073
|
+
return workTranscriptLinkPageSchema.parse(await this.request(`/work/threads/${encodeURIComponent(id)}/transcript-links${workQuery(page)}`));
|
|
15074
|
+
}
|
|
15075
|
+
async workComment(id) {
|
|
15076
|
+
return workCommentSchema.parse(await this.request(`/work/comments/${encodeURIComponent(id)}`));
|
|
15077
|
+
}
|
|
15078
|
+
async workCommentRevisions(id, page = {}) {
|
|
15079
|
+
return workCommentRevisionPageSchema.parse(await this.request(`/work/comments/${encodeURIComponent(id)}/revisions${workQuery(page)}`));
|
|
15080
|
+
}
|
|
15081
|
+
async workAttachment(id) {
|
|
15082
|
+
return workAttachmentSchema.parse(await this.request(`/work/attachments/${encodeURIComponent(id)}`));
|
|
15083
|
+
}
|
|
15084
|
+
async workAttachmentChecks(id, page = {}) {
|
|
15085
|
+
return workAttachmentCheckPageSchema.parse(await this.request(`/work/attachments/${encodeURIComponent(id)}/checks${workQuery(page)}`));
|
|
15086
|
+
}
|
|
15087
|
+
async workTranscriptLink(id) {
|
|
15088
|
+
return workTranscriptLinkSchema.parse(await this.request(`/work/transcript-links/${encodeURIComponent(id)}`));
|
|
15089
|
+
}
|
|
15090
|
+
/** The linked range of the Run's history, under the Run's own visibility; `after` is the last sequence read. */
|
|
15091
|
+
async workTranscriptHistory(id, after) {
|
|
15092
|
+
return workTranscriptHistorySchema.parse(await this.request(
|
|
15093
|
+
`/work/transcript-links/${encodeURIComponent(id)}/history${after === void 0 ? "" : `?after=${after}`}`
|
|
15094
|
+
));
|
|
15095
|
+
}
|
|
15096
|
+
async workDiscussionActivity(subject, page = {}) {
|
|
15097
|
+
const collection = { comment: "comments", attachment: "attachments", transcript_link: "transcript-links" }[subject.kind];
|
|
15098
|
+
return workDiscussionActivityPageSchema.parse(
|
|
15099
|
+
await this.request(`/work/${collection}/${encodeURIComponent(subject.id)}/activity${workQuery(page)}`)
|
|
15100
|
+
);
|
|
15101
|
+
}
|
|
15102
|
+
// ── end DRI-303 ──
|
|
15103
|
+
// ── Normalized import preview (DRI-305) ──
|
|
15104
|
+
/** Previews one normalized import (`import.preview`, Team admin) on its own route, with the Work receipts. */
|
|
15105
|
+
async workImportCommand(command) {
|
|
15106
|
+
return workImportCommandResultSchema.parse(await this.request("/work/imports/commands", workImportCommandSchema.parse(command)));
|
|
15107
|
+
}
|
|
15108
|
+
async workImports(page = {}) {
|
|
15109
|
+
return workImportBatchPageSchema.parse(await this.request(`/work/imports${workQuery(page)}`));
|
|
15110
|
+
}
|
|
15111
|
+
async workImport(id) {
|
|
15112
|
+
return workImportBatchSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}`));
|
|
15113
|
+
}
|
|
15114
|
+
/** A batch's previewed items, optionally of one collection or one disposition. */
|
|
15115
|
+
async workImportItems(id, page = {}) {
|
|
15116
|
+
return workImportItemPageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/items${sourceQuery(page)}`));
|
|
15117
|
+
}
|
|
15118
|
+
/** The frozen envelope and mapping, exactly as previewed, with their digests. */
|
|
15119
|
+
async workImportSnapshot(id) {
|
|
15120
|
+
return workImportSnapshotSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/snapshot`));
|
|
15121
|
+
}
|
|
15122
|
+
// ── end DRI-305 ──
|
|
15123
|
+
// ── Import commit (DRI-306): `import.commit` goes through `workImportCommand` ──
|
|
15124
|
+
/** What the commit did with a batch, item by item and part by part, optionally filtered. */
|
|
15125
|
+
async workImportOutcomes(id, page = {}) {
|
|
15126
|
+
return workImportOutcomePageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/outcomes${sourceQuery(page)}`));
|
|
15127
|
+
}
|
|
15128
|
+
// ── end DRI-306 ──
|
|
15129
|
+
// ── Import completion (DRI-307): `import.complete` goes through `workImportCommand` ──
|
|
15130
|
+
/** A committed batch's discussion components and their current states, optionally of one collection or state. */
|
|
15131
|
+
async workImportComponents(id, page = {}) {
|
|
15132
|
+
return workImportComponentPageSchema.parse(await this.request(`/work/imports/${encodeURIComponent(id)}/components${sourceQuery(page)}`));
|
|
15133
|
+
}
|
|
15134
|
+
// ── end DRI-307 ──
|
|
13896
15135
|
/** The recent Runs; with a filter, every Run carrying that key or every child of that Run, newest first. */
|
|
13897
15136
|
async runs(filter) {
|
|
13898
15137
|
const query = !filter ? "" : "key" in filter ? `?key=${encodeURIComponent(filter.key)}` : `?parent=${encodeURIComponent(filter.parent)}`;
|
|
@@ -15585,39 +16824,1407 @@ ${USAGE3}
|
|
|
15585
16824
|
}
|
|
15586
16825
|
}
|
|
15587
16826
|
|
|
15588
|
-
// src/app/entry/
|
|
15589
|
-
import
|
|
16827
|
+
// src/app/entry/driveCommand.ts
|
|
16828
|
+
import path14 from "path";
|
|
15590
16829
|
|
|
15591
|
-
// src/
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
var
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
"
|
|
15600
|
-
"
|
|
15601
|
-
"
|
|
15602
|
-
"
|
|
15603
|
-
"
|
|
15604
|
-
"
|
|
15605
|
-
"
|
|
15606
|
-
"model",
|
|
15607
|
-
"apiKeyHelper",
|
|
15608
|
-
"awsCredentialExport"
|
|
16830
|
+
// src/core/drive/merge.ts
|
|
16831
|
+
var DEFAULT_TIMEOUT_MS = 9 * 6e4;
|
|
16832
|
+
var DEFAULT_POLL_MS = 15e3;
|
|
16833
|
+
var DEFAULT_NO_CHECKS_GRACE_MS = 45e3;
|
|
16834
|
+
var DEFAULT_SETTLE_MS = 2e4;
|
|
16835
|
+
var DEFAULT_LOCK_WAIT_MS = 6e4;
|
|
16836
|
+
var MAX_ROUNDS = 2;
|
|
16837
|
+
var DEPENDENCY_FILES = [
|
|
16838
|
+
"package.json",
|
|
16839
|
+
"package-lock.json",
|
|
16840
|
+
"npm-shrinkwrap.json",
|
|
16841
|
+
"pnpm-lock.yaml",
|
|
16842
|
+
"yarn.lock",
|
|
16843
|
+
"bun.lock",
|
|
16844
|
+
"bun.lockb"
|
|
15609
16845
|
];
|
|
15610
|
-
|
|
15611
|
-
|
|
16846
|
+
var OUTPUT_TAIL = 4e3;
|
|
16847
|
+
var SCRATCH_PREFIX = "drisp-drive-merge-";
|
|
16848
|
+
var MERGED_REFS = "refs/drisp/merged";
|
|
16849
|
+
var TESTED_REFS = "refs/drisp/tested";
|
|
16850
|
+
function tail(text) {
|
|
16851
|
+
const trimmed = text.trim();
|
|
16852
|
+
return trimmed.length > OUTPUT_TAIL ? `\u2026${trimmed.slice(-OUTPUT_TAIL)}` : trimmed;
|
|
16853
|
+
}
|
|
16854
|
+
function both(result) {
|
|
16855
|
+
return tail([result.stdout, result.stderr].filter(Boolean).join("\n"));
|
|
16856
|
+
}
|
|
16857
|
+
var Stop = class extends Error {
|
|
16858
|
+
constructor(result) {
|
|
16859
|
+
super(result.detail);
|
|
16860
|
+
this.result = result;
|
|
16861
|
+
}
|
|
16862
|
+
result;
|
|
16863
|
+
};
|
|
16864
|
+
function stop(outcome, detail, output) {
|
|
16865
|
+
throw new Stop({ outcome, detail, ...output ? { output } : {} });
|
|
16866
|
+
}
|
|
16867
|
+
function refuse(detail, output) {
|
|
16868
|
+
return stop("refused", detail, output);
|
|
16869
|
+
}
|
|
16870
|
+
function baseKeptMoving(defaultBranch) {
|
|
16871
|
+
return `base kept moving: ${defaultBranch} moved after each of ${MAX_ROUNDS} rebase-and-check rounds; run the command again`;
|
|
16872
|
+
}
|
|
16873
|
+
async function driveMerge(input, deps) {
|
|
15612
16874
|
try {
|
|
15613
|
-
|
|
15614
|
-
} catch {
|
|
15615
|
-
|
|
16875
|
+
return await new DriveMerge(input, deps).run();
|
|
16876
|
+
} catch (error) {
|
|
16877
|
+
if (error instanceof Stop) return error.result;
|
|
16878
|
+
throw error;
|
|
15616
16879
|
}
|
|
15617
|
-
|
|
16880
|
+
}
|
|
16881
|
+
function judge(checks, scope) {
|
|
16882
|
+
const { failing, unfinished } = judgeChecks(checks);
|
|
16883
|
+
if (failing.length > 0)
|
|
16884
|
+
return {
|
|
16885
|
+
kind: "failed",
|
|
16886
|
+
detail: `${scope} failed: ${failing.map((check) => check.name).join(", ")}`
|
|
16887
|
+
};
|
|
16888
|
+
const cancelled = unfinished.filter((check) => check.bucket === "cancel");
|
|
16889
|
+
if (cancelled.length > 0)
|
|
16890
|
+
return {
|
|
16891
|
+
kind: "failed",
|
|
16892
|
+
detail: `${scope} cancelled: ${cancelled.map((check) => check.name).join(", ")}`
|
|
16893
|
+
};
|
|
16894
|
+
if (unfinished.length > 0)
|
|
16895
|
+
return {
|
|
16896
|
+
kind: "waiting",
|
|
16897
|
+
detail: `${scope} not finished: ${describeChecks(unfinished)}`
|
|
16898
|
+
};
|
|
16899
|
+
return {
|
|
16900
|
+
kind: "passed",
|
|
15618
16901
|
scope,
|
|
15619
|
-
|
|
15620
|
-
|
|
16902
|
+
detail: `${scope} passed (${checks.map((check) => check.name).join(", ")})`
|
|
16903
|
+
};
|
|
16904
|
+
}
|
|
16905
|
+
var DriveMerge = class {
|
|
16906
|
+
constructor(input, deps) {
|
|
16907
|
+
this.input = input;
|
|
16908
|
+
this.deps = deps;
|
|
16909
|
+
this.timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
16910
|
+
this.pollMs = input.pollMs ?? DEFAULT_POLL_MS;
|
|
16911
|
+
this.graceMs = input.noChecksGraceMs ?? DEFAULT_NO_CHECKS_GRACE_MS;
|
|
16912
|
+
this.settleMs = input.settleMs ?? DEFAULT_SETTLE_MS;
|
|
16913
|
+
}
|
|
16914
|
+
input;
|
|
16915
|
+
deps;
|
|
16916
|
+
timeoutMs;
|
|
16917
|
+
/** Set when the command starts; every stage takes what is left of it. */
|
|
16918
|
+
deadline = 0;
|
|
16919
|
+
pollMs;
|
|
16920
|
+
graceMs;
|
|
16921
|
+
settleMs;
|
|
16922
|
+
left() {
|
|
16923
|
+
return this.deadline - this.deps.now();
|
|
16924
|
+
}
|
|
16925
|
+
timedOut(what) {
|
|
16926
|
+
return stop(
|
|
16927
|
+
"checks_timeout",
|
|
16928
|
+
`${what}: the command's ${Math.round(this.timeoutMs / 1e3)} s deadline passed; run the command again`
|
|
16929
|
+
);
|
|
16930
|
+
}
|
|
16931
|
+
git(cwd, ...args) {
|
|
16932
|
+
return this.deps.git(cwd, args);
|
|
16933
|
+
}
|
|
16934
|
+
/** stdout of a git call that must succeed, else `refused` naming it. */
|
|
16935
|
+
must(cwd, what, ...args) {
|
|
16936
|
+
const result = this.git(cwd, ...args);
|
|
16937
|
+
if (result.code !== 0) refuse(`${what} failed`, both(result));
|
|
16938
|
+
return result.stdout.trim();
|
|
16939
|
+
}
|
|
16940
|
+
sha(cwd, ref) {
|
|
16941
|
+
const result = this.git(cwd, "rev-parse", "--verify", "--quiet", ref);
|
|
16942
|
+
return result.code === 0 ? result.stdout.trim() : null;
|
|
16943
|
+
}
|
|
16944
|
+
isAncestor(cwd, commit, of) {
|
|
16945
|
+
return this.git(cwd, "merge-base", "--is-ancestor", commit, of).code === 0;
|
|
16946
|
+
}
|
|
16947
|
+
checkedOut(cwd) {
|
|
16948
|
+
return this.git(
|
|
16949
|
+
cwd,
|
|
16950
|
+
"symbolic-ref",
|
|
16951
|
+
"--quiet",
|
|
16952
|
+
"--short",
|
|
16953
|
+
"HEAD"
|
|
16954
|
+
).stdout.trim();
|
|
16955
|
+
}
|
|
16956
|
+
rebaseInProgress(worktreeDir) {
|
|
16957
|
+
return ["rebase-merge", "rebase-apply"].some((state) => {
|
|
16958
|
+
const at = this.git(
|
|
16959
|
+
worktreeDir,
|
|
16960
|
+
"rev-parse",
|
|
16961
|
+
"--path-format=absolute",
|
|
16962
|
+
"--git-path",
|
|
16963
|
+
state
|
|
16964
|
+
).stdout.trim();
|
|
16965
|
+
return at !== "" && this.deps.exists(at);
|
|
16966
|
+
});
|
|
16967
|
+
}
|
|
16968
|
+
async run() {
|
|
16969
|
+
const { worktree, item } = this.input;
|
|
16970
|
+
this.deadline = this.deps.now() + this.timeoutMs;
|
|
16971
|
+
const top = this.git(worktree, "rev-parse", "--show-toplevel");
|
|
16972
|
+
if (top.code !== 0) refuse(`${worktree} is not a git worktree`, both(top));
|
|
16973
|
+
const worktreeDir = top.stdout.trim();
|
|
16974
|
+
if (this.git(worktreeDir, "check-ref-format", `${MERGED_REFS}/${item}`).code !== 0)
|
|
16975
|
+
refuse(`"${item}" cannot name a git ref; use the item's identifier`);
|
|
16976
|
+
const commonDir = this.must(
|
|
16977
|
+
worktreeDir,
|
|
16978
|
+
"reading the git common dir",
|
|
16979
|
+
"rev-parse",
|
|
16980
|
+
"--path-format=absolute",
|
|
16981
|
+
"--git-common-dir"
|
|
16982
|
+
);
|
|
16983
|
+
const release2 = await this.deps.lock(
|
|
16984
|
+
commonDir,
|
|
16985
|
+
Math.min(this.input.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS, this.left())
|
|
16986
|
+
);
|
|
16987
|
+
if (!release2)
|
|
16988
|
+
refuse(
|
|
16989
|
+
"another merge in progress: another drisp drive merge holds the lock in this repository; run the command again once it finishes"
|
|
16990
|
+
);
|
|
16991
|
+
try {
|
|
16992
|
+
this.removeStaleScratch(worktreeDir);
|
|
16993
|
+
return await this.locked(worktreeDir);
|
|
16994
|
+
} finally {
|
|
16995
|
+
release2();
|
|
16996
|
+
}
|
|
16997
|
+
}
|
|
16998
|
+
/** Temporary checkouts a killed run left behind (they live under `drisp-drive-merge-*`). */
|
|
16999
|
+
removeStaleScratch(worktreeDir) {
|
|
17000
|
+
const stale = this.git(worktreeDir, "worktree", "list", "--porcelain").stdout.split("\n").filter(
|
|
17001
|
+
(line) => line.startsWith("worktree ") && line.includes(SCRATCH_PREFIX)
|
|
17002
|
+
).map((line) => line.slice("worktree ".length));
|
|
17003
|
+
for (const dir of stale)
|
|
17004
|
+
this.git(worktreeDir, "worktree", "remove", "--force", dir);
|
|
17005
|
+
this.git(worktreeDir, "worktree", "prune");
|
|
17006
|
+
}
|
|
17007
|
+
async locked(worktreeDir) {
|
|
17008
|
+
const { item } = this.input;
|
|
17009
|
+
if (this.rebaseInProgress(worktreeDir))
|
|
17010
|
+
refuse(
|
|
17011
|
+
"a rebase is in progress in the worktree; run `git rebase --abort` there (or finish it), then merge again"
|
|
17012
|
+
);
|
|
17013
|
+
const mainCheckout = this.mainCheckout(worktreeDir);
|
|
17014
|
+
const current = this.checkedOut(worktreeDir);
|
|
17015
|
+
const branch = this.input.branch ?? current;
|
|
17016
|
+
if (!branch)
|
|
17017
|
+
refuse("the worktree has a detached HEAD; check out the item branch");
|
|
17018
|
+
if (current !== branch)
|
|
17019
|
+
refuse(
|
|
17020
|
+
`the worktree has ${current || "a detached HEAD"} checked out, not ${branch}`
|
|
17021
|
+
);
|
|
17022
|
+
const originUrl = this.git(
|
|
17023
|
+
mainCheckout,
|
|
17024
|
+
"config",
|
|
17025
|
+
"--get",
|
|
17026
|
+
"remote.origin.url"
|
|
17027
|
+
);
|
|
17028
|
+
const hasOrigin = originUrl.code === 0;
|
|
17029
|
+
const defaultBranch = this.defaultBranch(mainCheckout, hasOrigin);
|
|
17030
|
+
if (branch === defaultBranch)
|
|
17031
|
+
refuse(`${branch} is the default branch; name the item's branch`);
|
|
17032
|
+
const dirty = this.uncommitted(worktreeDir);
|
|
17033
|
+
if (dirty.length > 0)
|
|
17034
|
+
stop(
|
|
17035
|
+
"conflict",
|
|
17036
|
+
"the worktree has uncommitted changes or untracked files; commit them (or remove or ignore them), then merge again",
|
|
17037
|
+
dirty.join("\n")
|
|
17038
|
+
);
|
|
17039
|
+
const github = /github\.com[:/]/i.test(originUrl.stdout);
|
|
17040
|
+
this.deps.log(
|
|
17041
|
+
`drive merge ${item}: ${branch} \u2192 ${defaultBranch} (${github ? "GitHub" : hasOrigin ? "git origin" : "local"})`
|
|
17042
|
+
);
|
|
17043
|
+
const ctx = { worktreeDir, mainCheckout, branch, defaultBranch, hasOrigin };
|
|
17044
|
+
return github ? this.viaGitHub(ctx) : this.viaGit(ctx);
|
|
17045
|
+
}
|
|
17046
|
+
/** The main checkout: the first entry of `git worktree list`. */
|
|
17047
|
+
mainCheckout(worktreeDir) {
|
|
17048
|
+
const list = this.must(
|
|
17049
|
+
worktreeDir,
|
|
17050
|
+
"git worktree list",
|
|
17051
|
+
"worktree",
|
|
17052
|
+
"list",
|
|
17053
|
+
"--porcelain"
|
|
17054
|
+
);
|
|
17055
|
+
const first = list.split("\n").find((line) => line.startsWith("worktree "))?.slice("worktree ".length);
|
|
17056
|
+
if (!first) refuse("could not find the main checkout");
|
|
17057
|
+
return first;
|
|
17058
|
+
}
|
|
17059
|
+
/** origin's HEAD, else the main checkout's `main`/`master`. */
|
|
17060
|
+
defaultBranch(mainCheckout, hasOrigin) {
|
|
17061
|
+
const head = this.git(
|
|
17062
|
+
mainCheckout,
|
|
17063
|
+
"symbolic-ref",
|
|
17064
|
+
"--quiet",
|
|
17065
|
+
"--short",
|
|
17066
|
+
"refs/remotes/origin/HEAD"
|
|
17067
|
+
).stdout.trim();
|
|
17068
|
+
if (head.startsWith("origin/")) return head.slice("origin/".length);
|
|
17069
|
+
if (hasOrigin) {
|
|
17070
|
+
const remote = this.git(
|
|
17071
|
+
mainCheckout,
|
|
17072
|
+
"ls-remote",
|
|
17073
|
+
"--symref",
|
|
17074
|
+
"origin",
|
|
17075
|
+
"HEAD"
|
|
17076
|
+
).stdout.match(/^ref: refs\/heads\/(\S+)\s+HEAD/m);
|
|
17077
|
+
if (remote?.[1]) return remote[1];
|
|
17078
|
+
}
|
|
17079
|
+
for (const name of ["main", "master"]) {
|
|
17080
|
+
if (this.sha(mainCheckout, `refs/heads/${name}`)) return name;
|
|
17081
|
+
}
|
|
17082
|
+
return refuse("could not tell the default branch (no main or master)");
|
|
17083
|
+
}
|
|
17084
|
+
status(cwd, untracked) {
|
|
17085
|
+
return this.git(
|
|
17086
|
+
cwd,
|
|
17087
|
+
"status",
|
|
17088
|
+
"--porcelain",
|
|
17089
|
+
`--untracked-files=${untracked}`
|
|
17090
|
+
).stdout.split("\n").map((line) => line.slice(3).trim()).filter(Boolean);
|
|
17091
|
+
}
|
|
17092
|
+
/** Changes to tracked files and untracked, non-ignored files: neither may ride along unseen. */
|
|
17093
|
+
uncommitted(cwd) {
|
|
17094
|
+
return this.status(cwd, "all");
|
|
17095
|
+
}
|
|
17096
|
+
/** Rebase the worktree onto `onto`; a conflict is aborted and reported. Returns the rebased HEAD. */
|
|
17097
|
+
rebase(worktreeDir, onto) {
|
|
17098
|
+
const rebased = this.git(worktreeDir, "rebase", onto);
|
|
17099
|
+
if (rebased.code !== 0) {
|
|
17100
|
+
const files = this.git(
|
|
17101
|
+
worktreeDir,
|
|
17102
|
+
"diff",
|
|
17103
|
+
"--name-only",
|
|
17104
|
+
"--diff-filter=U"
|
|
17105
|
+
).stdout.split("\n").filter(Boolean);
|
|
17106
|
+
if (this.rebaseInProgress(worktreeDir))
|
|
17107
|
+
this.git(worktreeDir, "rebase", "--abort");
|
|
17108
|
+
if (files.length === 0)
|
|
17109
|
+
refuse(`git rebase ${onto} failed (not a conflict)`, both(rebased));
|
|
17110
|
+
stop(
|
|
17111
|
+
"conflict",
|
|
17112
|
+
`rebasing onto ${onto} conflicts in ${files.join(", ")}; the rebase was aborted`,
|
|
17113
|
+
files.join("\n")
|
|
17114
|
+
);
|
|
17115
|
+
}
|
|
17116
|
+
return this.must(worktreeDir, "reading HEAD", "rev-parse", "HEAD");
|
|
17117
|
+
}
|
|
17118
|
+
/** The repository's own checks: `.athena/config.json` `checks`, else package.json's `test` script. */
|
|
17119
|
+
repoChecks(mainCheckout, worktreeDir) {
|
|
17120
|
+
const configured = this.deps.configuredChecks(mainCheckout);
|
|
17121
|
+
if (configured && configured.length > 0) return configured;
|
|
17122
|
+
if (this.deps.packageTestScript(worktreeDir) !== void 0)
|
|
17123
|
+
return ["npm test"];
|
|
17124
|
+
return refuse(
|
|
17125
|
+
'no checks to prove the merge: set "checks" in .athena/config.json or a package.json test script'
|
|
17126
|
+
);
|
|
17127
|
+
}
|
|
17128
|
+
/** The manifests and lockfiles whose change means the worktree's installed dependencies may not match. */
|
|
17129
|
+
dependenciesChanged(cwd, base, sha) {
|
|
17130
|
+
const changed = this.git(
|
|
17131
|
+
cwd,
|
|
17132
|
+
"diff",
|
|
17133
|
+
"--name-only",
|
|
17134
|
+
base,
|
|
17135
|
+
sha,
|
|
17136
|
+
"--",
|
|
17137
|
+
...DEPENDENCY_FILES.map((name) => `:(glob)**/${name}`)
|
|
17138
|
+
).stdout.trim();
|
|
17139
|
+
return changed !== "";
|
|
17140
|
+
}
|
|
17141
|
+
/** The install the checkout's lockfile calls for, scripts off. */
|
|
17142
|
+
installCommand(dir) {
|
|
17143
|
+
const has = (name) => this.deps.exists(`${dir}/${name}`);
|
|
17144
|
+
if (has("pnpm-lock.yaml"))
|
|
17145
|
+
return "pnpm install --frozen-lockfile --ignore-scripts";
|
|
17146
|
+
if (has("yarn.lock"))
|
|
17147
|
+
return "yarn install --frozen-lockfile --ignore-scripts";
|
|
17148
|
+
if (has("bun.lock") || has("bun.lockb"))
|
|
17149
|
+
return "bun install --frozen-lockfile --ignore-scripts";
|
|
17150
|
+
if (has("package-lock.json") || has("npm-shrinkwrap.json"))
|
|
17151
|
+
return "npm ci --ignore-scripts --no-audit --no-fund";
|
|
17152
|
+
return "npm install --ignore-scripts --no-audit --no-fund";
|
|
17153
|
+
}
|
|
17154
|
+
/**
|
|
17155
|
+
* Run each check in a clean, detached checkout of `sha` (never the worktree, whose files may differ from the
|
|
17156
|
+
* commit); the first non-zero exit fails the merge, and the deadline is `checks_timeout`. The worktree's
|
|
17157
|
+
* `node_modules` is reused only when no manifest or lockfile changed since `base`; otherwise the lockfile's
|
|
17158
|
+
* install runs in the checkout first.
|
|
17159
|
+
*/
|
|
17160
|
+
async runRepoChecks(mainCheckout, worktreeDir, base, sha) {
|
|
17161
|
+
const checks = this.repoChecks(mainCheckout, worktreeDir);
|
|
17162
|
+
const scratch = this.deps.scratchDir();
|
|
17163
|
+
const added = this.git(
|
|
17164
|
+
worktreeDir,
|
|
17165
|
+
"worktree",
|
|
17166
|
+
"add",
|
|
17167
|
+
"--quiet",
|
|
17168
|
+
"--detach",
|
|
17169
|
+
scratch.dir,
|
|
17170
|
+
sha
|
|
17171
|
+
);
|
|
17172
|
+
if (added.code !== 0) {
|
|
17173
|
+
scratch.cleanup();
|
|
17174
|
+
refuse("could not check out the tested commit", both(added));
|
|
17175
|
+
}
|
|
17176
|
+
const run = async (command, what) => {
|
|
17177
|
+
if (this.left() <= 0) this.timedOut(`before ${what}`);
|
|
17178
|
+
const result = await this.deps.runCheck(
|
|
17179
|
+
scratch.dir,
|
|
17180
|
+
command,
|
|
17181
|
+
this.left()
|
|
17182
|
+
);
|
|
17183
|
+
if (result.timedOut)
|
|
17184
|
+
stop(
|
|
17185
|
+
"checks_timeout",
|
|
17186
|
+
`${what} was killed at the command's ${Math.round(this.timeoutMs / 1e3)} s deadline; run the command again`,
|
|
17187
|
+
both(result)
|
|
17188
|
+
);
|
|
17189
|
+
if (result.code !== 0)
|
|
17190
|
+
stop("checks_failed", `${what} exited ${result.code}`, both(result));
|
|
17191
|
+
};
|
|
17192
|
+
try {
|
|
17193
|
+
if (this.dependenciesChanged(worktreeDir, base, sha)) {
|
|
17194
|
+
const install = this.installCommand(scratch.dir);
|
|
17195
|
+
this.deps.log(`dependencies changed; installing: ${install}`);
|
|
17196
|
+
await run(install, `the dependency install \`${install}\``);
|
|
17197
|
+
} else {
|
|
17198
|
+
this.deps.linkDependencies(worktreeDir, scratch.dir);
|
|
17199
|
+
}
|
|
17200
|
+
for (const check of checks) {
|
|
17201
|
+
this.deps.log(`check (${sha.slice(0, 7)}): ${check}`);
|
|
17202
|
+
await run(check, `\`${check}\``);
|
|
17203
|
+
}
|
|
17204
|
+
return checks;
|
|
17205
|
+
} finally {
|
|
17206
|
+
this.git(worktreeDir, "worktree", "remove", "--force", scratch.dir);
|
|
17207
|
+
scratch.cleanup();
|
|
17208
|
+
this.git(worktreeDir, "worktree", "prune");
|
|
17209
|
+
}
|
|
17210
|
+
}
|
|
17211
|
+
// ── recorded merges ───────────────────────────────────────────────
|
|
17212
|
+
recordedMerge(cwd) {
|
|
17213
|
+
return this.sha(cwd, `${MERGED_REFS}/${this.input.item}`);
|
|
17214
|
+
}
|
|
17215
|
+
forgetMerge(cwd) {
|
|
17216
|
+
this.git(cwd, "update-ref", "-d", `${MERGED_REFS}/${this.input.item}`);
|
|
17217
|
+
this.git(cwd, "update-ref", "-d", `${TESTED_REFS}/${this.input.item}`);
|
|
17218
|
+
}
|
|
17219
|
+
/**
|
|
17220
|
+
* The default branch's commits origin lacks, along its first parents, are pushed only when each is a merge this
|
|
17221
|
+
* command recorded; anything else on it was not merged here and is refused, so a local `git merge` is never
|
|
17222
|
+
* laundered through a push. Returns a note for the detail.
|
|
17223
|
+
*/
|
|
17224
|
+
pushRecordedOnly(mainCheckout, defaultBranch) {
|
|
17225
|
+
const remote = `refs/remotes/origin/${defaultBranch}`;
|
|
17226
|
+
const ahead = this.sha(mainCheckout, remote) ? this.git(
|
|
17227
|
+
mainCheckout,
|
|
17228
|
+
"rev-list",
|
|
17229
|
+
"--first-parent",
|
|
17230
|
+
`${remote}..refs/heads/${defaultBranch}`
|
|
17231
|
+
).stdout.split("\n").filter(Boolean) : [];
|
|
17232
|
+
if (ahead.length === 0) return "";
|
|
17233
|
+
const recorded = new Set(
|
|
17234
|
+
this.git(
|
|
17235
|
+
mainCheckout,
|
|
17236
|
+
"for-each-ref",
|
|
17237
|
+
"--format=%(objectname)",
|
|
17238
|
+
MERGED_REFS
|
|
17239
|
+
).stdout.split("\n").filter(Boolean)
|
|
17240
|
+
);
|
|
17241
|
+
const foreign = ahead.filter((commit) => !recorded.has(commit));
|
|
17242
|
+
if (foreign.length > 0)
|
|
17243
|
+
refuse(
|
|
17244
|
+
`${defaultBranch} has commits drisp drive merge did not make, so it pushes nothing; a member must remove them (or push them) first`,
|
|
17245
|
+
this.git(
|
|
17246
|
+
mainCheckout,
|
|
17247
|
+
"log",
|
|
17248
|
+
"--oneline",
|
|
17249
|
+
"--no-walk",
|
|
17250
|
+
...foreign
|
|
17251
|
+
).stdout.trim()
|
|
17252
|
+
);
|
|
17253
|
+
const pushed = this.git(
|
|
17254
|
+
mainCheckout,
|
|
17255
|
+
"push",
|
|
17256
|
+
"--quiet",
|
|
17257
|
+
"origin",
|
|
17258
|
+
defaultBranch
|
|
17259
|
+
);
|
|
17260
|
+
if (pushed.code !== 0)
|
|
17261
|
+
refuse(
|
|
17262
|
+
`${defaultBranch} holds a recorded merge that origin lacks, but pushing it failed; run the command again`,
|
|
17263
|
+
both(pushed)
|
|
17264
|
+
);
|
|
17265
|
+
return `; pushed ${defaultBranch} to origin`;
|
|
17266
|
+
}
|
|
17267
|
+
/** Fast-forward the default branch to origin's when origin moved on; refuse when they diverged. */
|
|
17268
|
+
catchUpWithOrigin(mainCheckout, defaultBranch) {
|
|
17269
|
+
this.must(mainCheckout, "git fetch origin", "fetch", "--quiet", "origin");
|
|
17270
|
+
const remote = `refs/remotes/origin/${defaultBranch}`;
|
|
17271
|
+
if (!this.sha(mainCheckout, remote)) return;
|
|
17272
|
+
if (this.isAncestor(mainCheckout, remote, `refs/heads/${defaultBranch}`))
|
|
17273
|
+
return;
|
|
17274
|
+
const ff = this.git(mainCheckout, "merge", "--ff-only", "--quiet", remote);
|
|
17275
|
+
if (ff.code !== 0)
|
|
17276
|
+
refuse(
|
|
17277
|
+
`${defaultBranch} and origin/${defaultBranch} have diverged`,
|
|
17278
|
+
both(ff)
|
|
17279
|
+
);
|
|
17280
|
+
}
|
|
17281
|
+
// ── a git origin, or none ─────────────────────────────────────────
|
|
17282
|
+
async viaGit(ctx) {
|
|
17283
|
+
const { worktreeDir, mainCheckout, branch, defaultBranch, hasOrigin } = ctx;
|
|
17284
|
+
const { item } = this.input;
|
|
17285
|
+
const defaultRef = `refs/heads/${defaultBranch}`;
|
|
17286
|
+
if (hasOrigin)
|
|
17287
|
+
this.must(mainCheckout, "git fetch origin", "fetch", "--quiet", "origin");
|
|
17288
|
+
const tip = this.must(
|
|
17289
|
+
worktreeDir,
|
|
17290
|
+
`reading ${branch}`,
|
|
17291
|
+
"rev-parse",
|
|
17292
|
+
`refs/heads/${branch}`
|
|
17293
|
+
);
|
|
17294
|
+
let recorded = this.recordedMerge(mainCheckout);
|
|
17295
|
+
if (recorded && !(this.isAncestor(mainCheckout, recorded, defaultRef) && (this.isAncestor(mainCheckout, tip, defaultRef) || this.sha(mainCheckout, `${TESTED_REFS}/${item}`) === tip))) {
|
|
17296
|
+
this.forgetMerge(mainCheckout);
|
|
17297
|
+
recorded = null;
|
|
17298
|
+
}
|
|
17299
|
+
if (recorded) {
|
|
17300
|
+
const pushed = hasOrigin ? this.pushRecordedOnly(mainCheckout, defaultBranch) : "";
|
|
17301
|
+
if (hasOrigin) this.deleteRemoteBranch(mainCheckout, branch);
|
|
17302
|
+
return {
|
|
17303
|
+
outcome: "already_merged",
|
|
17304
|
+
detail: `${item} was merged at ${recorded.slice(0, 7)}${pushed}`
|
|
17305
|
+
};
|
|
17306
|
+
}
|
|
17307
|
+
if (this.isAncestor(mainCheckout, tip, defaultRef))
|
|
17308
|
+
refuse(
|
|
17309
|
+
`no commits to merge: ${defaultBranch} already holds every commit of ${branch}, and drisp drive merge did not merge it (a merge it did not record does not count)`
|
|
17310
|
+
);
|
|
17311
|
+
const checkedOut = this.checkedOut(mainCheckout);
|
|
17312
|
+
if (checkedOut !== defaultBranch)
|
|
17313
|
+
refuse(
|
|
17314
|
+
`the main checkout has ${checkedOut || "a detached HEAD"} checked out, not ${defaultBranch}`
|
|
17315
|
+
);
|
|
17316
|
+
const dirty = this.status(mainCheckout, "no");
|
|
17317
|
+
if (dirty.length > 0)
|
|
17318
|
+
refuse(
|
|
17319
|
+
"the main checkout has uncommitted changes to tracked files",
|
|
17320
|
+
dirty.join("\n")
|
|
17321
|
+
);
|
|
17322
|
+
for (let round = 1; round <= MAX_ROUNDS; round++) {
|
|
17323
|
+
if (hasOrigin) {
|
|
17324
|
+
this.pushRecordedOnly(mainCheckout, defaultBranch);
|
|
17325
|
+
this.catchUpWithOrigin(mainCheckout, defaultBranch);
|
|
17326
|
+
}
|
|
17327
|
+
const before = this.must(
|
|
17328
|
+
mainCheckout,
|
|
17329
|
+
"reading the base",
|
|
17330
|
+
"rev-parse",
|
|
17331
|
+
defaultRef
|
|
17332
|
+
);
|
|
17333
|
+
const sha = this.rebase(worktreeDir, defaultRef);
|
|
17334
|
+
const checks = await this.runRepoChecks(
|
|
17335
|
+
mainCheckout,
|
|
17336
|
+
worktreeDir,
|
|
17337
|
+
before,
|
|
17338
|
+
sha
|
|
17339
|
+
);
|
|
17340
|
+
if (hasOrigin) this.catchUpWithOrigin(mainCheckout, defaultBranch);
|
|
17341
|
+
const base = this.must(
|
|
17342
|
+
mainCheckout,
|
|
17343
|
+
"reading the base",
|
|
17344
|
+
"rev-parse",
|
|
17345
|
+
defaultRef
|
|
17346
|
+
);
|
|
17347
|
+
if (!this.isAncestor(mainCheckout, base, sha)) {
|
|
17348
|
+
this.deps.log(
|
|
17349
|
+
`${defaultBranch} moved to ${base.slice(0, 7)} during the checks; rebasing again`
|
|
17350
|
+
);
|
|
17351
|
+
continue;
|
|
17352
|
+
}
|
|
17353
|
+
const merge = this.must(
|
|
17354
|
+
mainCheckout,
|
|
17355
|
+
"creating the merge commit",
|
|
17356
|
+
"commit-tree",
|
|
17357
|
+
`${sha}^{tree}`,
|
|
17358
|
+
"-p",
|
|
17359
|
+
base,
|
|
17360
|
+
"-p",
|
|
17361
|
+
sha,
|
|
17362
|
+
"-m",
|
|
17363
|
+
`Merge ${item} (${branch})`
|
|
17364
|
+
);
|
|
17365
|
+
this.must(
|
|
17366
|
+
mainCheckout,
|
|
17367
|
+
"recording the merge",
|
|
17368
|
+
"update-ref",
|
|
17369
|
+
`${MERGED_REFS}/${item}`,
|
|
17370
|
+
merge
|
|
17371
|
+
);
|
|
17372
|
+
this.must(
|
|
17373
|
+
mainCheckout,
|
|
17374
|
+
"recording the merge",
|
|
17375
|
+
"update-ref",
|
|
17376
|
+
`${TESTED_REFS}/${item}`,
|
|
17377
|
+
sha
|
|
17378
|
+
);
|
|
17379
|
+
const advanced = this.advance(
|
|
17380
|
+
ctx,
|
|
17381
|
+
merge,
|
|
17382
|
+
` after ${checks.map((check) => `\`${check}\``).join(", ")} passed on ${sha.slice(0, 7)}`
|
|
17383
|
+
);
|
|
17384
|
+
if (advanced) return advanced;
|
|
17385
|
+
}
|
|
17386
|
+
return stop("checks_failed", baseKeptMoving(defaultBranch));
|
|
17387
|
+
}
|
|
17388
|
+
/**
|
|
17389
|
+
* Fast-forward the default branch to the recorded merge and push it. Returns null when origin moved under the
|
|
17390
|
+
* push, after undoing the local merge, so the caller rebases and checks again.
|
|
17391
|
+
*/
|
|
17392
|
+
advance(ctx, merge, evidence) {
|
|
17393
|
+
const { mainCheckout, branch, defaultBranch, hasOrigin } = ctx;
|
|
17394
|
+
const before = this.must(
|
|
17395
|
+
mainCheckout,
|
|
17396
|
+
"reading the base",
|
|
17397
|
+
"rev-parse",
|
|
17398
|
+
`refs/heads/${defaultBranch}`
|
|
17399
|
+
);
|
|
17400
|
+
const ff = this.git(mainCheckout, "merge", "--ff-only", "--quiet", merge);
|
|
17401
|
+
if (ff.code !== 0) {
|
|
17402
|
+
this.forgetMerge(mainCheckout);
|
|
17403
|
+
refuse(
|
|
17404
|
+
`fast-forwarding ${defaultBranch} in the main checkout failed`,
|
|
17405
|
+
both(ff)
|
|
17406
|
+
);
|
|
17407
|
+
}
|
|
17408
|
+
let note = "";
|
|
17409
|
+
if (hasOrigin) {
|
|
17410
|
+
const pushed = this.git(
|
|
17411
|
+
mainCheckout,
|
|
17412
|
+
"push",
|
|
17413
|
+
"--quiet",
|
|
17414
|
+
"origin",
|
|
17415
|
+
defaultBranch
|
|
17416
|
+
);
|
|
17417
|
+
if (pushed.code !== 0) {
|
|
17418
|
+
this.git(mainCheckout, "fetch", "--quiet", "origin");
|
|
17419
|
+
const remote = this.sha(
|
|
17420
|
+
mainCheckout,
|
|
17421
|
+
`refs/remotes/origin/${defaultBranch}`
|
|
17422
|
+
);
|
|
17423
|
+
if (remote && !this.isAncestor(mainCheckout, remote, merge)) {
|
|
17424
|
+
this.git(mainCheckout, "reset", "--keep", "--quiet", before);
|
|
17425
|
+
this.forgetMerge(mainCheckout);
|
|
17426
|
+
this.deps.log(
|
|
17427
|
+
`origin/${defaultBranch} moved during the merge; rebasing again`
|
|
17428
|
+
);
|
|
17429
|
+
return null;
|
|
17430
|
+
}
|
|
17431
|
+
refuse(
|
|
17432
|
+
`merged ${branch} into ${defaultBranch} locally (recorded), but pushing ${defaultBranch} failed; run the command again to push`,
|
|
17433
|
+
both(pushed)
|
|
17434
|
+
);
|
|
17435
|
+
}
|
|
17436
|
+
this.deleteRemoteBranch(mainCheckout, branch);
|
|
17437
|
+
note = "; pushed to origin";
|
|
17438
|
+
}
|
|
17439
|
+
return {
|
|
17440
|
+
outcome: "merged",
|
|
17441
|
+
detail: `${branch} merged into ${defaultBranch} at ${merge.slice(0, 7)}${evidence}${note}`
|
|
17442
|
+
};
|
|
17443
|
+
}
|
|
17444
|
+
// ── GitHub ────────────────────────────────────────────────────────
|
|
17445
|
+
async viewPullRequest(worktreeDir, selector) {
|
|
17446
|
+
const view = await this.deps.gh(worktreeDir, [
|
|
17447
|
+
"pr",
|
|
17448
|
+
"view",
|
|
17449
|
+
selector,
|
|
17450
|
+
"--json",
|
|
17451
|
+
"number,state,url,headRefName,headRefOid,baseRefName,mergedAt"
|
|
17452
|
+
]);
|
|
17453
|
+
if (view.code !== 0)
|
|
17454
|
+
refuse(
|
|
17455
|
+
`no pull request found for ${selector}; the subagent opens one before the merge`,
|
|
17456
|
+
both(view)
|
|
17457
|
+
);
|
|
17458
|
+
try {
|
|
17459
|
+
return JSON.parse(view.stdout);
|
|
17460
|
+
} catch {
|
|
17461
|
+
return refuse("could not read gh pr view", both(view));
|
|
17462
|
+
}
|
|
17463
|
+
}
|
|
17464
|
+
async viaGitHub(ctx) {
|
|
17465
|
+
const { worktreeDir, mainCheckout, branch, defaultBranch } = ctx;
|
|
17466
|
+
let pr = await this.viewPullRequest(worktreeDir, this.input.pr ?? branch);
|
|
17467
|
+
if (pr.headRefName !== branch)
|
|
17468
|
+
refuse(
|
|
17469
|
+
`pull request #${pr.number} is for ${pr.headRefName}, not ${branch}`
|
|
17470
|
+
);
|
|
17471
|
+
if (pr.state === "MERGED") {
|
|
17472
|
+
this.pullMainCheckout(mainCheckout, defaultBranch);
|
|
17473
|
+
this.deleteRemoteBranch(mainCheckout, branch);
|
|
17474
|
+
return {
|
|
17475
|
+
outcome: "already_merged",
|
|
17476
|
+
detail: `pull request #${pr.number} was merged at ${pr.mergedAt ?? "an earlier time"}`
|
|
17477
|
+
};
|
|
17478
|
+
}
|
|
17479
|
+
if (pr.state !== "OPEN")
|
|
17480
|
+
refuse(`pull request #${pr.number} is ${pr.state.toLowerCase()}`);
|
|
17481
|
+
if (pr.baseRefName !== defaultBranch)
|
|
17482
|
+
refuse(
|
|
17483
|
+
`pull request #${pr.number} targets ${pr.baseRefName}, not ${defaultBranch}`
|
|
17484
|
+
);
|
|
17485
|
+
const base = `refs/remotes/origin/${defaultBranch}`;
|
|
17486
|
+
const fetchBase = () => this.must(
|
|
17487
|
+
worktreeDir,
|
|
17488
|
+
"git fetch origin",
|
|
17489
|
+
"fetch",
|
|
17490
|
+
"--quiet",
|
|
17491
|
+
"origin",
|
|
17492
|
+
defaultBranch
|
|
17493
|
+
);
|
|
17494
|
+
fetchBase();
|
|
17495
|
+
if (this.isAncestor(worktreeDir, `refs/heads/${branch}`, base))
|
|
17496
|
+
refuse(
|
|
17497
|
+
`no commits to merge: origin/${defaultBranch} already holds every commit of ${branch}`
|
|
17498
|
+
);
|
|
17499
|
+
for (let round = 1; round <= MAX_ROUNDS; round++) {
|
|
17500
|
+
if (round > 1) {
|
|
17501
|
+
pr = await this.viewPullRequest(worktreeDir, String(pr.number));
|
|
17502
|
+
fetchBase();
|
|
17503
|
+
}
|
|
17504
|
+
const baseSha = this.must(
|
|
17505
|
+
worktreeDir,
|
|
17506
|
+
"reading the base",
|
|
17507
|
+
"rev-parse",
|
|
17508
|
+
base
|
|
17509
|
+
);
|
|
17510
|
+
const sha = this.rebase(worktreeDir, base);
|
|
17511
|
+
this.pushBranch(worktreeDir, branch, pr.headRefOid, sha);
|
|
17512
|
+
const proof = await this.awaitChecks(worktreeDir, pr.number, sha);
|
|
17513
|
+
const ran = proof === "none" ? await this.runRepoChecks(mainCheckout, worktreeDir, baseSha, sha) : null;
|
|
17514
|
+
fetchBase();
|
|
17515
|
+
if (!this.isAncestor(worktreeDir, base, sha)) {
|
|
17516
|
+
this.deps.log(
|
|
17517
|
+
`origin/${defaultBranch} moved during the checks; rebasing again`
|
|
17518
|
+
);
|
|
17519
|
+
continue;
|
|
17520
|
+
}
|
|
17521
|
+
const merged = await this.deps.gh(worktreeDir, [
|
|
17522
|
+
"pr",
|
|
17523
|
+
"merge",
|
|
17524
|
+
String(pr.number),
|
|
17525
|
+
"--squash",
|
|
17526
|
+
"--match-head-commit",
|
|
17527
|
+
sha
|
|
17528
|
+
]);
|
|
17529
|
+
if (merged.code !== 0) {
|
|
17530
|
+
const after = await this.viewPullRequest(
|
|
17531
|
+
worktreeDir,
|
|
17532
|
+
String(pr.number)
|
|
17533
|
+
);
|
|
17534
|
+
if (after.state !== "MERGED")
|
|
17535
|
+
refuse(`gh pr merge #${pr.number} failed`, both(merged));
|
|
17536
|
+
}
|
|
17537
|
+
this.pullMainCheckout(mainCheckout, defaultBranch);
|
|
17538
|
+
this.deleteRemoteBranch(mainCheckout, branch);
|
|
17539
|
+
return {
|
|
17540
|
+
outcome: "merged",
|
|
17541
|
+
detail: `pull request #${pr.number} squash-merged at ${sha.slice(0, 7)} after ${ran ? `no GitHub checks; ${ran.map((check) => `\`${check}\``).join(", ")} passed` : proof}`
|
|
17542
|
+
};
|
|
17543
|
+
}
|
|
17544
|
+
return stop("checks_failed", baseKeptMoving(defaultBranch));
|
|
17545
|
+
}
|
|
17546
|
+
/**
|
|
17547
|
+
* Push the rebased branch, leased on the head GitHub reported, so a commit pushed meanwhile is never overwritten.
|
|
17548
|
+
* When the lease fails it reads origin again: already at `sha` is fine; anything else is `refused`.
|
|
17549
|
+
*/
|
|
17550
|
+
pushBranch(worktreeDir, branch, lease, sha) {
|
|
17551
|
+
const pushed = this.git(
|
|
17552
|
+
worktreeDir,
|
|
17553
|
+
"push",
|
|
17554
|
+
"--quiet",
|
|
17555
|
+
`--force-with-lease=refs/heads/${branch}:${lease}`,
|
|
17556
|
+
"origin",
|
|
17557
|
+
`${sha}:refs/heads/${branch}`
|
|
17558
|
+
);
|
|
17559
|
+
if (pushed.code === 0) return;
|
|
17560
|
+
this.git(worktreeDir, "fetch", "--quiet", "origin", branch);
|
|
17561
|
+
const remote = this.sha(worktreeDir, `refs/remotes/origin/${branch}`);
|
|
17562
|
+
if (remote === sha) return;
|
|
17563
|
+
refuse(
|
|
17564
|
+
`origin's ${branch} is at ${remote?.slice(0, 7) ?? "an unknown commit"}, not the ${lease.slice(0, 7)} the pull request reported, so the push was refused; the subagent pulls those commits into its worktree, then merge again`,
|
|
17565
|
+
both(pushed)
|
|
17566
|
+
);
|
|
17567
|
+
}
|
|
17568
|
+
/**
|
|
17569
|
+
* Wait until GitHub reports the pushed commit's checks done. Required checks decide; with none required, all
|
|
17570
|
+
* checks, which must hold for a settle period after GitHub first reports the commit; with no checks at all
|
|
17571
|
+
* after the grace period (also counted from then), `none`. A failed or cancelled check is `checks_failed`; the
|
|
17572
|
+
* bound is `checks_timeout`.
|
|
17573
|
+
*/
|
|
17574
|
+
async awaitChecks(worktreeDir, pr, sha) {
|
|
17575
|
+
let seenAt = null;
|
|
17576
|
+
for (; ; ) {
|
|
17577
|
+
const view = await this.viewPullRequest(worktreeDir, String(pr));
|
|
17578
|
+
let verdict;
|
|
17579
|
+
if (view.headRefOid !== sha) {
|
|
17580
|
+
verdict = {
|
|
17581
|
+
kind: "waiting",
|
|
17582
|
+
detail: `GitHub has not seen ${sha.slice(0, 7)} yet`
|
|
17583
|
+
};
|
|
17584
|
+
} else {
|
|
17585
|
+
seenAt ??= this.deps.now();
|
|
17586
|
+
const since = this.deps.now() - seenAt;
|
|
17587
|
+
verdict = await this.readChecks(worktreeDir, pr);
|
|
17588
|
+
if (verdict.kind === "none" && since >= this.graceMs) return "none";
|
|
17589
|
+
if (verdict.kind === "passed" && verdict.scope === "checks" && since < this.settleMs)
|
|
17590
|
+
verdict = {
|
|
17591
|
+
kind: "waiting",
|
|
17592
|
+
detail: "checks passed; waiting for late checks to register"
|
|
17593
|
+
};
|
|
17594
|
+
}
|
|
17595
|
+
if (verdict.kind === "passed") return verdict.detail;
|
|
17596
|
+
if (verdict.kind === "failed") stop("checks_failed", verdict.detail);
|
|
17597
|
+
if (this.left() < this.pollMs)
|
|
17598
|
+
stop(
|
|
17599
|
+
"checks_timeout",
|
|
17600
|
+
`the command's ${Math.round(this.timeoutMs / 1e3)} s deadline passed waiting for checks (${verdict.detail}); run the command again to keep waiting`
|
|
17601
|
+
);
|
|
17602
|
+
this.deps.log(`waiting for checks: ${verdict.detail}`);
|
|
17603
|
+
await this.deps.sleep(this.pollMs);
|
|
17604
|
+
}
|
|
17605
|
+
}
|
|
17606
|
+
async readChecks(worktreeDir, pr) {
|
|
17607
|
+
const read = async (required) => {
|
|
17608
|
+
const result = await this.deps.gh(worktreeDir, [
|
|
17609
|
+
"pr",
|
|
17610
|
+
"checks",
|
|
17611
|
+
String(pr),
|
|
17612
|
+
...required ? ["--required"] : [],
|
|
17613
|
+
"--json",
|
|
17614
|
+
"name,bucket"
|
|
17615
|
+
]);
|
|
17616
|
+
return readCheckList(result.stdout, result.stderr);
|
|
17617
|
+
};
|
|
17618
|
+
const empty = (list2) => list2.kind === "none" || list2.kind === "checks" && list2.checks.length === 0;
|
|
17619
|
+
let list = await read(true);
|
|
17620
|
+
let scope = "required checks";
|
|
17621
|
+
if (empty(list)) {
|
|
17622
|
+
list = await read(false);
|
|
17623
|
+
scope = "checks";
|
|
17624
|
+
}
|
|
17625
|
+
if (empty(list)) return { kind: "none", detail: "no checks reported" };
|
|
17626
|
+
if (list.kind === "unreadable")
|
|
17627
|
+
return { kind: "waiting", detail: list.detail };
|
|
17628
|
+
return list.kind === "checks" ? judge(list.checks, scope) : { kind: "none", detail: "no checks reported" };
|
|
17629
|
+
}
|
|
17630
|
+
/**
|
|
17631
|
+
* Delete the merged item's branch on origin, so clean-up needs no push of its own (in Run D of the sandbox a
|
|
17632
|
+
* clean-up loop's `git push origin --delete $b` parked the Run: its target is a variable). Best effort: a branch
|
|
17633
|
+
* that was never pushed, or one GitHub already deleted, is fine.
|
|
17634
|
+
*/
|
|
17635
|
+
deleteRemoteBranch(mainCheckout, branch) {
|
|
17636
|
+
if (this.git(
|
|
17637
|
+
mainCheckout,
|
|
17638
|
+
"ls-remote",
|
|
17639
|
+
"--exit-code",
|
|
17640
|
+
"--heads",
|
|
17641
|
+
"origin",
|
|
17642
|
+
branch
|
|
17643
|
+
).code !== 0)
|
|
17644
|
+
return;
|
|
17645
|
+
this.git(mainCheckout, "push", "--quiet", "origin", "--delete", branch);
|
|
17646
|
+
}
|
|
17647
|
+
/** Bring the main checkout up to date after a merge on GitHub, so the next worktree starts from it. Best effort. */
|
|
17648
|
+
pullMainCheckout(mainCheckout, defaultBranch) {
|
|
17649
|
+
if (this.checkedOut(mainCheckout) !== defaultBranch) return;
|
|
17650
|
+
this.git(
|
|
17651
|
+
mainCheckout,
|
|
17652
|
+
"pull",
|
|
17653
|
+
"--quiet",
|
|
17654
|
+
"--ff-only",
|
|
17655
|
+
"origin",
|
|
17656
|
+
defaultBranch
|
|
17657
|
+
);
|
|
17658
|
+
}
|
|
17659
|
+
};
|
|
17660
|
+
|
|
17661
|
+
// src/core/drive/processes.ts
|
|
17662
|
+
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
17663
|
+
import fs13 from "fs";
|
|
17664
|
+
import os4 from "os";
|
|
17665
|
+
import path13 from "path";
|
|
17666
|
+
|
|
17667
|
+
// src/core/drive/lock.ts
|
|
17668
|
+
import fs12 from "fs";
|
|
17669
|
+
import path12 from "path";
|
|
17670
|
+
var LOCK_FILE = "drisp-drive-merge.lock";
|
|
17671
|
+
function alive(pid) {
|
|
17672
|
+
try {
|
|
17673
|
+
process.kill(pid, 0);
|
|
17674
|
+
return true;
|
|
17675
|
+
} catch (error) {
|
|
17676
|
+
return error.code === "EPERM";
|
|
17677
|
+
}
|
|
17678
|
+
}
|
|
17679
|
+
var TAKEOVER_STALE_MS = 1e4;
|
|
17680
|
+
function ownerOf(file) {
|
|
17681
|
+
try {
|
|
17682
|
+
const owner = Number(fs12.readFileSync(file, "utf8"));
|
|
17683
|
+
return Number.isInteger(owner) && owner > 0 ? owner : null;
|
|
17684
|
+
} catch {
|
|
17685
|
+
return null;
|
|
17686
|
+
}
|
|
17687
|
+
}
|
|
17688
|
+
function takeOverStale(file, deadOwner) {
|
|
17689
|
+
const marker = `${file}.takeover`;
|
|
17690
|
+
try {
|
|
17691
|
+
fs12.writeFileSync(marker, String(process.pid), { flag: "wx" });
|
|
17692
|
+
} catch {
|
|
17693
|
+
try {
|
|
17694
|
+
if (Date.now() - fs12.statSync(marker).mtimeMs > TAKEOVER_STALE_MS)
|
|
17695
|
+
fs12.unlinkSync(marker);
|
|
17696
|
+
} catch {
|
|
17697
|
+
}
|
|
17698
|
+
return;
|
|
17699
|
+
}
|
|
17700
|
+
try {
|
|
17701
|
+
if (ownerOf(file) === deadOwner) fs12.unlinkSync(file);
|
|
17702
|
+
} catch {
|
|
17703
|
+
} finally {
|
|
17704
|
+
fs12.unlinkSync(marker);
|
|
17705
|
+
}
|
|
17706
|
+
}
|
|
17707
|
+
async function acquireLock(commonDir, waitMs) {
|
|
17708
|
+
const file = path12.join(commonDir, LOCK_FILE);
|
|
17709
|
+
const deadline = Date.now() + waitMs;
|
|
17710
|
+
for (; ; ) {
|
|
17711
|
+
try {
|
|
17712
|
+
fs12.writeFileSync(file, String(process.pid), { flag: "wx" });
|
|
17713
|
+
return () => {
|
|
17714
|
+
if (ownerOf(file) === process.pid) {
|
|
17715
|
+
try {
|
|
17716
|
+
fs12.unlinkSync(file);
|
|
17717
|
+
} catch {
|
|
17718
|
+
}
|
|
17719
|
+
}
|
|
17720
|
+
};
|
|
17721
|
+
} catch (error) {
|
|
17722
|
+
if (error.code !== "EEXIST") throw error;
|
|
17723
|
+
}
|
|
17724
|
+
const owner = ownerOf(file);
|
|
17725
|
+
if (owner !== null && !alive(owner)) {
|
|
17726
|
+
takeOverStale(file, owner);
|
|
17727
|
+
continue;
|
|
17728
|
+
}
|
|
17729
|
+
if (Date.now() >= deadline) return null;
|
|
17730
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
17731
|
+
}
|
|
17732
|
+
}
|
|
17733
|
+
|
|
17734
|
+
// src/core/drive/processes.ts
|
|
17735
|
+
var OUTPUT_CAP = 1024 * 1024;
|
|
17736
|
+
function runGit(cwd, args) {
|
|
17737
|
+
const result = spawnSync2("git", ["-C", cwd, ...args], {
|
|
17738
|
+
encoding: "utf8",
|
|
17739
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
17740
|
+
maxBuffer: 64 * 1024 * 1024
|
|
17741
|
+
});
|
|
17742
|
+
return {
|
|
17743
|
+
code: result.error ? 127 : result.status ?? 1,
|
|
17744
|
+
stdout: result.stdout,
|
|
17745
|
+
stderr: result.error ? result.error.message : result.stderr
|
|
17746
|
+
};
|
|
17747
|
+
}
|
|
17748
|
+
function runProcess(program, args, cwd, timeoutMs) {
|
|
17749
|
+
return new Promise((resolve) => {
|
|
17750
|
+
let stdout = "";
|
|
17751
|
+
let stderr = "";
|
|
17752
|
+
let timedOut = false;
|
|
17753
|
+
const child = spawn(program, args, {
|
|
17754
|
+
cwd,
|
|
17755
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
17756
|
+
detached: true
|
|
17757
|
+
});
|
|
17758
|
+
const keep = (text, chunk) => (text + chunk.toString()).slice(-OUTPUT_CAP);
|
|
17759
|
+
child.stdout.on("data", (chunk) => {
|
|
17760
|
+
stdout = keep(stdout, chunk);
|
|
17761
|
+
});
|
|
17762
|
+
child.stderr.on("data", (chunk) => {
|
|
17763
|
+
stderr = keep(stderr, chunk);
|
|
17764
|
+
});
|
|
17765
|
+
const timer = setTimeout(() => {
|
|
17766
|
+
timedOut = true;
|
|
17767
|
+
try {
|
|
17768
|
+
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
|
17769
|
+
} catch {
|
|
17770
|
+
child.kill("SIGKILL");
|
|
17771
|
+
}
|
|
17772
|
+
}, timeoutMs);
|
|
17773
|
+
child.on("error", (error) => {
|
|
17774
|
+
clearTimeout(timer);
|
|
17775
|
+
resolve({ code: 127, stdout, stderr: error.message, timedOut });
|
|
17776
|
+
});
|
|
17777
|
+
child.on("close", (code, signal) => {
|
|
17778
|
+
clearTimeout(timer);
|
|
17779
|
+
resolve({
|
|
17780
|
+
code: code ?? (signal ? 128 : 1),
|
|
17781
|
+
stdout,
|
|
17782
|
+
stderr,
|
|
17783
|
+
timedOut
|
|
17784
|
+
});
|
|
17785
|
+
});
|
|
17786
|
+
});
|
|
17787
|
+
}
|
|
17788
|
+
var GH_TIMEOUT_MS = 6e4;
|
|
17789
|
+
function runCheck(cwd, command, timeoutMs) {
|
|
17790
|
+
return runProcess(
|
|
17791
|
+
"bash",
|
|
17792
|
+
["-e", "-o", "pipefail", "-c", command],
|
|
17793
|
+
cwd,
|
|
17794
|
+
timeoutMs
|
|
17795
|
+
).then((result) => ({
|
|
17796
|
+
...result,
|
|
17797
|
+
// One stream reads better in a Report: the check's own interleaving is lost either way.
|
|
17798
|
+
stdout: [result.stdout, result.stderr].filter(Boolean).join("\n"),
|
|
17799
|
+
stderr: ""
|
|
17800
|
+
}));
|
|
17801
|
+
}
|
|
17802
|
+
function packageTestScript(dir) {
|
|
17803
|
+
try {
|
|
17804
|
+
const manifest = JSON.parse(
|
|
17805
|
+
fs13.readFileSync(path13.join(dir, "package.json"), "utf8")
|
|
17806
|
+
);
|
|
17807
|
+
const test = manifest.scripts?.["test"];
|
|
17808
|
+
return typeof test === "string" && test.trim() ? test : void 0;
|
|
17809
|
+
} catch {
|
|
17810
|
+
return void 0;
|
|
17811
|
+
}
|
|
17812
|
+
}
|
|
17813
|
+
function scratchDir() {
|
|
17814
|
+
const parent = fs13.mkdtempSync(path13.join(os4.tmpdir(), SCRATCH_PREFIX));
|
|
17815
|
+
return {
|
|
17816
|
+
dir: path13.join(parent, "checkout"),
|
|
17817
|
+
cleanup: () => fs13.rmSync(parent, { recursive: true, force: true })
|
|
17818
|
+
};
|
|
17819
|
+
}
|
|
17820
|
+
function linkDependencies(fromDir, toDir) {
|
|
17821
|
+
const modules = path13.join(fromDir, "node_modules");
|
|
17822
|
+
if (fs13.existsSync(modules) && !fs13.existsSync(path13.join(toDir, "node_modules")))
|
|
17823
|
+
fs13.symlinkSync(modules, path13.join(toDir, "node_modules"), "dir");
|
|
17824
|
+
}
|
|
17825
|
+
function processDeps(configuredChecks, log) {
|
|
17826
|
+
return {
|
|
17827
|
+
git: runGit,
|
|
17828
|
+
gh: (cwd, args) => runProcess("gh", args, cwd, GH_TIMEOUT_MS).then(
|
|
17829
|
+
({ timedOut, ...result }) => timedOut ? {
|
|
17830
|
+
...result,
|
|
17831
|
+
code: result.code || 124,
|
|
17832
|
+
stderr: `${result.stderr}
|
|
17833
|
+
gh timed out`
|
|
17834
|
+
} : result
|
|
17835
|
+
),
|
|
17836
|
+
runCheck,
|
|
17837
|
+
configuredChecks,
|
|
17838
|
+
packageTestScript,
|
|
17839
|
+
lock: acquireLock,
|
|
17840
|
+
scratchDir,
|
|
17841
|
+
linkDependencies,
|
|
17842
|
+
exists: (target) => fs13.existsSync(target),
|
|
17843
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
17844
|
+
now: () => Date.now(),
|
|
17845
|
+
log
|
|
17846
|
+
};
|
|
17847
|
+
}
|
|
17848
|
+
|
|
17849
|
+
// src/app/entry/driveCommand.ts
|
|
17850
|
+
var USAGE4 = `Usage: drisp drive merge <item> --worktree <path> [--branch <branch>] [--pr <number|url>] [--timeout-ms <ms>]
|
|
17851
|
+
Merges a drive item: already merged \u2192 already_merged; rebase onto the default branch (conflict); on GitHub push with
|
|
17852
|
+
--force-with-lease, wait for the required checks (all checks when none is required; the repository's checks when
|
|
17853
|
+
there are none) and gh pr merge; otherwise run the repository's checks (.athena/config.json "checks", else the
|
|
17854
|
+
package.json test script) and merge --no-ff into the default branch, then push it. Prints {outcome, detail, output?}
|
|
17855
|
+
with outcome merged | already_merged | conflict | checks_failed | checks_timeout | refused.`;
|
|
17856
|
+
async function runDriveCommand(input) {
|
|
17857
|
+
const stdout = input.stdout ?? process.stdout;
|
|
17858
|
+
const stderr = input.stderr ?? process.stderr;
|
|
17859
|
+
const [item, ...extra] = input.positional;
|
|
17860
|
+
if (input.subcommand !== "merge" || !item || extra.length > 0 || !input.flags.worktree || input.flags.timeoutMs !== void 0 && !(Number.isFinite(input.flags.timeoutMs) && input.flags.timeoutMs > 0)) {
|
|
17861
|
+
stderr.write(`${USAGE4}
|
|
17862
|
+
`);
|
|
17863
|
+
return 2;
|
|
17864
|
+
}
|
|
17865
|
+
const deps = input.deps ?? processDeps(
|
|
17866
|
+
(dir) => readConfig(dir).checks,
|
|
17867
|
+
(line) => stderr.write(`${line}
|
|
17868
|
+
`)
|
|
17869
|
+
);
|
|
17870
|
+
let result;
|
|
17871
|
+
try {
|
|
17872
|
+
result = await driveMerge(
|
|
17873
|
+
{
|
|
17874
|
+
item,
|
|
17875
|
+
worktree: path14.resolve(input.cwd, input.flags.worktree),
|
|
17876
|
+
...input.flags.branch ? { branch: input.flags.branch } : {},
|
|
17877
|
+
...input.flags.pr ? { pr: input.flags.pr } : {},
|
|
17878
|
+
...input.flags.timeoutMs !== void 0 ? { timeoutMs: input.flags.timeoutMs } : {}
|
|
17879
|
+
},
|
|
17880
|
+
deps
|
|
17881
|
+
);
|
|
17882
|
+
} catch (error) {
|
|
17883
|
+
result = {
|
|
17884
|
+
outcome: "refused",
|
|
17885
|
+
detail: `drive merge failed unexpectedly: ${error instanceof Error ? error.message : String(error)}`
|
|
17886
|
+
};
|
|
17887
|
+
}
|
|
17888
|
+
stdout.write(`${JSON.stringify({ item, ...result })}
|
|
17889
|
+
`);
|
|
17890
|
+
return result.outcome === "merged" || result.outcome === "already_merged" ? 0 : 1;
|
|
17891
|
+
}
|
|
17892
|
+
|
|
17893
|
+
// src/app/entry/trackerCommand.ts
|
|
17894
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
17895
|
+
async function runTrackerCommand(args, env2 = process.env) {
|
|
17896
|
+
const usage2 = "Usage: drisp tracker get <issue-id> | apply <operation.json>\nOperations: issue.revision, issue.comment, issue.attention, issue.evidence. Read the Issue first for its current version. Evidence includes revision, reference, kind and runId; staging/QA also require deploymentId. Production release confirmation is a human action.";
|
|
17897
|
+
try {
|
|
17898
|
+
if (!env2.DRISP_HUB_URL || !env2.DRISP_RUN_TOKEN)
|
|
17899
|
+
throw new Error(
|
|
17900
|
+
"This command requires a native Issue Run with DRISP_HUB_URL and DRISP_RUN_TOKEN."
|
|
17901
|
+
);
|
|
17902
|
+
const client2 = DrispClient.delegated(
|
|
17903
|
+
env2.DRISP_HUB_URL,
|
|
17904
|
+
env2.DRISP_RUN_TOKEN
|
|
17905
|
+
);
|
|
17906
|
+
const [action, argument, ...extra] = args;
|
|
17907
|
+
if (!argument || extra.length) throw new Error(usage2);
|
|
17908
|
+
if (action === "get")
|
|
17909
|
+
process.stdout.write(
|
|
17910
|
+
JSON.stringify(await client2.trackerIssue(argument)) + "\n"
|
|
17911
|
+
);
|
|
17912
|
+
else if (action === "apply") {
|
|
17913
|
+
const operation = trackerOperationSchema.parse(
|
|
17914
|
+
JSON.parse(await readFile2(argument, "utf8"))
|
|
17915
|
+
);
|
|
17916
|
+
process.stdout.write(
|
|
17917
|
+
JSON.stringify(await client2.trackerOperation(operation)) + "\n"
|
|
17918
|
+
);
|
|
17919
|
+
} else throw new Error(usage2);
|
|
17920
|
+
return 0;
|
|
17921
|
+
} catch (error) {
|
|
17922
|
+
process.stderr.write(
|
|
17923
|
+
(error instanceof Error ? error.message : String(error)) + "\n"
|
|
17924
|
+
);
|
|
17925
|
+
return 2;
|
|
17926
|
+
}
|
|
17927
|
+
}
|
|
17928
|
+
|
|
17929
|
+
// src/app/entry/workCommand.ts
|
|
17930
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
17931
|
+
var WORK_USAGE = `Usage: drisp work authority
|
|
17932
|
+
drisp work issue <issue-id> | plan <plan-id>
|
|
17933
|
+
drisp work start issue|plan <id> [--activation <file.json>] [--command-id <id>]
|
|
17934
|
+
drisp work follow-up issue|plan|execution <id> "<text>" [--command-id <id>]
|
|
17935
|
+
drisp work inspect <execution-id>
|
|
17936
|
+
drisp work hold|unhold|pause|resume <execution-id> [--reason <text>] [--generation <n>] [--command-id <id>]
|
|
17937
|
+
drisp work cancel <execution-id> [--reason <text>] [--grace-ms <0-590000>] [--generation <n>] [--command-id <id>]
|
|
17938
|
+
drisp work revise <assignment-id> --changes <file.json> --reason <text> [--command-id <id>]
|
|
17939
|
+
drisp work reconcile <effect-id> succeeded|failed --evidence <file.json> --generation <n>
|
|
17940
|
+
drisp work recover <assignment-id> --reason <text> [--session auto|original|fresh] [--runner-id <id>]
|
|
17941
|
+
drisp work close <execution-id> cancelled|failed [--acknowledge <effect-id,...>] [--reason <text>]
|
|
17942
|
+
drisp work answer <question-id> "<text>" | --decision allow|deny [--command-id <id>]
|
|
17943
|
+
drisp work accept|reject <execution-id> <package-id> "<reason>" [--generation <n>]
|
|
17944
|
+
drisp work brief
|
|
17945
|
+
drisp work forward <question-id> "<note>"
|
|
17946
|
+
drisp work incorporate <assignment-id> <version> --generation <n> [--tool-use-id <id>]
|
|
17947
|
+
DRISP_HUB_URL is the Team's Hub base (https://<hub>/v2/orgs/<team>); DRISP_HUB_TOKEN a member's token, DRISP_RUN_TOKEN a
|
|
17948
|
+
coordinated Run's credential (brief, incorporate, forward, and answer as that Run). incorporate reads
|
|
17949
|
+
DRISP_TOOL_USE_ID when set; the Runner does not set it yet (DRI-353). A control prints "requested" until the
|
|
17950
|
+
Hub records it effective; a cancel reads requested, then the cancellation's state (stopping, waiting_on_children,
|
|
17951
|
+
effects_outstanding, runs_lost), and effective only once the execution closed as cancelled, with cleanStop as the Hub
|
|
17952
|
+
recorded it. Nothing here starts a legacy Build: under legacy execution authority starts and controls are
|
|
17953
|
+
refused (execution_authority_legacy) and the Build commands stay the legacy path.`;
|
|
17954
|
+
function workCommandFlags(flags) {
|
|
17955
|
+
const result = {};
|
|
17956
|
+
for (const name of [
|
|
17957
|
+
"reason",
|
|
17958
|
+
"activation",
|
|
17959
|
+
"commandId",
|
|
17960
|
+
"toolUseId",
|
|
17961
|
+
"decision",
|
|
17962
|
+
"changes",
|
|
17963
|
+
"evidence",
|
|
17964
|
+
"session",
|
|
17965
|
+
"runnerId",
|
|
17966
|
+
"acknowledge"
|
|
17967
|
+
])
|
|
17968
|
+
if (typeof flags[name] === "string") result[name] = flags[name];
|
|
17969
|
+
for (const name of ["generation", "graceMs"])
|
|
17970
|
+
if (typeof flags[name] === "number") result[name] = flags[name];
|
|
17971
|
+
return result;
|
|
17972
|
+
}
|
|
17973
|
+
var UsageError = class extends Error {
|
|
17974
|
+
};
|
|
17975
|
+
var UNSUPPORTED = {
|
|
17976
|
+
control: "execution.control",
|
|
17977
|
+
amend: "execution.amend",
|
|
17978
|
+
activate: "execution.activate",
|
|
17979
|
+
message: "message.send",
|
|
17980
|
+
"record-effect": "effect.record",
|
|
17981
|
+
"decide-child": "coordinator.decide",
|
|
17982
|
+
build: "legacy.build_start"
|
|
17983
|
+
};
|
|
17984
|
+
var RUN_CREDENTIAL = /* @__PURE__ */ new Set(["brief", "incorporate", "forward"]);
|
|
17985
|
+
async function runWorkCommand(args, flags = {}, env2 = process.env, io = {
|
|
17986
|
+
out: (line) => process.stdout.write(line + "\n"),
|
|
17987
|
+
err: (line) => process.stderr.write(line + "\n")
|
|
17988
|
+
}) {
|
|
17989
|
+
const [sub = "", ...rest] = args;
|
|
17990
|
+
try {
|
|
17991
|
+
if (!sub || sub === "help") throw new UsageError(WORK_USAGE);
|
|
17992
|
+
const asRun = RUN_CREDENTIAL.has(sub);
|
|
17993
|
+
const token = asRun ? env2.DRISP_RUN_TOKEN ?? env2.DRISP_HUB_TOKEN : env2.DRISP_HUB_TOKEN ?? env2.DRISP_RUN_TOKEN;
|
|
17994
|
+
if (!env2.DRISP_HUB_URL || !token)
|
|
17995
|
+
throw new UsageError(
|
|
17996
|
+
`drisp work needs DRISP_HUB_URL and ${asRun ? "DRISP_RUN_TOKEN" : "DRISP_HUB_TOKEN"}.`
|
|
17997
|
+
);
|
|
17998
|
+
if (flags.generation !== void 0 && !(Number.isInteger(flags.generation) && flags.generation > 0))
|
|
17999
|
+
throw new UsageError("--generation must be a positive whole number.");
|
|
18000
|
+
const team = /\/v2\/orgs\/([^/]+)\/?$/.exec(
|
|
18001
|
+
new URL(env2.DRISP_HUB_URL).pathname
|
|
18002
|
+
);
|
|
18003
|
+
if (!team)
|
|
18004
|
+
throw new UsageError(
|
|
18005
|
+
"DRISP_HUB_URL must be the Team base, https://<hub>/v2/orgs/<team>."
|
|
18006
|
+
);
|
|
18007
|
+
const client2 = DrispClient.delegated(env2.DRISP_HUB_URL, token);
|
|
18008
|
+
const teamId = decodeURIComponent(team[1]);
|
|
18009
|
+
const flows = new WorkFlows(client2, teamId);
|
|
18010
|
+
const result = await dispatch(
|
|
18011
|
+
flows,
|
|
18012
|
+
sub,
|
|
18013
|
+
rest,
|
|
18014
|
+
flags,
|
|
18015
|
+
env2,
|
|
18016
|
+
io.readFile ?? ((path17) => readFile3(path17, "utf8"))
|
|
18017
|
+
);
|
|
18018
|
+
io.out(JSON.stringify(result));
|
|
18019
|
+
return 0;
|
|
18020
|
+
} catch (error) {
|
|
18021
|
+
if (error instanceof UsageError) {
|
|
18022
|
+
io.err(error.message);
|
|
18023
|
+
return 2;
|
|
18024
|
+
}
|
|
18025
|
+
if (error instanceof WorkFlowError || error instanceof ClientError) {
|
|
18026
|
+
io.out(
|
|
18027
|
+
JSON.stringify({
|
|
18028
|
+
error: error.code,
|
|
18029
|
+
...error instanceof ClientError ? { status: error.status } : {},
|
|
18030
|
+
...error.details
|
|
18031
|
+
})
|
|
18032
|
+
);
|
|
18033
|
+
return 1;
|
|
18034
|
+
}
|
|
18035
|
+
io.out(
|
|
18036
|
+
JSON.stringify({
|
|
18037
|
+
error: "request_failed",
|
|
18038
|
+
message: error instanceof Error ? error.message : String(error)
|
|
18039
|
+
})
|
|
18040
|
+
);
|
|
18041
|
+
return 1;
|
|
18042
|
+
}
|
|
18043
|
+
}
|
|
18044
|
+
function need(values, count) {
|
|
18045
|
+
if (values.length !== count) throw new UsageError(WORK_USAGE);
|
|
18046
|
+
return values;
|
|
18047
|
+
}
|
|
18048
|
+
function scopeOf(kind) {
|
|
18049
|
+
if (kind !== "issue" && kind !== "plan") throw new UsageError(WORK_USAGE);
|
|
18050
|
+
return kind;
|
|
18051
|
+
}
|
|
18052
|
+
async function dispatch(flows, sub, rest, flags, env2, read) {
|
|
18053
|
+
const commandId = flags.commandId;
|
|
18054
|
+
switch (sub) {
|
|
18055
|
+
case "authority": {
|
|
18056
|
+
need(rest, 0);
|
|
18057
|
+
const path17 = await flows.path();
|
|
18058
|
+
return { path: path17, operations: await flows.operations() };
|
|
18059
|
+
}
|
|
18060
|
+
case "issue": {
|
|
18061
|
+
const [id] = need(rest, 1);
|
|
18062
|
+
return flows.readIssue(id);
|
|
18063
|
+
}
|
|
18064
|
+
case "plan": {
|
|
18065
|
+
const [id] = need(rest, 1);
|
|
18066
|
+
return flows.readPlan(id);
|
|
18067
|
+
}
|
|
18068
|
+
case "start": {
|
|
18069
|
+
const [kind, id] = need(rest, 2);
|
|
18070
|
+
const activation = flags.activation ? JSON.parse(await read(flags.activation)) : void 0;
|
|
18071
|
+
if (activation && typeof activation.runId !== "string")
|
|
18072
|
+
activation.runId = crypto.randomUUID();
|
|
18073
|
+
return flows.start(
|
|
18074
|
+
{ kind: scopeOf(kind), id },
|
|
18075
|
+
{
|
|
18076
|
+
...activation ? { activation } : {},
|
|
18077
|
+
...commandId ? { commandId } : {}
|
|
18078
|
+
}
|
|
18079
|
+
);
|
|
18080
|
+
}
|
|
18081
|
+
case "follow-up": {
|
|
18082
|
+
const [kind, id, text] = need(rest, 3);
|
|
18083
|
+
const target = kind === "execution" ? { kind: "execution", id } : { kind: scopeOf(kind), id };
|
|
18084
|
+
return flows.followUp(target, text, commandId);
|
|
18085
|
+
}
|
|
18086
|
+
case "inspect": {
|
|
18087
|
+
const [id] = need(rest, 1);
|
|
18088
|
+
return flows.inspect(id);
|
|
18089
|
+
}
|
|
18090
|
+
case "hold":
|
|
18091
|
+
case "unhold":
|
|
18092
|
+
case "pause":
|
|
18093
|
+
case "resume":
|
|
18094
|
+
case "cancel": {
|
|
18095
|
+
const [id] = need(rest, 1);
|
|
18096
|
+
if (sub === "cancel" && flags.graceMs !== void 0 && !(Number.isInteger(flags.graceMs) && flags.graceMs >= 0 && flags.graceMs <= maxClientCancelBoundaryGraceMs))
|
|
18097
|
+
throw new UsageError(
|
|
18098
|
+
`--grace-ms must be a whole number of milliseconds from 0 to ${maxClientCancelBoundaryGraceMs}.`
|
|
18099
|
+
);
|
|
18100
|
+
return flows.control(id, sub, {
|
|
18101
|
+
...flags.reason ? { reason: flags.reason } : {},
|
|
18102
|
+
...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
|
|
18103
|
+
...sub === "cancel" && flags.graceMs !== void 0 ? { boundaryGraceMs: flags.graceMs } : {},
|
|
18104
|
+
...commandId ? { commandId } : {}
|
|
18105
|
+
});
|
|
18106
|
+
}
|
|
18107
|
+
case "revise": {
|
|
18108
|
+
const [id] = need(rest, 1);
|
|
18109
|
+
if (!flags.changes || !flags.reason) throw new UsageError(WORK_USAGE);
|
|
18110
|
+
const changes = JSON.parse(await read(flags.changes));
|
|
18111
|
+
return flows.revise(id, changes, flags.reason, {
|
|
18112
|
+
...commandId ? { commandId } : {}
|
|
18113
|
+
});
|
|
18114
|
+
}
|
|
18115
|
+
case "reconcile": {
|
|
18116
|
+
const [id, outcome] = need(rest, 2);
|
|
18117
|
+
if (outcome !== "succeeded" && outcome !== "failed" || !flags.evidence || flags.generation === void 0)
|
|
18118
|
+
throw new UsageError(WORK_USAGE);
|
|
18119
|
+
const evidence = JSON.parse(await read(flags.evidence));
|
|
18120
|
+
return flows.reconcile(id, outcome, evidence, {
|
|
18121
|
+
expectedGeneration: flags.generation,
|
|
18122
|
+
...commandId ? { commandId } : {}
|
|
18123
|
+
});
|
|
18124
|
+
}
|
|
18125
|
+
case "recover": {
|
|
18126
|
+
const [id] = need(rest, 1);
|
|
18127
|
+
const session = flags.session ?? "auto";
|
|
18128
|
+
if (!flags.reason || session !== "auto" && session !== "original" && session !== "fresh")
|
|
18129
|
+
throw new UsageError(WORK_USAGE);
|
|
18130
|
+
return flows.recover(id, {
|
|
18131
|
+
reason: flags.reason,
|
|
18132
|
+
session,
|
|
18133
|
+
...flags.runnerId ? { runnerId: flags.runnerId } : {},
|
|
18134
|
+
...commandId ? { commandId } : {}
|
|
18135
|
+
});
|
|
18136
|
+
}
|
|
18137
|
+
case "close": {
|
|
18138
|
+
const [id, outcome] = need(rest, 2);
|
|
18139
|
+
if (outcome !== "cancelled" && outcome !== "failed")
|
|
18140
|
+
throw new UsageError(WORK_USAGE);
|
|
18141
|
+
return flows.close(id, outcome, {
|
|
18142
|
+
...flags.reason ? { reason: flags.reason } : {},
|
|
18143
|
+
...flags.acknowledge ? { acknowledge: flags.acknowledge.split(",").filter(Boolean) } : {},
|
|
18144
|
+
...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
|
|
18145
|
+
...commandId ? { commandId } : {}
|
|
18146
|
+
});
|
|
18147
|
+
}
|
|
18148
|
+
case "answer": {
|
|
18149
|
+
const [id, text] = rest;
|
|
18150
|
+
if (!id || rest.length > 2) throw new UsageError(WORK_USAGE);
|
|
18151
|
+
const decision = flags.decision;
|
|
18152
|
+
const answer = decision === "allow" || decision === "deny" ? { kind: "permission", decision } : text ? { kind: "text", text } : void 0;
|
|
18153
|
+
if (!answer || text && flags.decision) throw new UsageError(WORK_USAGE);
|
|
18154
|
+
return flows.answer(id, answer, {
|
|
18155
|
+
...commandId ? { commandId } : {},
|
|
18156
|
+
...env2.DRISP_HUB_TOKEN ? {} : { asRun: true }
|
|
18157
|
+
});
|
|
18158
|
+
}
|
|
18159
|
+
case "accept":
|
|
18160
|
+
case "reject": {
|
|
18161
|
+
const [id, packageId, reason] = need(rest, 3);
|
|
18162
|
+
return flows.decide(id, packageId, sub, reason, {
|
|
18163
|
+
...flags.generation !== void 0 ? { expectedGeneration: flags.generation } : {},
|
|
18164
|
+
...commandId ? { commandId } : {}
|
|
18165
|
+
});
|
|
18166
|
+
}
|
|
18167
|
+
case "brief":
|
|
18168
|
+
need(rest, 0);
|
|
18169
|
+
return flows.brief();
|
|
18170
|
+
case "forward": {
|
|
18171
|
+
const [id, note] = need(rest, 2);
|
|
18172
|
+
return flows.forward(id, note, commandId ? { commandId } : {});
|
|
18173
|
+
}
|
|
18174
|
+
case "incorporate": {
|
|
18175
|
+
const [id, version2] = need(rest, 2);
|
|
18176
|
+
if (flags.generation === void 0 || !/^[1-9][0-9]*$/.test(version2))
|
|
18177
|
+
throw new UsageError(WORK_USAGE);
|
|
18178
|
+
const toolUseId = flags.toolUseId ?? env2.DRISP_TOOL_USE_ID;
|
|
18179
|
+
return flows.incorporate(id, Number(version2), {
|
|
18180
|
+
generation: flags.generation,
|
|
18181
|
+
...toolUseId ? { toolUseId } : {},
|
|
18182
|
+
...commandId ? { commandId } : {}
|
|
18183
|
+
});
|
|
18184
|
+
}
|
|
18185
|
+
default: {
|
|
18186
|
+
const operation = UNSUPPORTED[sub];
|
|
18187
|
+
const reason = operation ? WorkFlows.unsupported(operation) : void 0;
|
|
18188
|
+
if (operation && reason)
|
|
18189
|
+
throw new WorkFlowError("unsupported_operation", { operation, reason });
|
|
18190
|
+
throw new UsageError(WORK_USAGE);
|
|
18191
|
+
}
|
|
18192
|
+
}
|
|
18193
|
+
}
|
|
18194
|
+
|
|
18195
|
+
// src/app/entry/doctorCommand.ts
|
|
18196
|
+
import process7 from "process";
|
|
18197
|
+
|
|
18198
|
+
// src/harnesses/claude/system/doctorEnvironment.ts
|
|
18199
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
18200
|
+
import * as fs14 from "fs";
|
|
18201
|
+
import * as os5 from "os";
|
|
18202
|
+
import * as path15 from "path";
|
|
18203
|
+
var MANAGED_POLICY_KEYS = [
|
|
18204
|
+
"forceLoginMethod",
|
|
18205
|
+
"forceLoginOrgUUID",
|
|
18206
|
+
"allowManagedHooksOnly",
|
|
18207
|
+
"allowManagedMcpServersOnly",
|
|
18208
|
+
"allowManagedPermissionRulesOnly",
|
|
18209
|
+
"disableAutoMode",
|
|
18210
|
+
"disableBypassPermissionsMode",
|
|
18211
|
+
"forceRemoteSettingsRefresh",
|
|
18212
|
+
"availableModels",
|
|
18213
|
+
"model",
|
|
18214
|
+
"apiKeyHelper",
|
|
18215
|
+
"awsCredentialExport"
|
|
18216
|
+
];
|
|
18217
|
+
function inspectSettingsFile(scope, filePath, statFn, readFileFn) {
|
|
18218
|
+
let stat;
|
|
18219
|
+
try {
|
|
18220
|
+
stat = statFn(filePath);
|
|
18221
|
+
} catch {
|
|
18222
|
+
return { scope, path: filePath, present: false };
|
|
18223
|
+
}
|
|
18224
|
+
const info = {
|
|
18225
|
+
scope,
|
|
18226
|
+
path: filePath,
|
|
18227
|
+
present: true,
|
|
15621
18228
|
sizeBytes: stat.size,
|
|
15622
18229
|
mtime: stat.mtime.toISOString()
|
|
15623
18230
|
};
|
|
@@ -15690,14 +18297,14 @@ function tryRichAuth(claudeBinary) {
|
|
|
15690
18297
|
function collectEnvironment(options = {}) {
|
|
15691
18298
|
const platform2 = options.platform ?? process.platform;
|
|
15692
18299
|
const env2 = options.env ?? process.env;
|
|
15693
|
-
const homeDir = options.homeDir ??
|
|
18300
|
+
const homeDir = options.homeDir ?? os5.homedir();
|
|
15694
18301
|
const cwd = options.cwd ?? process.cwd();
|
|
15695
18302
|
const resolveBinary = options.resolveClaudeBinaryFn ?? resolveClaudeBinary;
|
|
15696
18303
|
const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
|
|
15697
18304
|
const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
|
|
15698
18305
|
const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
|
|
15699
|
-
const statFn = options.statFn ?? ((p) =>
|
|
15700
|
-
const readFileFn = options.readFileFn ?? ((p) =>
|
|
18306
|
+
const statFn = options.statFn ?? ((p) => fs14.statSync(p));
|
|
18307
|
+
const readFileFn = options.readFileFn ?? ((p) => fs14.readFileSync(p, "utf8"));
|
|
15701
18308
|
const settingsPaths = resolveClaudeSettingsSurfacePaths(
|
|
15702
18309
|
homeDir,
|
|
15703
18310
|
cwd,
|
|
@@ -15742,7 +18349,7 @@ function collectEnvironment(options = {}) {
|
|
|
15742
18349
|
});
|
|
15743
18350
|
let globalConfigPresent = false;
|
|
15744
18351
|
try {
|
|
15745
|
-
statFn(
|
|
18352
|
+
statFn(path15.join(homeDir, ".claude.json"));
|
|
15746
18353
|
globalConfigPresent = true;
|
|
15747
18354
|
} catch {
|
|
15748
18355
|
globalConfigPresent = false;
|
|
@@ -16222,13 +18829,13 @@ function runRunsCommand(input) {
|
|
|
16222
18829
|
// src/app/entry/cli.tsx
|
|
16223
18830
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
16224
18831
|
function resolvePackageJsonPath(entryUrl) {
|
|
16225
|
-
let currentDir =
|
|
18832
|
+
let currentDir = path16.dirname(fileURLToPath(entryUrl));
|
|
16226
18833
|
for (; ; ) {
|
|
16227
|
-
const candidatePath =
|
|
16228
|
-
if (
|
|
18834
|
+
const candidatePath = path16.join(currentDir, "package.json");
|
|
18835
|
+
if (fs15.existsSync(candidatePath)) {
|
|
16229
18836
|
return candidatePath;
|
|
16230
18837
|
}
|
|
16231
|
-
const parentDir =
|
|
18838
|
+
const parentDir = path16.dirname(currentDir);
|
|
16232
18839
|
if (parentDir === currentDir) {
|
|
16233
18840
|
throw new Error("Could not locate package.json for CLI metadata.");
|
|
16234
18841
|
}
|
|
@@ -16238,6 +18845,8 @@ function resolvePackageJsonPath(entryUrl) {
|
|
|
16238
18845
|
var require2 = createRequire(import.meta.url);
|
|
16239
18846
|
var { version } = require2(resolvePackageJsonPath(import.meta.url));
|
|
16240
18847
|
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
18848
|
+
"tracker",
|
|
18849
|
+
"work",
|
|
16241
18850
|
"setup",
|
|
16242
18851
|
"sessions",
|
|
16243
18852
|
"resume",
|
|
@@ -16251,6 +18860,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
16251
18860
|
"marketplace",
|
|
16252
18861
|
"runner",
|
|
16253
18862
|
"build",
|
|
18863
|
+
"drive",
|
|
16254
18864
|
// Deprecated alias of `runner` (#188); removed in 0.7.0.
|
|
16255
18865
|
"dashboard",
|
|
16256
18866
|
"telemetry",
|
|
@@ -16277,7 +18887,7 @@ async function flushTelemetryOptOut(config) {
|
|
|
16277
18887
|
deviceId: deviceId2,
|
|
16278
18888
|
telemetryEnabled: config.telemetry,
|
|
16279
18889
|
appVersion: version,
|
|
16280
|
-
os: `${
|
|
18890
|
+
os: `${os6.platform()}-${os6.arch()}`
|
|
16281
18891
|
});
|
|
16282
18892
|
trackTelemetryOptedOut();
|
|
16283
18893
|
await shutdownTelemetry();
|
|
@@ -16308,6 +18918,11 @@ function printExecDryRunSummary(runtimeConfig, context) {
|
|
|
16308
18918
|
lines.push(
|
|
16309
18919
|
` model: ${runtimeConfig.modelName ?? "(default)"} [${runtimeConfig.settingsProvenance?.model ?? "resolved"}]`
|
|
16310
18920
|
);
|
|
18921
|
+
lines.push(
|
|
18922
|
+
` gates: ${Object.entries(runtimeConfig.gates).map(
|
|
18923
|
+
([gate, mode]) => `${gate}=${mode} [${runtimeConfig.settingsProvenance?.gates[gate] ?? "resolved"}]`
|
|
18924
|
+
).join(", ")}`
|
|
18925
|
+
);
|
|
16311
18926
|
lines.push(
|
|
16312
18927
|
` permission grace: ${runtimeConfig.permissionGraceMs}ms [${runtimeConfig.settingsProvenance?.permissionGraceMs ?? "resolved"}]`
|
|
16313
18928
|
);
|
|
@@ -16398,6 +19013,11 @@ var cli = meow(
|
|
|
16398
19013
|
runner [sub] Run the runner that connects this machine to a Team and executes its Runs
|
|
16399
19014
|
(no subcommand: foreground; connect [code] --hub <url> [--replace], disconnect [team], status, restart, recover, help)
|
|
16400
19015
|
build plan --input <path> Plan a Build inside an Orchestrator Run (prints one JSON object)
|
|
19016
|
+
tracker get <issue> Read the current native Drisp Issue
|
|
19017
|
+
tracker apply <json> Apply a scoped native Issue operation from a JSON file
|
|
19018
|
+
work <sub> Canonical Work and coordinated executions (authority, issue, plan, start,
|
|
19019
|
+
follow-up, inspect, hold, unhold, pause, resume, cancel, close, revise, reconcile, recover, answer, accept, reject,
|
|
19020
|
+
brief, forward, incorporate); drisp work help for usage
|
|
16401
19021
|
build children List this Build's children and their interruptions
|
|
16402
19022
|
build inspect <run> [cursor] Read a child's state and paged history
|
|
16403
19023
|
build guide <run> "<text>" Guide a child or answer its delegated technical question
|
|
@@ -16407,6 +19027,8 @@ var cli = meow(
|
|
|
16407
19027
|
build step <n> Report the Run's current step: writes the step run artifact ([--qa none|web|macos|both], --profile, --merge you, --sign-off <sha>, --complete --evidence, --verify)
|
|
16408
19028
|
build check <name> -- <cmd> Run a command and record its outcome: writes the checks run artifact
|
|
16409
19029
|
build merged Record the merged pull request: writes the pull_request run artifact
|
|
19030
|
+
drive merge <item> --worktree <path> [--branch <b>] [--pr <n>] [--timeout-ms <ms>]
|
|
19031
|
+
Merge a drive item: rebase, prove the checks, merge (prints one JSON outcome)
|
|
16410
19032
|
telemetry [action] Manage anonymous telemetry (enable/disable/status)
|
|
16411
19033
|
doctor Diagnose Claude headless setup (use with --harness=claude)
|
|
16412
19034
|
|
|
@@ -16427,6 +19049,9 @@ var cli = meow(
|
|
|
16427
19049
|
--steer Queue a human steer for the head of the next Turn's prompt (repeatable; run mode)
|
|
16428
19050
|
--answer allow | deny: answer the permission a parked run is waiting on; replayed
|
|
16429
19051
|
into the re-issued call on --continue (run mode)
|
|
19052
|
+
--gate <gate>=ask|auto for this run only (repeatable; run mode). Gates:
|
|
19053
|
+
planApproval, merge, budget, irreversible. Only tightens: =auto has an
|
|
19054
|
+
effect only where the configured floor is already auto (docs/human-gates.md)
|
|
16430
19055
|
--permission-grace-ms How long a permission no rule answers is held for an attached
|
|
16431
19056
|
hub before it is deferred and the run parks (default 60000; run mode)
|
|
16432
19057
|
--json Emit JSONL events to stdout (run mode)
|
|
@@ -16514,6 +19139,10 @@ var cli = meow(
|
|
|
16514
19139
|
answer: {
|
|
16515
19140
|
type: "string"
|
|
16516
19141
|
},
|
|
19142
|
+
gate: {
|
|
19143
|
+
type: "string",
|
|
19144
|
+
isMultiple: true
|
|
19145
|
+
},
|
|
16517
19146
|
permissionGraceMs: {
|
|
16518
19147
|
type: "number"
|
|
16519
19148
|
},
|
|
@@ -16545,6 +19174,22 @@ var cli = meow(
|
|
|
16545
19174
|
isMultiple: true
|
|
16546
19175
|
},
|
|
16547
19176
|
profile: { type: "string" },
|
|
19177
|
+
// drisp drive merge (DRI-368)
|
|
19178
|
+
worktree: { type: "string" },
|
|
19179
|
+
branch: { type: "string" },
|
|
19180
|
+
pr: { type: "string" },
|
|
19181
|
+
// drisp work (DRI-342)
|
|
19182
|
+
reason: { type: "string" },
|
|
19183
|
+
generation: { type: "number" },
|
|
19184
|
+
activation: { type: "string" },
|
|
19185
|
+
commandId: { type: "string" },
|
|
19186
|
+
graceMs: { type: "number" },
|
|
19187
|
+
toolUseId: { type: "string" },
|
|
19188
|
+
decision: { type: "string" },
|
|
19189
|
+
changes: { type: "string" },
|
|
19190
|
+
session: { type: "string" },
|
|
19191
|
+
runnerId: { type: "string" },
|
|
19192
|
+
acknowledge: { type: "string" },
|
|
16548
19193
|
testFirst: { type: "string" },
|
|
16549
19194
|
review: { type: "string" },
|
|
16550
19195
|
merge: { type: "string" },
|
|
@@ -16594,7 +19239,7 @@ var cli = meow(
|
|
|
16594
19239
|
}
|
|
16595
19240
|
);
|
|
16596
19241
|
async function main() {
|
|
16597
|
-
const projectDir =
|
|
19242
|
+
const projectDir = path16.resolve(cli.flags.projectDir);
|
|
16598
19243
|
const [command, ...commandArgs] = cli.input;
|
|
16599
19244
|
const isRunCommand = command === "run" || command === "exec";
|
|
16600
19245
|
if (command && !KNOWN_COMMANDS.has(command)) {
|
|
@@ -16648,7 +19293,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16648
19293
|
await exitWith(1);
|
|
16649
19294
|
return;
|
|
16650
19295
|
}
|
|
16651
|
-
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-
|
|
19296
|
+
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-MVTO43MW.js");
|
|
16652
19297
|
const { waitUntilExit } = render(
|
|
16653
19298
|
/* @__PURE__ */ jsx24(
|
|
16654
19299
|
WorkflowInstallWizard,
|
|
@@ -16726,6 +19371,36 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16726
19371
|
);
|
|
16727
19372
|
return;
|
|
16728
19373
|
}
|
|
19374
|
+
if (command === "tracker") {
|
|
19375
|
+
await exitWith(await runTrackerCommand(commandArgs));
|
|
19376
|
+
return;
|
|
19377
|
+
}
|
|
19378
|
+
if (command === "work") {
|
|
19379
|
+
await exitWith(
|
|
19380
|
+
await runWorkCommand(
|
|
19381
|
+
commandArgs,
|
|
19382
|
+
workCommandFlags(cli.flags)
|
|
19383
|
+
)
|
|
19384
|
+
);
|
|
19385
|
+
return;
|
|
19386
|
+
}
|
|
19387
|
+
if (command === "drive") {
|
|
19388
|
+
const [subcommand = "", ...positional] = commandArgs;
|
|
19389
|
+
await exitWith(
|
|
19390
|
+
await runDriveCommand({
|
|
19391
|
+
subcommand,
|
|
19392
|
+
positional,
|
|
19393
|
+
flags: {
|
|
19394
|
+
...typeof cli.flags.worktree === "string" ? { worktree: cli.flags.worktree } : {},
|
|
19395
|
+
...typeof cli.flags.branch === "string" ? { branch: cli.flags.branch } : {},
|
|
19396
|
+
...typeof cli.flags.pr === "string" ? { pr: cli.flags.pr } : {},
|
|
19397
|
+
...typeof cli.flags.timeoutMs === "number" ? { timeoutMs: cli.flags.timeoutMs } : {}
|
|
19398
|
+
},
|
|
19399
|
+
cwd: process.cwd()
|
|
19400
|
+
})
|
|
19401
|
+
);
|
|
19402
|
+
return;
|
|
19403
|
+
}
|
|
16729
19404
|
if (command === "build") {
|
|
16730
19405
|
const rawArgs = process.argv.slice(2);
|
|
16731
19406
|
const dashIdx = rawArgs.indexOf("--");
|
|
@@ -16828,8 +19503,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16828
19503
|
const showSetup = isRunCommand ? false : shouldShowSetup({
|
|
16829
19504
|
cliInput: cli.input,
|
|
16830
19505
|
setupComplete: globalConfig.setupComplete,
|
|
16831
|
-
globalConfigExists:
|
|
16832
|
-
|
|
19506
|
+
globalConfigExists: fs15.existsSync(
|
|
19507
|
+
path16.join(os6.homedir(), ".config", "athena", "config.json")
|
|
16833
19508
|
)
|
|
16834
19509
|
});
|
|
16835
19510
|
let runtimeConfig;
|
|
@@ -16861,6 +19536,11 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16861
19536
|
await exitWith(RUN_EXIT_CODE.USAGE);
|
|
16862
19537
|
return;
|
|
16863
19538
|
}
|
|
19539
|
+
if ((cli.flags.gate ?? []).length > 0 && !isRunCommand) {
|
|
19540
|
+
console.error("Error: --gate is only supported in run mode.");
|
|
19541
|
+
await exitWith(RUN_EXIT_CODE.USAGE);
|
|
19542
|
+
return;
|
|
19543
|
+
}
|
|
16864
19544
|
if (cli.flags.dryRun) {
|
|
16865
19545
|
if (!isRunCommand) {
|
|
16866
19546
|
console.error("Error: --dry-run is only supported in exec mode.");
|
|
@@ -16886,7 +19566,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16886
19566
|
deviceId: resolvedDeviceId,
|
|
16887
19567
|
telemetryEnabled: globalConfig.telemetry,
|
|
16888
19568
|
appVersion: version,
|
|
16889
|
-
os: `${
|
|
19569
|
+
os: `${os6.platform()}-${os6.arch()}`
|
|
16890
19570
|
});
|
|
16891
19571
|
trackAppLaunched({ version, harness: runtimeConfig.harness });
|
|
16892
19572
|
if (!isRunCommand && isFirstRun && globalConfig.telemetry !== false) {
|
|
@@ -16907,7 +19587,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
16907
19587
|
timeoutMs: cli.flags.timeoutMs,
|
|
16908
19588
|
verbose: cli.flags.verbose,
|
|
16909
19589
|
permissionGraceMs: cli.flags.permissionGraceMs,
|
|
16910
|
-
answer: cli.flags.answer
|
|
19590
|
+
answer: cli.flags.answer,
|
|
19591
|
+
gates: cli.flags.gate
|
|
16911
19592
|
},
|
|
16912
19593
|
runtimeConfig
|
|
16913
19594
|
});
|