@drisp/cli 0.6.6 → 0.6.8
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 +27 -18
- package/dist/{WorkflowInstallWizard-R76OV74M.js → WorkflowInstallWizard-TQ4SF5KB.js} +3 -3
- package/dist/{chunk-7FYAABQ4.js → chunk-CIPO2P2Q.js} +9 -2
- package/dist/chunk-G3PMV62Z.js +36 -0
- package/dist/{chunk-QGAGV2OI.js → chunk-HN3WNQVY.js} +18821 -12692
- package/dist/chunk-MP5EWDIQ.js +16 -0
- package/dist/{chunk-USK5VSQ7.js → chunk-PQIB66SG.js} +1 -1
- package/dist/cli.js +605 -1643
- package/dist/dashboard-daemon.js +5 -10
- package/dist/hook-forwarder.js +2 -2
- package/dist/runner.js +5 -10
- package/package.json +6 -9
- package/dist/chunk-5V5YDSKZ.js +0 -49
- package/dist/chunk-PZ5AY32C.js +0 -10
package/dist/cli.js
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
import "./chunk-HXBCZAP7.js";
|
|
3
3
|
import {
|
|
4
4
|
CREDENTIAL_SOURCES_TRIED,
|
|
5
|
+
ExecutionStore,
|
|
5
6
|
HARNESS_PROCESS_PRESETS,
|
|
6
7
|
IndexedTimeline,
|
|
7
8
|
PORTABLE_PROVIDER_ENV_VARS,
|
|
8
9
|
RUN_EXIT_CODE,
|
|
9
|
-
RunnerStartupError,
|
|
10
|
-
alreadyRunningMessage,
|
|
11
10
|
attachRuntimeEventLoop,
|
|
12
11
|
bootstrapRuntimeConfig,
|
|
13
12
|
buildApiKeyHelperSettings,
|
|
@@ -18,19 +17,22 @@ import {
|
|
|
18
17
|
classifyFailure,
|
|
19
18
|
compactText,
|
|
20
19
|
computeInputRows,
|
|
21
|
-
|
|
20
|
+
connectAlreadyConnectedSchema,
|
|
21
|
+
connectRedeemResponseSchema,
|
|
22
|
+
connectStartResponseSchema,
|
|
22
23
|
createFeedMapper,
|
|
23
|
-
|
|
24
|
+
createLocalFeedSink,
|
|
24
25
|
createRuntime,
|
|
25
26
|
createSessionStore,
|
|
26
27
|
credentialHelperKey,
|
|
27
28
|
cursorToVisualPosition,
|
|
28
|
-
dashboardClientConfigPath,
|
|
29
29
|
detectClaudeVersion,
|
|
30
|
+
expandHome,
|
|
30
31
|
extractFriendlyServerName,
|
|
31
32
|
extractPermissionSnapshot,
|
|
32
33
|
fit,
|
|
33
34
|
fitAnsi,
|
|
35
|
+
forgetRunner,
|
|
34
36
|
formatClock,
|
|
35
37
|
formatProbeCommand,
|
|
36
38
|
formatRunLabel,
|
|
@@ -45,6 +47,8 @@ import {
|
|
|
45
47
|
getMostRecentAthenaSession,
|
|
46
48
|
getSessionMeta,
|
|
47
49
|
hintGlyphs,
|
|
50
|
+
hubHttpUrl,
|
|
51
|
+
identityHubOrigin,
|
|
48
52
|
isBashToolResponse,
|
|
49
53
|
isCommandPrefix,
|
|
50
54
|
isScopedPermissionsRequest,
|
|
@@ -55,26 +59,22 @@ import {
|
|
|
55
59
|
localAnswerDecision,
|
|
56
60
|
lookupAllCredentials,
|
|
57
61
|
lookupCredential,
|
|
62
|
+
machineInstallationId,
|
|
58
63
|
makeSkippedProbe,
|
|
59
64
|
mergeFeedItems,
|
|
60
65
|
messageGlyphs,
|
|
61
|
-
|
|
66
|
+
overriddenIdentityPath,
|
|
62
67
|
parseFrontmatter,
|
|
63
68
|
parseToolName,
|
|
69
|
+
platformRunnerServices,
|
|
64
70
|
probeSkipReason,
|
|
65
71
|
processRegistry,
|
|
66
72
|
progressGlyphs,
|
|
67
|
-
readAttachmentMirror,
|
|
68
|
-
readDashboardClientConfig,
|
|
69
|
-
readPidLock,
|
|
70
|
-
readRunnerStatusFile,
|
|
71
|
-
refreshDashboardAccessToken,
|
|
72
73
|
register,
|
|
73
74
|
registerCleanupOnExit,
|
|
74
75
|
releaseMcpAsset,
|
|
75
|
-
removeAttachmentMirror,
|
|
76
|
-
removeDashboardClientConfig,
|
|
77
76
|
renderInputLines,
|
|
77
|
+
require_string_width,
|
|
78
78
|
resolveClaudeBinary,
|
|
79
79
|
resolveClaudeSettingsSurfacePaths,
|
|
80
80
|
resolveEffectiveCapabilities,
|
|
@@ -84,28 +84,32 @@ import {
|
|
|
84
84
|
resolveRuntimeAuthOverlay,
|
|
85
85
|
resolveToolColumn,
|
|
86
86
|
runClaudeAuthStatus,
|
|
87
|
+
runConfiguredRunner,
|
|
87
88
|
runExec,
|
|
89
|
+
runMachineRunners,
|
|
88
90
|
runProbe,
|
|
89
|
-
|
|
91
|
+
runnerConfigDirectory,
|
|
92
|
+
runnerConfigSchema,
|
|
93
|
+
runnerDisconnectResponseSchema,
|
|
94
|
+
runnerIdentities,
|
|
95
|
+
runnerServiceUnit,
|
|
96
|
+
runnerSoftwareVersion,
|
|
97
|
+
serviceInstallationId,
|
|
90
98
|
sessionsDir,
|
|
91
99
|
sliceAnsi,
|
|
92
100
|
spaces,
|
|
93
101
|
startDashboardDecisionDrain,
|
|
94
|
-
startRunnerProcess,
|
|
95
102
|
startWorkflowExecution,
|
|
96
|
-
stringWidth,
|
|
97
|
-
stripAnsi,
|
|
98
103
|
supportsSessionApproval,
|
|
104
|
+
teamIdentityPath,
|
|
99
105
|
todoGlyphSet,
|
|
100
106
|
visualPositionToOffset,
|
|
101
|
-
waitForShutdownSignal,
|
|
102
107
|
wrapText,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
} from "./chunk-QGAGV2OI.js";
|
|
108
|
+
writeIdentityOnce
|
|
109
|
+
} from "./chunk-HN3WNQVY.js";
|
|
106
110
|
import {
|
|
107
111
|
generateId as generateId2
|
|
108
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-PQIB66SG.js";
|
|
109
113
|
import {
|
|
110
114
|
McpOptionsStep,
|
|
111
115
|
StepSelector,
|
|
@@ -144,17 +148,19 @@ import {
|
|
|
144
148
|
wakesFreshAfterHandover,
|
|
145
149
|
writeGlobalConfig,
|
|
146
150
|
writeProjectConfig
|
|
147
|
-
} from "./chunk-
|
|
148
|
-
import
|
|
151
|
+
} from "./chunk-CIPO2P2Q.js";
|
|
152
|
+
import {
|
|
153
|
+
__toESM
|
|
154
|
+
} from "./chunk-G3PMV62Z.js";
|
|
149
155
|
|
|
150
156
|
// src/app/entry/cli.tsx
|
|
151
157
|
import { render } from "ink";
|
|
152
158
|
import meow from "meow";
|
|
153
|
-
import
|
|
154
|
-
import { createRequire
|
|
155
|
-
import
|
|
156
|
-
import
|
|
157
|
-
import { fileURLToPath
|
|
159
|
+
import fs6 from "fs";
|
|
160
|
+
import { createRequire } from "module";
|
|
161
|
+
import os5 from "os";
|
|
162
|
+
import path6 from "path";
|
|
163
|
+
import { fileURLToPath } from "url";
|
|
158
164
|
|
|
159
165
|
// src/app/shell/sessionUiState.ts
|
|
160
166
|
var DEFAULT_STATIC_FLOOR = 0;
|
|
@@ -2175,7 +2181,7 @@ function useFeed(runtime, messages = [], initialAllowedTools, sessionStore, opti
|
|
|
2175
2181
|
const feedEventsRef = useRef([]);
|
|
2176
2182
|
const notifiedRuntimeErrorRef = useRef(null);
|
|
2177
2183
|
const dashboardFeedPublisher = useMemo3(
|
|
2178
|
-
() => options?.dashboardFeedPublisher ??
|
|
2184
|
+
() => options?.dashboardFeedPublisher ?? createLocalFeedSink(),
|
|
2179
2185
|
[options?.dashboardFeedPublisher]
|
|
2180
2186
|
);
|
|
2181
2187
|
const ownsFeedPublisher = !options?.dashboardFeedPublisher;
|
|
@@ -2214,10 +2220,7 @@ function useFeed(runtime, messages = [], initialAllowedTools, sessionStore, opti
|
|
|
2214
2220
|
useEffect(() => {
|
|
2215
2221
|
const athenaSessionId = resolveAthenaSessionId();
|
|
2216
2222
|
if (!athenaSessionId) return;
|
|
2217
|
-
if (!dashboardDecisionInboxRef.current)
|
|
2218
|
-
if (!readDashboardClientConfig()) return;
|
|
2219
|
-
dashboardDecisionInboxRef.current = createDashboardDecisionInbox();
|
|
2220
|
-
}
|
|
2223
|
+
if (!dashboardDecisionInboxRef.current) return;
|
|
2221
2224
|
const inbox = dashboardDecisionInboxRef.current;
|
|
2222
2225
|
const drain = startDashboardDecisionDrain({
|
|
2223
2226
|
runtime,
|
|
@@ -2749,7 +2752,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2749
2752
|
}
|
|
2750
2753
|
setIsRunning(false);
|
|
2751
2754
|
}, [base, cancelCurrentRun]);
|
|
2752
|
-
const
|
|
2755
|
+
const spawn = useCallback6(
|
|
2753
2756
|
async (prompt, continuation, _configOverride) => {
|
|
2754
2757
|
await cancelCurrentRun();
|
|
2755
2758
|
const latest = input.store?.getLatestRun();
|
|
@@ -2821,7 +2824,7 @@ function useWorkflowSessionController(base, input) {
|
|
|
2821
2824
|
}, []);
|
|
2822
2825
|
return {
|
|
2823
2826
|
...base,
|
|
2824
|
-
startTurn:
|
|
2827
|
+
startTurn: spawn,
|
|
2825
2828
|
isRunning,
|
|
2826
2829
|
interrupt,
|
|
2827
2830
|
kill,
|
|
@@ -3014,7 +3017,7 @@ import React2, {
|
|
|
3014
3017
|
} from "react";
|
|
3015
3018
|
import { Box as Box8, Text as Text11, useInput as useInput7 } from "ink";
|
|
3016
3019
|
|
|
3017
|
-
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
3020
|
+
// ../../node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
3018
3021
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
3019
3022
|
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
3020
3023
|
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
@@ -3200,7 +3203,7 @@ function assembleStyles() {
|
|
|
3200
3203
|
var ansiStyles = assembleStyles();
|
|
3201
3204
|
var ansi_styles_default = ansiStyles;
|
|
3202
3205
|
|
|
3203
|
-
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
3206
|
+
// ../../node_modules/chalk/source/vendor/supports-color/index.js
|
|
3204
3207
|
import process3 from "process";
|
|
3205
3208
|
import os from "os";
|
|
3206
3209
|
import tty from "tty";
|
|
@@ -3332,7 +3335,7 @@ var supportsColor = {
|
|
|
3332
3335
|
};
|
|
3333
3336
|
var supports_color_default = supportsColor;
|
|
3334
3337
|
|
|
3335
|
-
// node_modules/chalk/source/utilities.js
|
|
3338
|
+
// ../../node_modules/chalk/source/utilities.js
|
|
3336
3339
|
function stringReplaceAll(string, substring, replacer) {
|
|
3337
3340
|
let index = string.indexOf(substring);
|
|
3338
3341
|
if (index === -1) {
|
|
@@ -3362,7 +3365,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
|
3362
3365
|
return returnValue;
|
|
3363
3366
|
}
|
|
3364
3367
|
|
|
3365
|
-
// node_modules/chalk/source/index.js
|
|
3368
|
+
// ../../node_modules/chalk/source/index.js
|
|
3366
3369
|
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
3367
3370
|
var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
|
|
3368
3371
|
var STYLER = /* @__PURE__ */ Symbol("STYLER");
|
|
@@ -4968,6 +4971,27 @@ import process5 from "process";
|
|
|
4968
4971
|
import { useState as useState11, useCallback as useCallback12, useRef as useRef12, useEffect as useEffect10 } from "react";
|
|
4969
4972
|
import { useInput as useInput11 } from "ink";
|
|
4970
4973
|
|
|
4974
|
+
// ../../node_modules/ansi-regex/index.js
|
|
4975
|
+
function ansiRegex({ onlyFirst = false } = {}) {
|
|
4976
|
+
const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
|
|
4977
|
+
const osc = `(?:(?:\\u001B\\]|\\u009D)[^\\u0007\\u001B\\u009C\\u009D]*${ST})`;
|
|
4978
|
+
const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
|
|
4979
|
+
const pattern = `${osc}|${csi}`;
|
|
4980
|
+
return new RegExp(pattern, onlyFirst ? void 0 : "g");
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4983
|
+
// ../../node_modules/strip-ansi/index.js
|
|
4984
|
+
var regex = ansiRegex();
|
|
4985
|
+
function stripAnsi(string) {
|
|
4986
|
+
if (typeof string !== "string") {
|
|
4987
|
+
throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
|
|
4988
|
+
}
|
|
4989
|
+
if (!string.includes("\x1B") && !string.includes("\x9B")) {
|
|
4990
|
+
return string;
|
|
4991
|
+
}
|
|
4992
|
+
return string.replace(regex, "");
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4971
4995
|
// src/shared/utils/clipboard.ts
|
|
4972
4996
|
import { execSync } from "child_process";
|
|
4973
4997
|
function copyToClipboard(text) {
|
|
@@ -5254,14 +5278,14 @@ function extractWebSearch(_input, response) {
|
|
|
5254
5278
|
return { type: "text", content: extractTextContent(response), maxLines: 10 };
|
|
5255
5279
|
}
|
|
5256
5280
|
function extractNotebookEdit(input, _response) {
|
|
5257
|
-
const
|
|
5281
|
+
const path7 = typeof input["notebook_path"] === "string" ? input["notebook_path"] : "";
|
|
5258
5282
|
const mode = typeof input["edit_mode"] === "string" ? input["edit_mode"] : "replace";
|
|
5259
5283
|
const source = typeof input["new_source"] === "string" ? input["new_source"] : "";
|
|
5260
|
-
if (!source) return { type: "text", content: `${mode} cell in ${
|
|
5284
|
+
if (!source) return { type: "text", content: `${mode} cell in ${path7}` };
|
|
5261
5285
|
return {
|
|
5262
5286
|
type: "code",
|
|
5263
5287
|
content: source,
|
|
5264
|
-
language: detectLanguage(
|
|
5288
|
+
language: detectLanguage(path7),
|
|
5265
5289
|
maxLines: 10
|
|
5266
5290
|
};
|
|
5267
5291
|
}
|
|
@@ -5355,6 +5379,7 @@ import { highlight } from "cli-highlight";
|
|
|
5355
5379
|
// src/shared/markdown/renderMarkdown.ts
|
|
5356
5380
|
import { Marked } from "marked";
|
|
5357
5381
|
import { markedTerminal } from "marked-terminal";
|
|
5382
|
+
var import_string_width = __toESM(require_string_width(), 1);
|
|
5358
5383
|
import Table from "cli-table3";
|
|
5359
5384
|
|
|
5360
5385
|
// src/shared/utils/hyperlink.ts
|
|
@@ -5558,11 +5583,11 @@ function inferContinuationPrefix(line) {
|
|
|
5558
5583
|
/^(\s*(?:(?:[•*-]|\d+\.)\s+)?(?:\[[ xX]\]\s+)?[^:\n]{1,40}:\s+)/
|
|
5559
5584
|
);
|
|
5560
5585
|
if (fieldMatch) {
|
|
5561
|
-
return " ".repeat(
|
|
5586
|
+
return " ".repeat((0, import_string_width.default)(fieldMatch[1]));
|
|
5562
5587
|
}
|
|
5563
5588
|
const listMatch = line.match(/^(\s*(?:[•*-]|\d+\.)\s+(?:\[[ xX]\]\s+)?)/);
|
|
5564
5589
|
if (listMatch) {
|
|
5565
|
-
return " ".repeat(
|
|
5590
|
+
return " ".repeat((0, import_string_width.default)(listMatch[1]));
|
|
5566
5591
|
}
|
|
5567
5592
|
const indentMatch = line.match(/^(\s+)/);
|
|
5568
5593
|
return indentMatch?.[1] ?? "";
|
|
@@ -5570,15 +5595,15 @@ function inferContinuationPrefix(line) {
|
|
|
5570
5595
|
function wrapAnsiLine(line, maxWidth) {
|
|
5571
5596
|
if (maxWidth <= 0) return [""];
|
|
5572
5597
|
if (line.length === 0) return [""];
|
|
5573
|
-
if (
|
|
5598
|
+
if ((0, import_string_width.default)(line) <= maxWidth) return [line];
|
|
5574
5599
|
const chunks = [];
|
|
5575
5600
|
let remaining = line;
|
|
5576
5601
|
let firstLine = true;
|
|
5577
5602
|
const continuationPrefix = inferContinuationPrefix(line);
|
|
5578
|
-
const continuationWidth =
|
|
5603
|
+
const continuationWidth = (0, import_string_width.default)(continuationPrefix);
|
|
5579
5604
|
while (remaining.length > 0) {
|
|
5580
5605
|
const availableWidth = firstLine ? maxWidth : Math.max(1, maxWidth - continuationWidth);
|
|
5581
|
-
if (
|
|
5606
|
+
if ((0, import_string_width.default)(remaining) <= availableWidth) {
|
|
5582
5607
|
chunks.push(firstLine ? remaining : continuationPrefix + remaining);
|
|
5583
5608
|
break;
|
|
5584
5609
|
}
|
|
@@ -5589,7 +5614,7 @@ function wrapAnsiLine(line, maxWidth) {
|
|
|
5589
5614
|
const wrapped = chunk.slice(0, breakAt);
|
|
5590
5615
|
if (wrapped.trim().length > 0) {
|
|
5591
5616
|
chunk = wrapped;
|
|
5592
|
-
consumedWidth = Math.min(availableWidth,
|
|
5617
|
+
consumedWidth = Math.min(availableWidth, (0, import_string_width.default)(wrapped) + 1);
|
|
5593
5618
|
}
|
|
5594
5619
|
}
|
|
5595
5620
|
chunks.push(firstLine ? chunk : continuationPrefix + chunk);
|
|
@@ -5658,13 +5683,14 @@ function renderMarkdown(options) {
|
|
|
5658
5683
|
}
|
|
5659
5684
|
|
|
5660
5685
|
// src/ui/layout/renderDetailLines.ts
|
|
5686
|
+
var import_string_width2 = __toESM(require_string_width(), 1);
|
|
5661
5687
|
var MAX_HIGHLIGHT_SIZE = 5e4;
|
|
5662
5688
|
function wrapAnsiLine2(line, maxWidth) {
|
|
5663
5689
|
if (maxWidth <= 0) return [""];
|
|
5664
5690
|
if (line.length === 0) return [""];
|
|
5665
|
-
if (
|
|
5691
|
+
if ((0, import_string_width2.default)(line) <= maxWidth) return [line];
|
|
5666
5692
|
const chunks = [];
|
|
5667
|
-
const visualWidth =
|
|
5693
|
+
const visualWidth = (0, import_string_width2.default)(line);
|
|
5668
5694
|
for (let start = 0; start < visualWidth; start += maxWidth) {
|
|
5669
5695
|
chunks.push(sliceAnsi(line, start, start + maxWidth));
|
|
5670
5696
|
}
|
|
@@ -5785,10 +5811,10 @@ function extractToolSubject(toolName, toolInput) {
|
|
|
5785
5811
|
const parts = [];
|
|
5786
5812
|
const pattern = str(input, "pattern");
|
|
5787
5813
|
const glob = str(input, "glob");
|
|
5788
|
-
const
|
|
5814
|
+
const path7 = str(input, "path");
|
|
5789
5815
|
if (pattern) parts.push(`"${pattern}"`);
|
|
5790
5816
|
if (glob) parts.push(glob);
|
|
5791
|
-
if (
|
|
5817
|
+
if (path7) parts.push(`in ${path7}`);
|
|
5792
5818
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
5793
5819
|
}
|
|
5794
5820
|
case "Skill":
|
|
@@ -8185,6 +8211,9 @@ function buildHeaderModel(input) {
|
|
|
8185
8211
|
};
|
|
8186
8212
|
}
|
|
8187
8213
|
|
|
8214
|
+
// src/ui/header/renderLines.ts
|
|
8215
|
+
var import_string_width3 = __toESM(require_string_width(), 1);
|
|
8216
|
+
|
|
8188
8217
|
// src/ui/header/contextBar.ts
|
|
8189
8218
|
function formatTokenCount(value) {
|
|
8190
8219
|
if (value === null) return "--";
|
|
@@ -8411,7 +8440,7 @@ function renderHeaderLines(model, width, hasColor, theme) {
|
|
|
8411
8440
|
};
|
|
8412
8441
|
const currentLeft = [...leftTokens];
|
|
8413
8442
|
const totalTarget = Math.max(1, width - 1);
|
|
8414
|
-
while (currentLeft.length > 1 &&
|
|
8443
|
+
while (currentLeft.length > 1 && (0, import_string_width3.default)(buildLine(currentLeft)) + MIN_GAP + (0, import_string_width3.default)(ctxText) > totalTarget) {
|
|
8415
8444
|
let minIdx = 1;
|
|
8416
8445
|
let minPri = currentLeft[1].priority;
|
|
8417
8446
|
for (let i = 2; i < currentLeft.length; i++) {
|
|
@@ -8423,10 +8452,10 @@ function renderHeaderLines(model, width, hasColor, theme) {
|
|
|
8423
8452
|
currentLeft.splice(minIdx, 1);
|
|
8424
8453
|
}
|
|
8425
8454
|
const left = buildLine(currentLeft);
|
|
8426
|
-
const usedWidth =
|
|
8455
|
+
const usedWidth = (0, import_string_width3.default)(left) + (0, import_string_width3.default)(ctxText);
|
|
8427
8456
|
const gapWidth = Math.max(MIN_GAP, totalTarget - usedWidth);
|
|
8428
8457
|
const line = `${left}${" ".repeat(gapWidth)}${ctxText}`;
|
|
8429
|
-
const lineWidth =
|
|
8458
|
+
const lineWidth = (0, import_string_width3.default)(line);
|
|
8430
8459
|
const padded = lineWidth < totalTarget ? line + " ".repeat(totalTarget - lineWidth) : line;
|
|
8431
8460
|
return [padded];
|
|
8432
8461
|
}
|
|
@@ -12018,12 +12047,12 @@ function shouldShowSetup({
|
|
|
12018
12047
|
}
|
|
12019
12048
|
|
|
12020
12049
|
// src/app/entry/execCommand.ts
|
|
12021
|
-
import
|
|
12050
|
+
import crypto3 from "crypto";
|
|
12022
12051
|
|
|
12023
12052
|
// src/app/entry/resumeResolution.ts
|
|
12024
|
-
import
|
|
12053
|
+
import crypto2 from "crypto";
|
|
12025
12054
|
function resolveResumeTarget(input) {
|
|
12026
|
-
const createSessionId = input.createSessionId ??
|
|
12055
|
+
const createSessionId = input.createSessionId ?? crypto2.randomUUID;
|
|
12027
12056
|
const getSessionMetaFn = input.getSessionMetaFn ?? getSessionMeta;
|
|
12028
12057
|
const getMostRecentSessionFn = input.getMostRecentSessionFn ?? getMostRecentAthenaSession;
|
|
12029
12058
|
const getLatestRunFn = input.getLatestRunFn ?? getLatestRunForSession;
|
|
@@ -12085,7 +12114,7 @@ function continueFlagToRequest(continueFlag) {
|
|
|
12085
12114
|
}
|
|
12086
12115
|
async function runExecCommand(input, deps = {}) {
|
|
12087
12116
|
const logError = deps.logError ?? console.error;
|
|
12088
|
-
const createSessionId = deps.createSessionId ??
|
|
12117
|
+
const createSessionId = deps.createSessionId ?? crypto3.randomUUID;
|
|
12089
12118
|
const now = deps.now ?? Date.now;
|
|
12090
12119
|
const runExecFn = deps.runExecFn ?? runExec;
|
|
12091
12120
|
const getMostRecentSessionFn = deps.getMostRecentSessionFn ?? getMostRecentAthenaSession;
|
|
@@ -12970,1564 +12999,518 @@ Default: ${DEFAULT_MARKETPLACE_SLUG3}`
|
|
|
12970
12999
|
}
|
|
12971
13000
|
}
|
|
12972
13001
|
|
|
12973
|
-
// src/app/entry/
|
|
12974
|
-
import
|
|
12975
|
-
import {
|
|
12976
|
-
import fs6 from "fs";
|
|
12977
|
-
import { createRequire } from "module";
|
|
12978
|
-
import os5 from "os";
|
|
12979
|
-
import path6 from "path";
|
|
12980
|
-
import { fileURLToPath } from "url";
|
|
13002
|
+
// src/app/entry/runnerCommandV2.ts
|
|
13003
|
+
import { existsSync } from "fs";
|
|
13004
|
+
import { join as join2 } from "path";
|
|
12981
13005
|
|
|
12982
|
-
//
|
|
12983
|
-
import
|
|
12984
|
-
import
|
|
12985
|
-
import
|
|
12986
|
-
|
|
12987
|
-
|
|
12988
|
-
|
|
12989
|
-
const home = env2["HOME"] ?? os4.homedir();
|
|
12990
|
-
if (platform === "darwin") {
|
|
12991
|
-
const target = options.targetPath ?? path5.join(home, "Library", "LaunchAgents", "ai.drisp.daemon.plist");
|
|
12992
|
-
const paths = runnerStatePaths(env2);
|
|
12993
|
-
const plist = renderLaunchdPlist({
|
|
12994
|
-
label: "ai.drisp.daemon",
|
|
12995
|
-
nodeBinary: options.nodeBinary,
|
|
12996
|
-
daemonEntry: options.runnerEntry,
|
|
12997
|
-
workingDirectory: home,
|
|
12998
|
-
stdoutPath: paths.logPath,
|
|
12999
|
-
stderrPath: paths.logPath
|
|
13000
|
-
});
|
|
13001
|
-
writeIfChanged(target, plist);
|
|
13002
|
-
return {
|
|
13003
|
-
ok: true,
|
|
13004
|
-
platform: "darwin",
|
|
13005
|
-
path: target,
|
|
13006
|
-
loadCommand: `launchctl load -w ${target}`,
|
|
13007
|
-
startCommand: "launchctl start ai.drisp.daemon"
|
|
13008
|
-
};
|
|
13009
|
-
}
|
|
13010
|
-
if (platform === "linux") {
|
|
13011
|
-
const target = options.targetPath ?? path5.join(home, ".config", "systemd", "user", "drisp-daemon.service");
|
|
13012
|
-
const unit = renderSystemdUnit({
|
|
13013
|
-
description: "Drisp runner",
|
|
13014
|
-
nodeBinary: options.nodeBinary,
|
|
13015
|
-
daemonEntry: options.runnerEntry
|
|
13016
|
-
});
|
|
13017
|
-
writeIfChanged(target, unit);
|
|
13018
|
-
return {
|
|
13019
|
-
ok: true,
|
|
13020
|
-
platform: "linux",
|
|
13021
|
-
path: target,
|
|
13022
|
-
loadCommand: "systemctl --user daemon-reload",
|
|
13023
|
-
startCommand: "systemctl --user enable --now drisp-daemon.service"
|
|
13024
|
-
};
|
|
13025
|
-
}
|
|
13026
|
-
return {
|
|
13027
|
-
ok: false,
|
|
13028
|
-
platform: "unsupported",
|
|
13029
|
-
message: `service install not supported on ${platform}`
|
|
13030
|
-
};
|
|
13031
|
-
}
|
|
13032
|
-
function writeIfChanged(target, content) {
|
|
13033
|
-
const dir = path5.dirname(target);
|
|
13034
|
-
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
13035
|
-
let existing = null;
|
|
13036
|
-
try {
|
|
13037
|
-
existing = fs5.readFileSync(target, "utf-8");
|
|
13038
|
-
} catch (err) {
|
|
13039
|
-
if (err.code !== "ENOENT") throw err;
|
|
13040
|
-
}
|
|
13041
|
-
if (existing === content) return;
|
|
13042
|
-
fs5.writeFileSync(target, content, { mode: 384 });
|
|
13043
|
-
}
|
|
13044
|
-
function renderLaunchdPlist(input) {
|
|
13045
|
-
const argv = [input.nodeBinary, input.daemonEntry].map((s) => ` <string>${escapeXml(s)}</string>`).join("\n");
|
|
13046
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
13047
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
13048
|
-
<plist version="1.0">
|
|
13049
|
-
<dict>
|
|
13050
|
-
<key>Label</key>
|
|
13051
|
-
<string>${escapeXml(input.label)}</string>
|
|
13052
|
-
<key>ProgramArguments</key>
|
|
13053
|
-
<array>
|
|
13054
|
-
${argv}
|
|
13055
|
-
</array>
|
|
13056
|
-
<key>RunAtLoad</key>
|
|
13057
|
-
<true/>
|
|
13058
|
-
<key>KeepAlive</key>
|
|
13059
|
-
<dict>
|
|
13060
|
-
<key>SuccessfulExit</key>
|
|
13061
|
-
<false/>
|
|
13062
|
-
</dict>
|
|
13063
|
-
<key>WorkingDirectory</key>
|
|
13064
|
-
<string>${escapeXml(input.workingDirectory)}</string>
|
|
13065
|
-
<key>StandardOutPath</key>
|
|
13066
|
-
<string>${escapeXml(input.stdoutPath)}</string>
|
|
13067
|
-
<key>StandardErrorPath</key>
|
|
13068
|
-
<string>${escapeXml(input.stderrPath)}</string>
|
|
13069
|
-
<key>ProcessType</key>
|
|
13070
|
-
<string>Background</string>
|
|
13071
|
-
</dict>
|
|
13072
|
-
</plist>
|
|
13073
|
-
`;
|
|
13074
|
-
}
|
|
13075
|
-
function renderSystemdUnit(input) {
|
|
13076
|
-
return `[Unit]
|
|
13077
|
-
Description=${input.description}
|
|
13078
|
-
After=network-online.target
|
|
13079
|
-
|
|
13080
|
-
[Service]
|
|
13081
|
-
Type=simple
|
|
13082
|
-
ExecStart=${input.nodeBinary} ${input.daemonEntry}
|
|
13083
|
-
Restart=always
|
|
13084
|
-
RestartSec=5
|
|
13085
|
-
SuccessExitStatus=0
|
|
13086
|
-
StandardOutput=journal
|
|
13087
|
-
StandardError=journal
|
|
13088
|
-
|
|
13089
|
-
[Install]
|
|
13090
|
-
WantedBy=default.target
|
|
13091
|
-
`;
|
|
13092
|
-
}
|
|
13093
|
-
function escapeXml(input) {
|
|
13094
|
-
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
13095
|
-
}
|
|
13096
|
-
|
|
13097
|
-
// src/app/entry/runnerCommand.ts
|
|
13098
|
-
var USAGE3 = `Usage: drisp runner [subcommand] [options]
|
|
13099
|
-
|
|
13100
|
-
The runner is the one long-lived process that pairs this machine with the
|
|
13101
|
-
hub, receives its Runs over the instance socket, and executes them. Its
|
|
13102
|
-
state lives in ~/.local/state/drisp/ (runner.pid, runner.db, runner.log,
|
|
13103
|
-
runner.status.json).
|
|
13104
|
-
|
|
13105
|
-
Subcommands:
|
|
13106
|
-
(none) Run the runner in the foreground. Exits non-zero with
|
|
13107
|
-
"already running (pid N)" when another runner holds the pid file.
|
|
13108
|
-
--detach Start the runner in the background and verify it reaches the
|
|
13109
|
-
hub's socket.
|
|
13110
|
-
stop Stop the background runner (SIGTERM to the pid in runner.pid).
|
|
13111
|
-
restart Stop, then start detached.
|
|
13112
|
-
status Show pairing, the runner process (from runner.pid and
|
|
13113
|
-
runner.status.json), socket health, and token freshness.
|
|
13114
|
-
Non-zero on any unhealthy axis. --runner <id> also checks the
|
|
13115
|
-
hub-side runner binding.
|
|
13116
|
-
logs [--tail N] [--follow]
|
|
13117
|
-
Tail the runner log at ~/.local/state/drisp/runner.log.
|
|
13118
|
-
runs [--active] [--limit N]
|
|
13119
|
-
List runs the runner has handled (last 100, from the status file).
|
|
13120
|
-
install Generate a launchd plist (macOS) or systemd user unit (linux)
|
|
13121
|
-
so the runner starts automatically on login.
|
|
13122
|
-
pair <token> --url <dashboard-origin> [--name <machine-name>]
|
|
13123
|
-
Pair this machine with the hub and start the runner.
|
|
13124
|
-
unpair Stop the runner, revoke the refresh token, and remove the
|
|
13125
|
-
local config.
|
|
13126
|
-
refresh Mint a short-lived access token (rotates the refresh token).
|
|
13127
|
-
doctor Verify pairing health. With --runner <id>, also confirms the
|
|
13128
|
-
hub-side runner is bound to this instance.
|
|
13129
|
-
list Print the local attachment mirror (the hub-side runners bound
|
|
13130
|
-
to this instance at the last pair/refresh).
|
|
13131
|
-
|
|
13132
|
-
Options:
|
|
13133
|
-
--detach Run in the background (no subcommand)
|
|
13134
|
-
--url <origin> Dashboard origin (required for pair)
|
|
13135
|
-
--runner <id> Hub-side runner id (optional for "doctor" / "status")
|
|
13136
|
-
--name <name> Friendly machine name (optional, defaults to hostname)
|
|
13137
|
-
--tail N Number of trailing log lines (default 20)
|
|
13138
|
-
--follow Stream new log lines until interrupted
|
|
13139
|
-
--active Show only active runs
|
|
13140
|
-
--limit N Cap the number of runs returned
|
|
13141
|
-
--json Emit machine-readable JSON output
|
|
13142
|
-
|
|
13143
|
-
"drisp dashboard <subcommand>" is a deprecated alias of this command and is
|
|
13144
|
-
removed in 0.7.0.
|
|
13145
|
-
`;
|
|
13146
|
-
var require_ = createRequire(import.meta.url);
|
|
13147
|
-
var cachedVersion = null;
|
|
13148
|
-
function readPackageVersion() {
|
|
13149
|
-
if (cachedVersion !== null) return cachedVersion;
|
|
13006
|
+
// ../../modules/runtime/src/connect.ts
|
|
13007
|
+
import { readFile, rename } from "fs/promises";
|
|
13008
|
+
import { hostname, platform, release } from "os";
|
|
13009
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
13010
|
+
var LOOPBACK = ["localhost", "127.0.0.1", "[::1]"];
|
|
13011
|
+
function hubOrigin(value) {
|
|
13012
|
+
let url;
|
|
13150
13013
|
try {
|
|
13151
|
-
|
|
13152
|
-
if (typeof injected === "string" && injected.length > 0) {
|
|
13153
|
-
cachedVersion = injected;
|
|
13154
|
-
return cachedVersion;
|
|
13155
|
-
}
|
|
13014
|
+
url = new URL(value);
|
|
13156
13015
|
} catch {
|
|
13016
|
+
throw new Error(`${value} is not a Hub URL.`);
|
|
13017
|
+
}
|
|
13018
|
+
if (url.username || url.password || url.search || url.hash)
|
|
13019
|
+
throw new Error("The Hub URL must not carry credentials, a query or a fragment.");
|
|
13020
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && LOOPBACK.includes(url.hostname)))
|
|
13021
|
+
throw new Error("The Hub URL must use https (http is allowed only on this machine).");
|
|
13022
|
+
return new URL(url.origin);
|
|
13023
|
+
}
|
|
13024
|
+
var refusals = {
|
|
13025
|
+
connect_code_invalid: "This Connect code is unknown, already used or expired. Get a new one in Settings \u2192 Runners.",
|
|
13026
|
+
rate_limited: "Too many Connect attempts from this address. Wait ten minutes and try again.",
|
|
13027
|
+
unauthorized: "The member who created this code is no longer in the Team."
|
|
13028
|
+
};
|
|
13029
|
+
var requestRefusals = {
|
|
13030
|
+
authorization_expired: "The code expired before anyone approved it. Run drisp runner connect again for a new one.",
|
|
13031
|
+
access_denied: "The request to connect this machine was denied.",
|
|
13032
|
+
rate_limited: "Too many Connect requests from this address. Wait a few minutes and try again.",
|
|
13033
|
+
unauthorized: "The member who approved this machine is no longer in the Team."
|
|
13034
|
+
};
|
|
13035
|
+
var thisMachine = (installationId) => ({
|
|
13036
|
+
installationId,
|
|
13037
|
+
hostname: hostname().slice(0, 255) || "machine",
|
|
13038
|
+
os: `${platform()} ${release()}`.slice(0, 64),
|
|
13039
|
+
softwareVersion: runnerSoftwareVersion
|
|
13040
|
+
});
|
|
13041
|
+
var post = (origin, path7, body) => fetch(new URL(path7, origin), {
|
|
13042
|
+
method: "POST",
|
|
13043
|
+
headers: { "Content-Type": "application/json" },
|
|
13044
|
+
body: JSON.stringify(body)
|
|
13045
|
+
});
|
|
13046
|
+
async function connectTarget(origin, configPath, directory) {
|
|
13047
|
+
const single = configPath === void 0 ? void 0 : expandHome(configPath);
|
|
13048
|
+
if (single !== void 0) await refuseExisting(single);
|
|
13049
|
+
const presented = single === void 0 ? await machineInstallationId(directory) : crypto.randomUUID();
|
|
13050
|
+
const held = single === void 0 ? await runnerIdentities(directory) : [];
|
|
13051
|
+
const forTeam = (orgId) => held.filter(({ config }) => config.orgId === orgId && identityHubOrigin(config) === origin.origin);
|
|
13052
|
+
const ownIdentity = ({ orgId, runnerId }) => forTeam(orgId).find(({ config }) => config.runnerId === runnerId);
|
|
13053
|
+
return { single, presented, forTeam, ownIdentity };
|
|
13054
|
+
}
|
|
13055
|
+
async function alreadyConnected(existing, { forTeam, ownIdentity }) {
|
|
13056
|
+
const own = ownIdentity(existing);
|
|
13057
|
+
const problem = own ? await credentialAccepted(own.config) ? void 0 : "lapsed" : forTeam(existing.orgId).length ? "other_runner" : "missing";
|
|
13058
|
+
const runner = { orgId: existing.orgId, runnerId: existing.runnerId, name: existing.name };
|
|
13059
|
+
if (own && problem === void 0) return { status: "already_connected", ...runner, path: own.path };
|
|
13060
|
+
const path7 = (own ?? forTeam(existing.orgId)[0])?.path;
|
|
13061
|
+
return { status: "identity_unusable", ...runner, problem, ...path7 ? { path: path7 } : {} };
|
|
13062
|
+
}
|
|
13063
|
+
async function connectWithCode({
|
|
13064
|
+
code,
|
|
13065
|
+
hub,
|
|
13066
|
+
replace = false,
|
|
13067
|
+
configPath = overriddenIdentityPath(),
|
|
13068
|
+
directory = runnerConfigDirectory()
|
|
13069
|
+
}) {
|
|
13070
|
+
const origin = hubOrigin(hub);
|
|
13071
|
+
const target = await connectTarget(origin, configPath, directory);
|
|
13072
|
+
const { presented, forTeam, ownIdentity } = target;
|
|
13073
|
+
let redemption = await redeem(origin, code, presented);
|
|
13074
|
+
if (redemption.status === "already_connected" && !ownIdentity(redemption.existing)) {
|
|
13075
|
+
const replacedEarlier = forTeam(redemption.existing.orgId).find(
|
|
13076
|
+
({ config }) => config.installationId !== presented
|
|
13077
|
+
);
|
|
13078
|
+
if (replacedEarlier) redemption = await redeem(origin, code, replacedEarlier.config.installationId);
|
|
13079
|
+
}
|
|
13080
|
+
if (redemption.status === "already_connected") {
|
|
13081
|
+
const outcome = await alreadyConnected(redemption.existing, target);
|
|
13082
|
+
if (outcome.status === "already_connected" || !replace) return outcome;
|
|
13083
|
+
redemption = await redeem(origin, code, crypto.randomUUID());
|
|
13084
|
+
if (redemption.status === "already_connected")
|
|
13085
|
+
throw new Error("The Hub refused a new installation identity as already connected; try again.");
|
|
13086
|
+
}
|
|
13087
|
+
return saveIdentity(origin, redemption.credential, target, directory);
|
|
13088
|
+
}
|
|
13089
|
+
async function connectByApproval({
|
|
13090
|
+
hub,
|
|
13091
|
+
onCode,
|
|
13092
|
+
signal,
|
|
13093
|
+
configPath = overriddenIdentityPath(),
|
|
13094
|
+
directory = runnerConfigDirectory()
|
|
13095
|
+
}) {
|
|
13096
|
+
const origin = hubOrigin(hub);
|
|
13097
|
+
const target = await connectTarget(origin, configPath, directory);
|
|
13098
|
+
const refused = (body, status) => new Error(requestRefusals[body.error ?? ""] ?? `The Hub refused to connect this machine (${body.error ?? status}).`);
|
|
13099
|
+
const started = await post(origin, "/v2/runners/connect/start", thisMachine(target.presented));
|
|
13100
|
+
const startBody = await started.json().catch(() => ({}));
|
|
13101
|
+
if (!started.ok) throw refused(startBody, started.status);
|
|
13102
|
+
const { deviceCode, code, verificationUrl, expiresAt, intervalSec } = connectStartResponseSchema.parse(startBody);
|
|
13103
|
+
onCode({ code, verificationUrl, expiresAt });
|
|
13104
|
+
for (; ; ) {
|
|
13105
|
+
await sleep(intervalSec * 1e3, void 0, { signal });
|
|
13106
|
+
const polled = await post(origin, "/v2/runners/connect/poll", { deviceCode }).catch(() => void 0);
|
|
13107
|
+
const body = await polled?.json().catch(() => ({})) ?? {};
|
|
13108
|
+
if (polled?.ok) return saveIdentity(origin, connectRedeemResponseSchema.parse(body), target, directory);
|
|
13109
|
+
if (polled?.status === 409 && body.error === "runner_already_connected")
|
|
13110
|
+
return alreadyConnected(connectAlreadyConnectedSchema.parse(body), target);
|
|
13111
|
+
if (polled && polled.status < 500 && body.error !== "authorization_pending") throw refused(body, polled.status);
|
|
13112
|
+
if (Date.now() >= expiresAt && body.error !== "authorization_pending")
|
|
13113
|
+
throw refused({ error: "authorization_expired" }, 409);
|
|
13114
|
+
}
|
|
13115
|
+
}
|
|
13116
|
+
async function saveIdentity(origin, credential, { single, forTeam }, directory) {
|
|
13117
|
+
const endpoint = new URL(`/v2/orgs/${encodeURIComponent(credential.orgId)}/connect`, origin);
|
|
13118
|
+
endpoint.protocol = endpoint.protocol === "https:" ? "wss:" : "ws:";
|
|
13119
|
+
const config = runnerConfigSchema.parse({
|
|
13120
|
+
endpoint: endpoint.href,
|
|
13121
|
+
token: credential.token,
|
|
13122
|
+
orgId: credential.orgId,
|
|
13123
|
+
runnerId: credential.runnerId,
|
|
13124
|
+
installationId: credential.installationId,
|
|
13125
|
+
hubEpoch: credential.hubEpoch,
|
|
13126
|
+
capacity: 1,
|
|
13127
|
+
// Each Runner keeps its own store: the store pins its Runner and Hub epoch, and one installation identity can
|
|
13128
|
+
// be several Runners (one per Team, and a new one when the Hub enrols this machine again).
|
|
13129
|
+
dataDirectory: `~/.local/share/drisp/${pathSegment(credential.installationId)}/${pathSegment(credential.runnerId)}`,
|
|
13130
|
+
name: credential.name
|
|
13131
|
+
});
|
|
13132
|
+
const replaced = [];
|
|
13133
|
+
for (const { path: path8 } of forTeam(credential.orgId)) {
|
|
13134
|
+
const aside = `${path8}.replaced-${Date.now()}`;
|
|
13135
|
+
await rename(path8, aside);
|
|
13136
|
+
replaced.push(aside);
|
|
13157
13137
|
}
|
|
13138
|
+
const path7 = single ?? teamIdentityPath(origin, credential.orgId, directory);
|
|
13139
|
+
try {
|
|
13140
|
+
await writeIdentityOnce(path7, JSON.stringify(config, null, 2) + "\n");
|
|
13141
|
+
} catch (error) {
|
|
13142
|
+
if (error.code === "EEXIST")
|
|
13143
|
+
throw new Error(`Another Connect wrote a Runner identity for this Team at ${path7} meanwhile; it was left unchanged.`);
|
|
13144
|
+
throw error;
|
|
13145
|
+
}
|
|
13146
|
+
return { status: "connected", config, path: path7, replaced };
|
|
13147
|
+
}
|
|
13148
|
+
function pathSegment(value) {
|
|
13149
|
+
const segment = encodeURIComponent(value);
|
|
13150
|
+
if (segment === "." || segment === "..") throw new Error(`The Hub returned an unusable identifier (${value}).`);
|
|
13151
|
+
return segment;
|
|
13152
|
+
}
|
|
13153
|
+
async function redeem(origin, code, installationId) {
|
|
13154
|
+
const response = await post(origin, "/v2/runners/connect", { code, ...thisMachine(installationId) });
|
|
13155
|
+
const body = await response.json().catch(() => ({}));
|
|
13156
|
+
if (response.status === 409 && body.error === "runner_already_connected")
|
|
13157
|
+
return { status: "already_connected", existing: connectAlreadyConnectedSchema.parse(body) };
|
|
13158
|
+
if (!response.ok)
|
|
13159
|
+
throw new Error(refusals[body.error ?? ""] ?? `The Hub refused the Connect code (${body.error ?? response.status}).`);
|
|
13160
|
+
return { status: "connected", credential: connectRedeemResponseSchema.parse(body) };
|
|
13161
|
+
}
|
|
13162
|
+
async function credentialAccepted(config) {
|
|
13163
|
+
const url = hubHttpUrl(config.endpoint, "/credentials/renew");
|
|
13164
|
+
const response = await fetch(url, {
|
|
13165
|
+
method: "POST",
|
|
13166
|
+
headers: { Authorization: `Bearer ${config.token}`, "Content-Type": "application/json" },
|
|
13167
|
+
body: JSON.stringify({ hubEpoch: config.hubEpoch }),
|
|
13168
|
+
signal: AbortSignal.timeout(1e4)
|
|
13169
|
+
});
|
|
13170
|
+
if (response.ok) return true;
|
|
13171
|
+
const { error } = await response.json().catch(() => ({}));
|
|
13172
|
+
if (response.status === 401 || error === "recovery_required") return false;
|
|
13173
|
+
throw new Error(`The Hub could not check this machine's credential for the Team (${error ?? response.status}).`);
|
|
13174
|
+
}
|
|
13175
|
+
async function refuseExisting(target) {
|
|
13176
|
+
const existing = await readFile(target, "utf8").then((text) => text, () => void 0);
|
|
13177
|
+
if (existing === void 0) return;
|
|
13178
|
+
let name;
|
|
13158
13179
|
try {
|
|
13159
|
-
|
|
13160
|
-
cachedVersion = pkg.version ?? "0.0.0";
|
|
13180
|
+
name = runnerConfigSchema.safeParse(JSON.parse(existing)).data?.name;
|
|
13161
13181
|
} catch {
|
|
13162
|
-
cachedVersion = "0.0.0";
|
|
13163
13182
|
}
|
|
13164
|
-
|
|
13183
|
+
throw new Error(`This machine already has a Runner${name ? ` (${name})` : ""} configured at ${target}.`);
|
|
13184
|
+
}
|
|
13185
|
+
async function disconnectRunner({
|
|
13186
|
+
identity: { path: path7, config },
|
|
13187
|
+
serviceManager,
|
|
13188
|
+
othersRemain
|
|
13189
|
+
}) {
|
|
13190
|
+
const name = config.name ?? config.runnerId;
|
|
13191
|
+
const wasRunning = serviceManager ? (await serviceManager.status()).running : false;
|
|
13192
|
+
const restore = async () => {
|
|
13193
|
+
if (wasRunning) await serviceManager?.start();
|
|
13194
|
+
};
|
|
13195
|
+
await serviceManager?.stop();
|
|
13196
|
+
const url = hubHttpUrl(config.endpoint, "/runner/disconnect");
|
|
13197
|
+
let response;
|
|
13198
|
+
try {
|
|
13199
|
+
response = await fetch(url, {
|
|
13200
|
+
method: "POST",
|
|
13201
|
+
headers: { Authorization: `Bearer ${config.token}`, "Content-Type": "application/json" },
|
|
13202
|
+
body: JSON.stringify({ hubEpoch: config.hubEpoch }),
|
|
13203
|
+
signal: AbortSignal.timeout(15e3)
|
|
13204
|
+
});
|
|
13205
|
+
} catch (error) {
|
|
13206
|
+
await restore();
|
|
13207
|
+
throw new Error(`The Hub could not be reached, so ${name} is still connected: ${error instanceof Error ? error.message : String(error)}`);
|
|
13208
|
+
}
|
|
13209
|
+
const body = await response.json().catch(() => ({}));
|
|
13210
|
+
let result;
|
|
13211
|
+
if (response.ok) result = { name, stoppedRuns: runnerDisconnectResponseSchema.parse(body).stoppedRuns };
|
|
13212
|
+
else if (response.status === 410) result = { name, note: "The Hub had already disconnected it." };
|
|
13213
|
+
else if (response.status === 401)
|
|
13214
|
+
result = { name, note: "The Hub no longer accepts its credential: it lapsed after 30 days without contact, or was revoked." };
|
|
13215
|
+
else {
|
|
13216
|
+
await restore();
|
|
13217
|
+
throw new Error(`The Hub refused to disconnect ${name} (${body.error ?? response.status}); it is still connected.`);
|
|
13218
|
+
}
|
|
13219
|
+
await forgetRunner(path7);
|
|
13220
|
+
if (othersRemain) await restore();
|
|
13221
|
+
else await serviceManager?.remove();
|
|
13222
|
+
return result;
|
|
13165
13223
|
}
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13224
|
+
|
|
13225
|
+
// src/app/entry/runnerCommandV2.ts
|
|
13226
|
+
var usage = `Usage: drisp runner [connect [<code>] --hub <url> [--foreground] [--replace] | disconnect [<team>] | status | recover | help]
|
|
13227
|
+
|
|
13228
|
+
Connect this machine from Settings \u2192 Runners in the web console, which shows the
|
|
13229
|
+
command to paste here:
|
|
13230
|
+
drisp runner connect K7QF-M2PX --hub https://hub.example
|
|
13231
|
+
Or start from this machine: without a code, connect prints one to approve in the
|
|
13232
|
+
web console, where you choose the Team, and waits for the approval:
|
|
13233
|
+
drisp runner connect --hub https://hub.example
|
|
13234
|
+
|
|
13235
|
+
A machine is one Runner per Team. Connect writes that Team's Runner identity and
|
|
13236
|
+
credential to ~/.config/drisp/runners/<hub>/<team>.json, readable only by you, then
|
|
13237
|
+
installs and starts a background service (launchd on macOS, systemd --user on Linux)
|
|
13238
|
+
that runs this machine's Runner for every Team it serves, at login. Connecting again to
|
|
13239
|
+
the same Team reports the Runner the machine already is and changes nothing. When this
|
|
13240
|
+
machine can no longer serve that Runner (its credential is gone or has lapsed), Connect
|
|
13241
|
+
says so without using the code, and --replace connects a new Runner in its place.
|
|
13242
|
+
drisp runner status lists the Teams and the service's state. --foreground skips the
|
|
13243
|
+
service and runs the Runner in this terminal, for containers and CI; drisp runner runs
|
|
13244
|
+
every Team's Runner there again later. DRISP_HUB_URL may stand in for --hub, and
|
|
13245
|
+
DRISP_RUNNER_CONFIG selects one identity file of a separate installation instead, which
|
|
13246
|
+
gets its own service.
|
|
13247
|
+
drisp runner disconnect takes this machine out of a Team: the Hub stops the Runs on
|
|
13248
|
+
its Runner there, and this machine deletes that Team's credential, removing the
|
|
13249
|
+
service when no Team is left. Name the Team (its id or Runner name from status) when
|
|
13250
|
+
the machine serves several. A Runner disconnected from Settings does the same by
|
|
13251
|
+
itself the next time it reaches the Hub.
|
|
13252
|
+
For an uncertain crash, stop the service and every harness first, then:
|
|
13253
|
+
drisp runner recover <run-id> confirmed-stopped
|
|
13254
|
+
This records an explicit failed outcome; it never retries execution.
|
|
13255
|
+
`;
|
|
13256
|
+
function printApprovalRequest({
|
|
13257
|
+
code,
|
|
13258
|
+
verificationUrl,
|
|
13259
|
+
expiresAt
|
|
13260
|
+
}) {
|
|
13261
|
+
const minutes = Math.max(1, Math.round((expiresAt - Date.now()) / 6e4));
|
|
13262
|
+
process.stdout.write(
|
|
13263
|
+
`To connect this machine, approve code ${code.slice(0, 4)}-${code.slice(4)} in the web console:
|
|
13264
|
+
${verificationUrl}
|
|
13265
|
+
The code works once and expires in ${minutes} minutes. Waiting for approval\u2026
|
|
13266
|
+
`
|
|
13267
|
+
);
|
|
13174
13268
|
}
|
|
13175
|
-
|
|
13269
|
+
var serviceStarted = (service) => `The Runner runs as a background service (${service.kind} ${service.name}) and starts again at login.`;
|
|
13270
|
+
var servicesFor = (options) => options.services === void 0 ? platformRunnerServices() : options.services;
|
|
13271
|
+
async function serviceInstallation(config) {
|
|
13272
|
+
const single = overriddenIdentityPath();
|
|
13176
13273
|
return {
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
platform: os5.platform(),
|
|
13180
|
-
arch: os5.arch(),
|
|
13181
|
-
name: name ?? os5.hostname()
|
|
13274
|
+
installationId: await serviceInstallationId(config),
|
|
13275
|
+
...single ? { configPath: single } : {}
|
|
13182
13276
|
};
|
|
13183
13277
|
}
|
|
13184
|
-
async function
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13278
|
+
async function ensureService(services, config, identityPath) {
|
|
13279
|
+
let service;
|
|
13280
|
+
let installed = false;
|
|
13281
|
+
try {
|
|
13282
|
+
const { installationId, configPath } = await serviceInstallation(config);
|
|
13283
|
+
service = services(installationId);
|
|
13284
|
+
await service.install(runnerServiceUnit(configPath));
|
|
13285
|
+
installed = true;
|
|
13286
|
+
if ((await service.status()).running) await service.stop();
|
|
13287
|
+
await service.start();
|
|
13288
|
+
return service;
|
|
13289
|
+
} catch (error) {
|
|
13290
|
+
throw new Error(
|
|
13291
|
+
[
|
|
13292
|
+
`Connected, but the background service did not start: ${error instanceof Error ? error.message : String(error)}`,
|
|
13293
|
+
`The identity is saved to ${identityPath}: run drisp runner to run the Runner in the foreground.`,
|
|
13294
|
+
// launchd loads an installed LaunchAgent at the next login and RunAtLoad starts it. A systemd unit
|
|
13295
|
+
// is enabled only by the start that just failed, so it has no such fallback.
|
|
13296
|
+
...installed && service?.kind === "launchd" ? [
|
|
13297
|
+
`The LaunchAgent ${service.name} is installed, so launchd starts the Runner at your next login.`
|
|
13298
|
+
] : []
|
|
13299
|
+
].join("\n")
|
|
13300
|
+
);
|
|
13202
13301
|
}
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
}
|
|
13210
|
-
if (subcommandArgs.length > 1) {
|
|
13211
|
-
logError(`runner pair: unexpected argument ${subcommandArgs[1]}`);
|
|
13212
|
-
return 2;
|
|
13213
|
-
}
|
|
13214
|
-
if (!flags.url) {
|
|
13215
|
-
logError("runner pair: --url <dashboard-origin> is required");
|
|
13216
|
-
return 2;
|
|
13217
|
-
}
|
|
13218
|
-
let origin;
|
|
13219
|
-
try {
|
|
13220
|
-
origin = normalizeDashboardUrl(flags.url);
|
|
13221
|
-
} catch (err) {
|
|
13222
|
-
logError(
|
|
13223
|
-
`runner pair: ${err instanceof Error ? err.message : String(err)}`
|
|
13224
|
-
);
|
|
13225
|
-
return 2;
|
|
13226
|
-
}
|
|
13227
|
-
const fp = fingerprint();
|
|
13228
|
-
const body = {
|
|
13229
|
-
token,
|
|
13230
|
-
fingerprint: fp,
|
|
13231
|
-
hostInfo: (deps.hostInfo ?? (() => defaultHostInfo(flags.name)))(),
|
|
13232
|
-
capabilities: {
|
|
13233
|
-
instanceSocket: true,
|
|
13234
|
-
runtimeDaemon: true,
|
|
13235
|
-
cliVersion: packageVersion,
|
|
13236
|
-
// Legacy field — older dashboards read `version`. Drop in a
|
|
13237
|
-
// follow-up release once the dashboard accepts only `cliVersion`.
|
|
13238
|
-
version: packageVersion
|
|
13239
|
-
}
|
|
13240
|
-
};
|
|
13241
|
-
let response;
|
|
13242
|
-
try {
|
|
13243
|
-
response = await fetchImpl(`${origin}/api/instances/pair`, {
|
|
13244
|
-
method: "POST",
|
|
13245
|
-
headers: { "content-type": "application/json" },
|
|
13246
|
-
body: JSON.stringify(body)
|
|
13247
|
-
});
|
|
13248
|
-
} catch (err) {
|
|
13249
|
-
logError(
|
|
13250
|
-
`runner pair: failed to reach ${origin}: ${err instanceof Error ? err.message : String(err)}`
|
|
13251
|
-
);
|
|
13252
|
-
return 1;
|
|
13253
|
-
}
|
|
13254
|
-
if (!response.ok) {
|
|
13255
|
-
const message = await safeReadError(response);
|
|
13256
|
-
logError(
|
|
13257
|
-
`runner pair: ${origin} returned ${response.status}${message ? ` \u2014 ${message}` : ""}`
|
|
13258
|
-
);
|
|
13259
|
-
return 1;
|
|
13260
|
-
}
|
|
13261
|
-
let parsed;
|
|
13262
|
-
try {
|
|
13263
|
-
parsed = parsePairResponse(await response.json());
|
|
13264
|
-
} catch (err) {
|
|
13265
|
-
logError(
|
|
13266
|
-
`runner pair: invalid response from ${origin}: ${err instanceof Error ? err.message : String(err)}`
|
|
13267
|
-
);
|
|
13268
|
-
return 1;
|
|
13269
|
-
}
|
|
13270
|
-
if (parsed.requiredCliVersion && compareSemver(packageVersion, parsed.requiredCliVersion) < 0) {
|
|
13271
|
-
logError(
|
|
13272
|
-
`runner pair: cli version ${packageVersion} is older than the dashboard's required >=${parsed.requiredCliVersion}.`
|
|
13273
|
-
);
|
|
13274
|
-
logError(
|
|
13275
|
-
"runner pair: upgrade with `npm i -g @drisp/cli` then re-run pair."
|
|
13276
|
-
);
|
|
13277
|
-
return 1;
|
|
13278
|
-
}
|
|
13279
|
-
const config = {
|
|
13280
|
-
dashboardUrl: origin,
|
|
13281
|
-
instanceId: parsed.instanceId,
|
|
13282
|
-
refreshToken: parsed.refreshToken,
|
|
13283
|
-
fingerprint: fp,
|
|
13284
|
-
pairedAt: now()
|
|
13285
|
-
};
|
|
13286
|
-
writeConfig(config);
|
|
13287
|
-
try {
|
|
13288
|
-
writeMirror({
|
|
13289
|
-
instanceId: parsed.instanceId,
|
|
13290
|
-
fetchedAt: now(),
|
|
13291
|
-
attachments: (parsed.runners ?? []).map((r) => ({
|
|
13292
|
-
runnerId: r.runnerId,
|
|
13293
|
-
...r.name !== void 0 ? { name: r.name } : {},
|
|
13294
|
-
...r.executionTarget !== void 0 ? { executionTarget: r.executionTarget } : {},
|
|
13295
|
-
...r.remoteInstanceId !== void 0 ? { remoteInstanceId: r.remoteInstanceId } : {}
|
|
13296
|
-
}))
|
|
13297
|
-
});
|
|
13298
|
-
} catch (err) {
|
|
13299
|
-
logError(
|
|
13300
|
-
`runner pair: failed to write attachment mirror: ${err instanceof Error ? err.message : String(err)}`
|
|
13301
|
-
);
|
|
13302
|
-
}
|
|
13303
|
-
const priorStop = await (deps.stopRunner ?? defaultStopRunner)({});
|
|
13304
|
-
const started = await (deps.startDetachedRunner ?? defaultStartDetachedRunner)({
|
|
13305
|
-
log: (msg) => logOut(msg)
|
|
13306
|
-
});
|
|
13307
|
-
const daemonStart = {
|
|
13308
|
-
...started,
|
|
13309
|
-
...priorStop.wasRunning ? { restarted: true } : {}
|
|
13310
|
-
};
|
|
13311
|
-
if (flags.json) {
|
|
13312
|
-
logOut(
|
|
13313
|
-
JSON.stringify({
|
|
13314
|
-
ok: true,
|
|
13315
|
-
instanceId: parsed.instanceId,
|
|
13316
|
-
dashboardUrl: origin,
|
|
13317
|
-
configPath: configPath(),
|
|
13318
|
-
runner: daemonStart,
|
|
13319
|
-
...parsed.runners ? { runners: parsed.runners } : {},
|
|
13320
|
-
...parsed.capabilityAck ? { capabilityAck: parsed.capabilityAck } : {},
|
|
13321
|
-
...parsed.requiredCliVersion ? { requiredCliVersion: parsed.requiredCliVersion } : {}
|
|
13322
|
-
})
|
|
13302
|
+
}
|
|
13303
|
+
async function runRunnerCommandV2(input, options = {}) {
|
|
13304
|
+
try {
|
|
13305
|
+
if (input.flags.detach)
|
|
13306
|
+
throw new Error(
|
|
13307
|
+
"v1 --detach is unavailable: drisp runner connect installs the Runner as a background service."
|
|
13323
13308
|
);
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13309
|
+
switch (input.subcommand) {
|
|
13310
|
+
case "connect": {
|
|
13311
|
+
const [code, extra] = input.subcommandArgs;
|
|
13312
|
+
if (extra) throw new Error(`Unexpected argument ${extra}.`);
|
|
13313
|
+
const hub = input.flags.hub ?? process.env["DRISP_HUB_URL"];
|
|
13314
|
+
if (!hub)
|
|
13315
|
+
throw new Error(
|
|
13316
|
+
"Pass --hub <url>, the Hub shown with the code in Settings \u2192 Runners."
|
|
13317
|
+
);
|
|
13318
|
+
const services = input.flags.foreground ? null : servicesFor(options);
|
|
13319
|
+
const outcome = code ? await connectWithCode({
|
|
13320
|
+
code,
|
|
13321
|
+
hub,
|
|
13322
|
+
replace: Boolean(input.flags.replace)
|
|
13323
|
+
}) : await connectByApproval({
|
|
13324
|
+
hub,
|
|
13325
|
+
onCode: printApprovalRequest,
|
|
13326
|
+
signal: options.signal
|
|
13327
|
+
});
|
|
13328
|
+
if (outcome.status === "already_connected") {
|
|
13329
|
+
process.stdout.write(
|
|
13330
|
+
`This machine is already connected to this Team as ${outcome.name}. Its identity in ${outcome.path} is unchanged; start it with drisp runner.
|
|
13331
|
+
`
|
|
13330
13332
|
);
|
|
13333
|
+
return 0;
|
|
13331
13334
|
}
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
"runner pair: pairing succeeded; tail logs with `drisp runner logs --follow`."
|
|
13335
|
+
if (outcome.status === "identity_unusable") {
|
|
13336
|
+
const why = {
|
|
13337
|
+
missing: "it no longer holds that Runner's credential",
|
|
13338
|
+
other_runner: `its identity in ${outcome.path} belongs to another Runner`,
|
|
13339
|
+
lapsed: `the Hub no longer accepts the credential in ${outcome.path} (it expired or was revoked)`
|
|
13340
|
+
}[outcome.problem];
|
|
13341
|
+
const retry = [
|
|
13342
|
+
"drisp runner connect",
|
|
13343
|
+
code ?? "<code>",
|
|
13344
|
+
"--hub",
|
|
13345
|
+
hub,
|
|
13346
|
+
...input.flags.foreground ? ["--foreground"] : [],
|
|
13347
|
+
"--replace"
|
|
13348
|
+
].join(" ");
|
|
13349
|
+
throw new Error(
|
|
13350
|
+
`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.`
|
|
13349
13351
|
);
|
|
13350
13352
|
}
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13353
|
+
const { config, path: path7, replaced } = outcome;
|
|
13354
|
+
for (const aside of replaced)
|
|
13355
|
+
process.stdout.write(
|
|
13356
|
+
`The Hub no longer recognised this machine's earlier identity for this Team; it was moved aside to ${aside}.
|
|
13357
|
+
`
|
|
13358
|
+
);
|
|
13359
|
+
process.stdout.write(
|
|
13360
|
+
`Connected as ${config.name ?? config.runnerId}. Identity saved to ${path7}.
|
|
13361
|
+
`
|
|
13357
13362
|
);
|
|
13363
|
+
if (services) {
|
|
13364
|
+
const service = await ensureService(services, config, path7);
|
|
13365
|
+
process.stdout.write(`${serviceStarted(service)}
|
|
13366
|
+
`);
|
|
13367
|
+
return 0;
|
|
13368
|
+
}
|
|
13369
|
+
if (!input.flags.foreground)
|
|
13370
|
+
process.stdout.write(
|
|
13371
|
+
"No launchd or systemd here to run the Runner as a background service; running it in the foreground instead.\n"
|
|
13372
|
+
);
|
|
13373
|
+
await runConfiguredRunner(path7, options.signal);
|
|
13374
|
+
return 0;
|
|
13358
13375
|
}
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
report = {
|
|
13383
|
-
running: false,
|
|
13384
|
-
error: err instanceof Error ? err.message : String(err)
|
|
13385
|
-
};
|
|
13386
|
-
}
|
|
13387
|
-
let runnerHealth;
|
|
13388
|
-
if (flags.runner) {
|
|
13389
|
-
const refreshResult = await tryRefresh("refresh");
|
|
13390
|
-
if (refreshResult.ok) {
|
|
13391
|
-
runnerHealth = await fetchRunnerHealth(
|
|
13392
|
-
fetchImpl,
|
|
13393
|
-
config.dashboardUrl,
|
|
13394
|
-
flags.runner,
|
|
13395
|
-
refreshResult.token
|
|
13376
|
+
case "disconnect": {
|
|
13377
|
+
const [team, extra] = input.subcommandArgs;
|
|
13378
|
+
if (extra) throw new Error(`Unexpected argument ${extra}.`);
|
|
13379
|
+
const identities = await runnerIdentities();
|
|
13380
|
+
if (!identities.length)
|
|
13381
|
+
throw new Error("This machine has no Runner to disconnect.");
|
|
13382
|
+
const matching = team ? identities.filter(
|
|
13383
|
+
({ config }) => config.orgId === team || config.name === team
|
|
13384
|
+
) : identities;
|
|
13385
|
+
if (matching.length !== 1)
|
|
13386
|
+
throw new Error(
|
|
13387
|
+
(team ? `No single Runner on this machine matches ${team}.` : "This machine serves several Teams: name the one to leave.") + ` Its Teams: ${identities.map(({ config }) => `${config.orgId} (${config.name ?? config.runnerId})`).join(", ")}.`
|
|
13388
|
+
);
|
|
13389
|
+
const identity = matching[0];
|
|
13390
|
+
const services = servicesFor(options);
|
|
13391
|
+
const { name, stoppedRuns, note } = await disconnectRunner({
|
|
13392
|
+
identity,
|
|
13393
|
+
serviceManager: services ? services(await serviceInstallationId(identity.config)) : null,
|
|
13394
|
+
othersRemain: identities.length > 1
|
|
13395
|
+
});
|
|
13396
|
+
process.stdout.write(
|
|
13397
|
+
`Disconnected ${name}.${stoppedRuns ? ` ${stoppedRuns === 1 ? "1 Run on it was" : `${stoppedRuns} Runs on it were`} stopped.` : ""}${note ? ` ${note}` : ""} Its credential was deleted${identities.length > 1 ? "; the service runs on for the other Teams" : " and its service removed"}.
|
|
13398
|
+
`
|
|
13396
13399
|
);
|
|
13397
|
-
|
|
13398
|
-
runnerHealth = {
|
|
13399
|
-
id: flags.runner,
|
|
13400
|
-
matches: false,
|
|
13401
|
-
error: "could not refresh access token"
|
|
13402
|
-
};
|
|
13400
|
+
return 0;
|
|
13403
13401
|
}
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
logOut(
|
|
13410
|
-
JSON.stringify({
|
|
13411
|
-
ok,
|
|
13412
|
-
paired: true,
|
|
13413
|
-
instanceId: config.instanceId,
|
|
13414
|
-
dashboardUrl: config.dashboardUrl,
|
|
13415
|
-
pairedAt: config.pairedAt,
|
|
13416
|
-
...config.lastRefreshAt !== void 0 ? { lastRefreshAt: config.lastRefreshAt } : {},
|
|
13417
|
-
configPath: configPath(),
|
|
13418
|
-
runner: report.running ? {
|
|
13419
|
-
running: true,
|
|
13420
|
-
pid: report.status.pid,
|
|
13421
|
-
startedAt: report.status.startedAt,
|
|
13422
|
-
updatedAt: report.status.updatedAt,
|
|
13423
|
-
socketConnected: report.status.socketConnected,
|
|
13424
|
-
...report.status.wireMode !== void 0 ? { wireMode: report.status.wireMode } : {},
|
|
13425
|
-
...report.status.lastFrameAt !== void 0 ? { lastFrameAt: report.status.lastFrameAt } : {},
|
|
13426
|
-
activeRuns: report.status.activeRuns,
|
|
13427
|
-
completedRuns: report.status.completedRuns,
|
|
13428
|
-
...report.status.refreshState ? { refreshState: report.status.refreshState } : {}
|
|
13429
|
-
} : {
|
|
13430
|
-
running: false,
|
|
13431
|
-
...report.error ? { error: report.error } : {}
|
|
13432
|
-
},
|
|
13433
|
-
...runnerHealth ? { binding: runnerHealth } : {}
|
|
13434
|
-
})
|
|
13435
|
-
);
|
|
13436
|
-
} else {
|
|
13437
|
-
logOut(
|
|
13438
|
-
`runner: paired to ${config.dashboardUrl} as ${config.instanceId}`
|
|
13439
|
-
);
|
|
13440
|
-
if (runnerHealth) {
|
|
13441
|
-
if (runnerHealth.matches) {
|
|
13442
|
-
logOut(
|
|
13443
|
-
`binding: ${runnerHealth.id} bound to this instance (executionTarget=remote)`
|
|
13444
|
-
);
|
|
13445
|
-
} else {
|
|
13446
|
-
logError(
|
|
13447
|
-
`binding: ${runnerHealth.id} ${runnerHealth.error ?? "not bound"}`
|
|
13402
|
+
case "recover": {
|
|
13403
|
+
const [runId, confirmation] = input.subcommandArgs;
|
|
13404
|
+
if (!runId || confirmation !== "confirmed-stopped")
|
|
13405
|
+
throw new Error(
|
|
13406
|
+
"Stop the service and verify every harness is stopped, then use recover <run-id> confirmed-stopped."
|
|
13448
13407
|
);
|
|
13449
|
-
|
|
13450
|
-
|
|
13451
|
-
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
);
|
|
13460
|
-
logOut(
|
|
13461
|
-
`socket: ${r.socketConnected ? "connected" : "disconnected"}${r.wireMode ? ` (${r.wireMode} frame names)` : ""}${r.lastFrameAt !== void 0 ? ` (last frame ${formatDuration2(
|
|
13462
|
-
Math.max(0, Math.floor((now() - r.lastFrameAt) / 1e3))
|
|
13463
|
-
)} ago)` : ""}`
|
|
13408
|
+
const config = await recoveryIdentity(runId);
|
|
13409
|
+
const store = new ExecutionStore(
|
|
13410
|
+
join2(config.dataDirectory, "execution.sqlite"),
|
|
13411
|
+
{
|
|
13412
|
+
orgId: config.orgId,
|
|
13413
|
+
runnerId: config.runnerId,
|
|
13414
|
+
installationId: config.installationId,
|
|
13415
|
+
hubEpoch: config.hubEpoch
|
|
13416
|
+
},
|
|
13417
|
+
config.capacity
|
|
13464
13418
|
);
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
logError(
|
|
13472
|
-
`refresh: circuit-broken \u2014 hub unreachable; probing it and reconnecting as soon as it answers (retries anyway in ${formatDuration2(
|
|
13473
|
-
remainingSec
|
|
13474
|
-
)})`
|
|
13475
|
-
);
|
|
13476
|
-
} else {
|
|
13477
|
-
logError(
|
|
13478
|
-
`refresh: circuit-broken \u2014 sleeping for ${formatDuration2(
|
|
13479
|
-
remainingSec
|
|
13480
|
-
)} before retry. Re-pair if this persists.`
|
|
13481
|
-
);
|
|
13482
|
-
}
|
|
13483
|
-
} else {
|
|
13484
|
-
logOut(
|
|
13485
|
-
`refresh: ${r.refreshState.recentFailures} recent failure(s); next reconnect will retry`
|
|
13486
|
-
);
|
|
13487
|
-
}
|
|
13419
|
+
try {
|
|
13420
|
+
store.resolveRecovery(runId);
|
|
13421
|
+
if (config.portableCheckpoints)
|
|
13422
|
+
store.queueCheckpoint(runId, crypto.randomUUID());
|
|
13423
|
+
} finally {
|
|
13424
|
+
store.close();
|
|
13488
13425
|
}
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
`runner: NOT running${report.error ? ` (${report.error})` : ""}. Start it with \`drisp runner --detach\`.`
|
|
13426
|
+
process.stdout.write(
|
|
13427
|
+
"Recovery outcome recorded. Start the runner to publish it.\n"
|
|
13492
13428
|
);
|
|
13429
|
+
return 0;
|
|
13493
13430
|
}
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13431
|
+
case "status": {
|
|
13432
|
+
const identities = await runnerIdentities();
|
|
13433
|
+
if (!identities.length)
|
|
13434
|
+
throw new Error(
|
|
13435
|
+
"This machine is not connected to a Team. Settings \u2192 Runners shows the command to connect it."
|
|
13436
|
+
);
|
|
13437
|
+
const services = servicesFor(options);
|
|
13438
|
+
const service = services ? services(
|
|
13439
|
+
(await serviceInstallation(identities[0].config)).installationId
|
|
13440
|
+
) : void 0;
|
|
13441
|
+
process.stdout.write(
|
|
13442
|
+
JSON.stringify(
|
|
13443
|
+
{
|
|
13444
|
+
configured: true,
|
|
13445
|
+
// One entry per Team this machine serves; credentials are never printed.
|
|
13446
|
+
teams: identities.map(
|
|
13447
|
+
({ path: path7, config: { token: _token, ...config } }) => ({
|
|
13448
|
+
...config,
|
|
13449
|
+
path: path7
|
|
13450
|
+
})
|
|
13451
|
+
),
|
|
13452
|
+
service: service ? {
|
|
13453
|
+
manager: service.kind,
|
|
13454
|
+
name: service.name,
|
|
13455
|
+
...await service.status()
|
|
13456
|
+
} : { manager: null, installed: false, running: false },
|
|
13457
|
+
note: "Connection and execution state are visible in the Hub."
|
|
13458
|
+
},
|
|
13459
|
+
null,
|
|
13460
|
+
2
|
|
13461
|
+
) + "\n"
|
|
13499
13462
|
);
|
|
13463
|
+
return 0;
|
|
13500
13464
|
}
|
|
13465
|
+
case "":
|
|
13466
|
+
await runMachineRunners(options.signal, {
|
|
13467
|
+
services: servicesFor(options)
|
|
13468
|
+
});
|
|
13469
|
+
return 0;
|
|
13470
|
+
case "help":
|
|
13471
|
+
case "--help":
|
|
13472
|
+
process.stdout.write(usage);
|
|
13473
|
+
return 0;
|
|
13474
|
+
default:
|
|
13475
|
+
throw new Error(
|
|
13476
|
+
`Unsupported v2 runner command: ${input.subcommand}.
|
|
13477
|
+
${usage}`
|
|
13478
|
+
);
|
|
13501
13479
|
}
|
|
13502
|
-
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
}
|
|
13509
|
-
const tail = flags.tail ?? 20;
|
|
13510
|
-
const follow = flags.follow ?? false;
|
|
13511
|
-
try {
|
|
13512
|
-
const tailFn = deps.tailRunnerLog ?? defaultTailRunnerLog;
|
|
13513
|
-
return await tailFn({ tail, follow });
|
|
13514
|
-
} catch (err) {
|
|
13515
|
-
logError(
|
|
13516
|
-
`runner logs: ${err instanceof Error ? err.message : String(err)}`
|
|
13517
|
-
);
|
|
13518
|
-
return 1;
|
|
13519
|
-
}
|
|
13520
|
-
}
|
|
13521
|
-
if (subcommand === "runs") {
|
|
13522
|
-
if (subcommandArgs.length > 0) {
|
|
13523
|
-
logError(`runner runs: unexpected argument ${subcommandArgs[0]}`);
|
|
13524
|
-
return 2;
|
|
13525
|
-
}
|
|
13526
|
-
const readStatus = deps.readRunnerStatus ?? defaultReadRunnerStatus;
|
|
13527
|
-
let report;
|
|
13528
|
-
try {
|
|
13529
|
-
report = readStatus();
|
|
13530
|
-
} catch (err) {
|
|
13531
|
-
logError(
|
|
13532
|
-
`runner runs: ${err instanceof Error ? err.message : String(err)}`
|
|
13533
|
-
);
|
|
13534
|
-
return 1;
|
|
13535
|
-
}
|
|
13536
|
-
if (!report.running) {
|
|
13537
|
-
logError(`runner runs: ${report.error ?? "runner not running"}`);
|
|
13538
|
-
return 1;
|
|
13539
|
-
}
|
|
13540
|
-
let runs = report.status.runs;
|
|
13541
|
-
if (typeof flags.limit === "number" && flags.limit > 0) {
|
|
13542
|
-
runs = runs.slice(-flags.limit);
|
|
13543
|
-
}
|
|
13544
|
-
if (flags.active) {
|
|
13545
|
-
runs = runs.filter((run) => run.status === "running");
|
|
13546
|
-
}
|
|
13547
|
-
if (flags.json) {
|
|
13548
|
-
logOut(JSON.stringify({ ok: true, runs }));
|
|
13549
|
-
return 0;
|
|
13550
|
-
}
|
|
13551
|
-
if (runs.length === 0) {
|
|
13552
|
-
logOut("runner: no runs recorded");
|
|
13553
|
-
return 0;
|
|
13554
|
-
}
|
|
13555
|
-
logOut(["runId", "started", "duration", "status"].join(" "));
|
|
13556
|
-
for (const run of runs) {
|
|
13557
|
-
const duration = run.endedAt ? formatDuration2(
|
|
13558
|
-
Math.max(0, Math.floor((run.endedAt - run.startedAt) / 1e3))
|
|
13559
|
-
) : "\u2014";
|
|
13560
|
-
logOut(
|
|
13561
|
-
[
|
|
13562
|
-
run.runId,
|
|
13563
|
-
formatDuration2(
|
|
13564
|
-
Math.max(0, Math.floor((now() - run.startedAt) / 1e3))
|
|
13565
|
-
) + " ago",
|
|
13566
|
-
duration,
|
|
13567
|
-
run.status
|
|
13568
|
-
].join(" ")
|
|
13569
|
-
);
|
|
13570
|
-
}
|
|
13571
|
-
return 0;
|
|
13480
|
+
} catch (error) {
|
|
13481
|
+
process.stderr.write(
|
|
13482
|
+
`drisp runner: ${error instanceof Error ? error.message : String(error)}
|
|
13483
|
+
`
|
|
13484
|
+
);
|
|
13485
|
+
return 1;
|
|
13572
13486
|
}
|
|
13573
|
-
|
|
13574
|
-
|
|
13487
|
+
}
|
|
13488
|
+
async function recoveryIdentity(runId) {
|
|
13489
|
+
const identities = await runnerIdentities();
|
|
13490
|
+
if (!identities.length)
|
|
13491
|
+
throw new Error("This machine is not connected to a Team.");
|
|
13492
|
+
for (const { config } of identities) {
|
|
13493
|
+
const file = join2(config.dataDirectory, "execution.sqlite");
|
|
13494
|
+
if (!existsSync(file)) continue;
|
|
13495
|
+
const store = new ExecutionStore(
|
|
13496
|
+
file,
|
|
13497
|
+
{
|
|
13498
|
+
orgId: config.orgId,
|
|
13499
|
+
runnerId: config.runnerId,
|
|
13500
|
+
installationId: config.installationId,
|
|
13501
|
+
hubEpoch: config.hubEpoch
|
|
13502
|
+
},
|
|
13503
|
+
config.capacity
|
|
13504
|
+
);
|
|
13575
13505
|
try {
|
|
13576
|
-
|
|
13577
|
-
return
|
|
13578
|
-
} catch
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
);
|
|
13582
|
-
return { ok: false, code: 1 };
|
|
13506
|
+
store.run(runId);
|
|
13507
|
+
return config;
|
|
13508
|
+
} catch {
|
|
13509
|
+
} finally {
|
|
13510
|
+
store.close();
|
|
13583
13511
|
}
|
|
13584
13512
|
}
|
|
13585
|
-
|
|
13586
|
-
if (subcommandArgs.length > 0) {
|
|
13587
|
-
logError(`runner refresh: unexpected argument ${subcommandArgs[0]}`);
|
|
13588
|
-
return 2;
|
|
13589
|
-
}
|
|
13590
|
-
if (!readConfig2()) {
|
|
13591
|
-
logError('runner refresh: not paired. Run "drisp runner pair" first.');
|
|
13592
|
-
return 1;
|
|
13593
|
-
}
|
|
13594
|
-
const result = await tryRefresh("refresh");
|
|
13595
|
-
if (!result.ok) return result.code;
|
|
13596
|
-
const { token } = result;
|
|
13597
|
-
if (flags.json) {
|
|
13598
|
-
const rotated = readConfig2();
|
|
13599
|
-
logOut(
|
|
13600
|
-
JSON.stringify({
|
|
13601
|
-
ok: true,
|
|
13602
|
-
instanceId: token.instanceId,
|
|
13603
|
-
accessToken: token.accessToken,
|
|
13604
|
-
refreshToken: rotated?.refreshToken,
|
|
13605
|
-
expiresInSec: token.expiresInSec
|
|
13606
|
-
})
|
|
13607
|
-
);
|
|
13608
|
-
} else {
|
|
13609
|
-
logOut(`runner: refreshed access token for instance ${token.instanceId}`);
|
|
13610
|
-
}
|
|
13611
|
-
return 0;
|
|
13612
|
-
}
|
|
13613
|
-
if (subcommand === "") {
|
|
13614
|
-
if (subcommandArgs.length > 0) {
|
|
13615
|
-
logError(`runner: unexpected argument ${subcommandArgs[0]}`);
|
|
13616
|
-
return 2;
|
|
13617
|
-
}
|
|
13618
|
-
if (!readConfig2()) {
|
|
13619
|
-
logError('runner: not paired. Run "drisp runner pair" first.');
|
|
13620
|
-
return 1;
|
|
13621
|
-
}
|
|
13622
|
-
if (flags.detach) {
|
|
13623
|
-
const result = await (deps.startDetachedRunner ?? defaultStartDetachedRunner)({ log: (msg) => logOut(msg) });
|
|
13624
|
-
if (flags.json) {
|
|
13625
|
-
logOut(JSON.stringify(result));
|
|
13626
|
-
} else if (result.ok) {
|
|
13627
|
-
logOut(
|
|
13628
|
-
result.connected ? "runner: started and connected" : "runner: started; socket not yet verified (tail logs)"
|
|
13629
|
-
);
|
|
13630
|
-
} else {
|
|
13631
|
-
logError(`runner: ${result.message ?? "unknown failure"}`);
|
|
13632
|
-
}
|
|
13633
|
-
return result.ok ? 0 : 1;
|
|
13634
|
-
}
|
|
13635
|
-
let runner;
|
|
13636
|
-
try {
|
|
13637
|
-
runner = await startRunnerProcess({
|
|
13638
|
-
readConfig: readConfig2,
|
|
13639
|
-
refreshAccessToken: async () => performRefreshImpl("connect"),
|
|
13640
|
-
...deps.makeInstanceSocketClient ? { makeInstanceSocketClient: deps.makeInstanceSocketClient } : {},
|
|
13641
|
-
...deps.executeRemoteAssignment ? { executeRemoteAssignment: deps.executeRemoteAssignment } : {},
|
|
13642
|
-
...deps.fetchAttachments ? { fetchAttachments: deps.fetchAttachments } : {},
|
|
13643
|
-
...deps.statePaths ? { statePaths: deps.statePaths() } : {},
|
|
13644
|
-
writeMirror,
|
|
13645
|
-
retryInitialConnect: false,
|
|
13646
|
-
cliVersion: readPackageVersion(),
|
|
13647
|
-
log: (level, message) => {
|
|
13648
|
-
if (level === "error" || level === "warn") {
|
|
13649
|
-
logError(`runner: ${message}`);
|
|
13650
|
-
} else {
|
|
13651
|
-
logOut(`runner: ${message}`);
|
|
13652
|
-
}
|
|
13653
|
-
}
|
|
13654
|
-
});
|
|
13655
|
-
} catch (err) {
|
|
13656
|
-
logError(`runner: ${err instanceof Error ? err.message : String(err)}`);
|
|
13657
|
-
return err instanceof RunnerStartupError ? err.exitCode : 1;
|
|
13658
|
-
}
|
|
13659
|
-
logOut(`runner: foreground runtime connected (pid ${runner.pid})`);
|
|
13660
|
-
const wait = deps.waitForShutdown ?? waitForShutdownSignal;
|
|
13661
|
-
const reason = await wait();
|
|
13662
|
-
await runner.stop(reason);
|
|
13663
|
-
logOut(`runner: stopped (${reason})`);
|
|
13664
|
-
return 0;
|
|
13665
|
-
}
|
|
13666
|
-
if (subcommand === "stop") {
|
|
13667
|
-
if (subcommandArgs.length > 0) {
|
|
13668
|
-
logError(`runner stop: unexpected argument ${subcommandArgs[0]}`);
|
|
13669
|
-
return 2;
|
|
13670
|
-
}
|
|
13671
|
-
const result = await (deps.stopRunner ?? defaultStopRunner)({});
|
|
13672
|
-
if (flags.json) {
|
|
13673
|
-
logOut(JSON.stringify(result));
|
|
13674
|
-
} else if (!result.wasRunning) {
|
|
13675
|
-
logOut("runner: not running");
|
|
13676
|
-
} else if (result.ok) {
|
|
13677
|
-
logOut("runner: stopped");
|
|
13678
|
-
} else {
|
|
13679
|
-
logError(`runner stop: ${result.message ?? "unknown failure"}`);
|
|
13680
|
-
}
|
|
13681
|
-
return result.ok ? 0 : 1;
|
|
13682
|
-
}
|
|
13683
|
-
if (subcommand === "restart") {
|
|
13684
|
-
if (subcommandArgs.length > 0) {
|
|
13685
|
-
logError(`runner restart: unexpected argument ${subcommandArgs[0]}`);
|
|
13686
|
-
return 2;
|
|
13687
|
-
}
|
|
13688
|
-
const stopResult = await (deps.stopRunner ?? defaultStopRunner)({});
|
|
13689
|
-
if (stopResult.wasRunning && !stopResult.ok) {
|
|
13690
|
-
logError(
|
|
13691
|
-
`runner restart: stop failed: ${stopResult.message ?? "unknown"}`
|
|
13692
|
-
);
|
|
13693
|
-
return 1;
|
|
13694
|
-
}
|
|
13695
|
-
const startResult = await (deps.startDetachedRunner ?? defaultStartDetachedRunner)({ log: (msg) => logOut(msg) });
|
|
13696
|
-
if (flags.json) {
|
|
13697
|
-
logOut(JSON.stringify({ restart: true, ...startResult }));
|
|
13698
|
-
} else if (startResult.ok) {
|
|
13699
|
-
logOut(
|
|
13700
|
-
startResult.connected ? "runner: restarted and connected" : "runner: restarted; socket not yet verified"
|
|
13701
|
-
);
|
|
13702
|
-
} else {
|
|
13703
|
-
logError(`runner restart: ${startResult.message ?? "unknown"}`);
|
|
13704
|
-
}
|
|
13705
|
-
return startResult.ok ? 0 : 1;
|
|
13706
|
-
}
|
|
13707
|
-
if (subcommand === "install") {
|
|
13708
|
-
if (subcommandArgs.length > 0) {
|
|
13709
|
-
logError(`runner install: unexpected argument ${subcommandArgs[0]}`);
|
|
13710
|
-
return 2;
|
|
13711
|
-
}
|
|
13712
|
-
const installer = deps.installServiceUnit ?? defaultInstallServiceUnit;
|
|
13713
|
-
const result = installer();
|
|
13714
|
-
if (flags.json) {
|
|
13715
|
-
logOut(JSON.stringify(result));
|
|
13716
|
-
} else if (result.ok) {
|
|
13717
|
-
logOut(`runner: wrote service unit at ${result.path}`);
|
|
13718
|
-
logOut(`runner: load with: ${result.loadCommand}`);
|
|
13719
|
-
logOut(`runner: start with: ${result.startCommand}`);
|
|
13720
|
-
} else {
|
|
13721
|
-
logError(`runner install: ${result.message ?? "unsupported platform"}`);
|
|
13722
|
-
}
|
|
13723
|
-
return result.ok ? 0 : 1;
|
|
13724
|
-
}
|
|
13725
|
-
if (subcommand === "doctor") {
|
|
13726
|
-
let reportDoctor2 = function(ok) {
|
|
13727
|
-
if (flags.json) {
|
|
13728
|
-
logOut(
|
|
13729
|
-
JSON.stringify({
|
|
13730
|
-
ok,
|
|
13731
|
-
paired: true,
|
|
13732
|
-
instanceId: config.instanceId,
|
|
13733
|
-
dashboardUrl: config.dashboardUrl,
|
|
13734
|
-
pairedAt: config.pairedAt,
|
|
13735
|
-
...refreshed?.lastRefreshAt !== void 0 ? { lastRefreshAt: refreshed.lastRefreshAt } : {},
|
|
13736
|
-
configPath: configPath(),
|
|
13737
|
-
...runnerReport ? { runner: runnerReport } : {}
|
|
13738
|
-
})
|
|
13739
|
-
);
|
|
13740
|
-
} else {
|
|
13741
|
-
logOut(
|
|
13742
|
-
`runner: paired to ${config.dashboardUrl} as ${config.instanceId}`
|
|
13743
|
-
);
|
|
13744
|
-
if (token) {
|
|
13745
|
-
logOut(`runner: refresh token rotated, access token minted`);
|
|
13746
|
-
}
|
|
13747
|
-
if (runnerReport) {
|
|
13748
|
-
if (runnerReport.matches) {
|
|
13749
|
-
logOut(
|
|
13750
|
-
`runner: runner ${runnerReport.id} bound to this instance (executionTarget=remote, remoteInstanceId=${runnerReport.remoteInstanceId})`
|
|
13751
|
-
);
|
|
13752
|
-
} else {
|
|
13753
|
-
logError(
|
|
13754
|
-
`runner doctor: runner ${runnerReport.id} not bound \u2014 ${runnerReport.error ?? "unknown reason"}`
|
|
13755
|
-
);
|
|
13756
|
-
}
|
|
13757
|
-
}
|
|
13758
|
-
}
|
|
13759
|
-
return ok ? 0 : 1;
|
|
13760
|
-
};
|
|
13761
|
-
var reportDoctor = reportDoctor2;
|
|
13762
|
-
if (subcommandArgs.length > 0) {
|
|
13763
|
-
logError(`runner doctor: unexpected argument ${subcommandArgs[0]}`);
|
|
13764
|
-
return 2;
|
|
13765
|
-
}
|
|
13766
|
-
const config = readConfig2();
|
|
13767
|
-
if (!config) {
|
|
13768
|
-
if (flags.json) {
|
|
13769
|
-
logOut(JSON.stringify({ ok: false, paired: false }));
|
|
13770
|
-
} else {
|
|
13771
|
-
logOut("runner: not paired");
|
|
13772
|
-
logOut('runner doctor: run "drisp runner pair" before using doctor.');
|
|
13773
|
-
}
|
|
13774
|
-
return 1;
|
|
13775
|
-
}
|
|
13776
|
-
let token = null;
|
|
13777
|
-
if (flags.runner) {
|
|
13778
|
-
const refreshResult = await tryRefresh("refresh");
|
|
13779
|
-
if (!refreshResult.ok) return refreshResult.code;
|
|
13780
|
-
token = refreshResult.token;
|
|
13781
|
-
}
|
|
13782
|
-
const refreshed = readConfig2();
|
|
13783
|
-
let runnerReport;
|
|
13784
|
-
let runnerOk = !flags.runner;
|
|
13785
|
-
if (flags.runner && token) {
|
|
13786
|
-
const runnerId = flags.runner;
|
|
13787
|
-
const accessToken = token.accessToken;
|
|
13788
|
-
const expectedInstanceId = token.instanceId;
|
|
13789
|
-
const url = new URL(
|
|
13790
|
-
`/api/runners/${encodeURIComponent(runnerId)}`,
|
|
13791
|
-
config.dashboardUrl
|
|
13792
|
-
).toString();
|
|
13793
|
-
let response;
|
|
13794
|
-
try {
|
|
13795
|
-
response = await fetchImpl(url, {
|
|
13796
|
-
method: "GET",
|
|
13797
|
-
headers: {
|
|
13798
|
-
authorization: `Bearer ${accessToken}`,
|
|
13799
|
-
accept: "application/json"
|
|
13800
|
-
}
|
|
13801
|
-
});
|
|
13802
|
-
} catch (err) {
|
|
13803
|
-
runnerOk = false;
|
|
13804
|
-
runnerReport = {
|
|
13805
|
-
id: runnerId,
|
|
13806
|
-
matches: false,
|
|
13807
|
-
error: `request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
13808
|
-
};
|
|
13809
|
-
return reportDoctor2(false);
|
|
13810
|
-
}
|
|
13811
|
-
if (response.status === 404) {
|
|
13812
|
-
runnerOk = false;
|
|
13813
|
-
runnerReport = {
|
|
13814
|
-
id: runnerId,
|
|
13815
|
-
matches: false,
|
|
13816
|
-
error: "runner not found"
|
|
13817
|
-
};
|
|
13818
|
-
} else if (response.status === 405 || response.status === 501) {
|
|
13819
|
-
runnerOk = false;
|
|
13820
|
-
runnerReport = {
|
|
13821
|
-
id: runnerId,
|
|
13822
|
-
matches: false,
|
|
13823
|
-
error: "runner check unavailable: dashboard endpoint missing (GET /api/runners/<id>)"
|
|
13824
|
-
};
|
|
13825
|
-
} else if (!response.ok) {
|
|
13826
|
-
runnerOk = false;
|
|
13827
|
-
const message = await safeReadError(response);
|
|
13828
|
-
runnerReport = {
|
|
13829
|
-
id: runnerId,
|
|
13830
|
-
matches: false,
|
|
13831
|
-
error: `dashboard returned ${response.status}${message ? ` \u2014 ${message}` : ""}`
|
|
13832
|
-
};
|
|
13833
|
-
} else {
|
|
13834
|
-
let body;
|
|
13835
|
-
try {
|
|
13836
|
-
body = await response.json();
|
|
13837
|
-
} catch (err) {
|
|
13838
|
-
runnerOk = false;
|
|
13839
|
-
runnerReport = {
|
|
13840
|
-
id: runnerId,
|
|
13841
|
-
matches: false,
|
|
13842
|
-
error: `invalid response body: ${err instanceof Error ? err.message : String(err)}`
|
|
13843
|
-
};
|
|
13844
|
-
return reportDoctor2(false);
|
|
13845
|
-
}
|
|
13846
|
-
const obj = typeof body === "object" && body !== null ? body : {};
|
|
13847
|
-
const executionTarget = typeof obj["executionTarget"] === "string" ? obj["executionTarget"] : void 0;
|
|
13848
|
-
const remoteInstanceId = typeof obj["remoteInstanceId"] === "string" ? obj["remoteInstanceId"] : void 0;
|
|
13849
|
-
const matchesTarget = executionTarget === "remote";
|
|
13850
|
-
const matchesInstance = remoteInstanceId === expectedInstanceId;
|
|
13851
|
-
const matches = matchesTarget && matchesInstance;
|
|
13852
|
-
runnerOk = matches;
|
|
13853
|
-
const reasons = [];
|
|
13854
|
-
if (!matchesTarget) {
|
|
13855
|
-
reasons.push(
|
|
13856
|
-
`executionTarget is "${executionTarget ?? "unset"}" (expected "remote")`
|
|
13857
|
-
);
|
|
13858
|
-
}
|
|
13859
|
-
if (!matchesInstance) {
|
|
13860
|
-
reasons.push(
|
|
13861
|
-
`remoteInstanceId is "${remoteInstanceId ?? "unset"}" (expected "${expectedInstanceId}")`
|
|
13862
|
-
);
|
|
13863
|
-
}
|
|
13864
|
-
runnerReport = {
|
|
13865
|
-
id: runnerId,
|
|
13866
|
-
executionTarget,
|
|
13867
|
-
remoteInstanceId,
|
|
13868
|
-
matches,
|
|
13869
|
-
error: reasons.length > 0 ? reasons.join("; ") : void 0
|
|
13870
|
-
};
|
|
13871
|
-
}
|
|
13872
|
-
}
|
|
13873
|
-
return reportDoctor2(runnerOk);
|
|
13874
|
-
}
|
|
13875
|
-
if (subcommand === "console") {
|
|
13876
|
-
const message = "dashboard console is deprecated; paired dashboard feed sync now routes dashboard UI and channel decisions.";
|
|
13877
|
-
if (flags.json) {
|
|
13878
|
-
logOut(JSON.stringify({ ok: false, deprecated: true, message }));
|
|
13879
|
-
} else {
|
|
13880
|
-
logError(message);
|
|
13881
|
-
}
|
|
13882
|
-
return 1;
|
|
13883
|
-
}
|
|
13884
|
-
if (subcommand === "list") {
|
|
13885
|
-
if (subcommandArgs.length > 0) {
|
|
13886
|
-
logError(`runner list: unexpected argument ${subcommandArgs[0]}`);
|
|
13887
|
-
return 2;
|
|
13888
|
-
}
|
|
13889
|
-
const config = readConfig2();
|
|
13890
|
-
if (!config) {
|
|
13891
|
-
if (flags.json) {
|
|
13892
|
-
logOut(JSON.stringify({ ok: false, paired: false }));
|
|
13893
|
-
} else {
|
|
13894
|
-
logError('runner list: not paired. Run "drisp runner pair" first.');
|
|
13895
|
-
}
|
|
13896
|
-
return 1;
|
|
13897
|
-
}
|
|
13898
|
-
const mirror = readMirror();
|
|
13899
|
-
if (!mirror) {
|
|
13900
|
-
if (flags.json) {
|
|
13901
|
-
logOut(
|
|
13902
|
-
JSON.stringify({
|
|
13903
|
-
ok: true,
|
|
13904
|
-
paired: true,
|
|
13905
|
-
instanceId: config.instanceId,
|
|
13906
|
-
attachments: [],
|
|
13907
|
-
mirror: null
|
|
13908
|
-
})
|
|
13909
|
-
);
|
|
13910
|
-
} else {
|
|
13911
|
-
logOut(
|
|
13912
|
-
`runner list: paired as ${config.instanceId}, no attachment mirror on disk.`
|
|
13913
|
-
);
|
|
13914
|
-
logOut(
|
|
13915
|
-
"runner list: re-run `drisp runner pair` to refresh the mirror."
|
|
13916
|
-
);
|
|
13917
|
-
}
|
|
13918
|
-
return 0;
|
|
13919
|
-
}
|
|
13920
|
-
if (flags.json) {
|
|
13921
|
-
logOut(
|
|
13922
|
-
JSON.stringify({
|
|
13923
|
-
ok: true,
|
|
13924
|
-
paired: true,
|
|
13925
|
-
instanceId: mirror.instanceId,
|
|
13926
|
-
fetchedAt: mirror.fetchedAt,
|
|
13927
|
-
attachments: mirror.attachments
|
|
13928
|
-
})
|
|
13929
|
-
);
|
|
13930
|
-
} else {
|
|
13931
|
-
logOut(`runner: instance ${mirror.instanceId}`);
|
|
13932
|
-
logOut(
|
|
13933
|
-
`runner: mirror fetched ${new Date(mirror.fetchedAt).toISOString()}`
|
|
13934
|
-
);
|
|
13935
|
-
if (mirror.attachments.length === 0) {
|
|
13936
|
-
logOut("runner: no runners attached.");
|
|
13937
|
-
} else {
|
|
13938
|
-
logOut(`runner: ${mirror.attachments.length} runner(s) attached:`);
|
|
13939
|
-
for (const a of mirror.attachments) {
|
|
13940
|
-
const label = a.name ? `${a.name} (${a.runnerId})` : a.runnerId;
|
|
13941
|
-
const target = a.executionTarget ? ` [${a.executionTarget}]` : "";
|
|
13942
|
-
logOut(` - ${label}${target}`);
|
|
13943
|
-
}
|
|
13944
|
-
}
|
|
13945
|
-
}
|
|
13946
|
-
return 0;
|
|
13947
|
-
}
|
|
13948
|
-
if (subcommand === "unpair") {
|
|
13949
|
-
if (subcommandArgs.length > 0) {
|
|
13950
|
-
logError(`runner unpair: unexpected argument ${subcommandArgs[0]}`);
|
|
13951
|
-
return 2;
|
|
13952
|
-
}
|
|
13953
|
-
const config = readConfig2();
|
|
13954
|
-
if (!config) {
|
|
13955
|
-
if (flags.json) {
|
|
13956
|
-
logOut(JSON.stringify({ ok: true, paired: false }));
|
|
13957
|
-
} else {
|
|
13958
|
-
logOut("runner unpair: not paired (nothing to do)");
|
|
13959
|
-
}
|
|
13960
|
-
return 0;
|
|
13961
|
-
}
|
|
13962
|
-
const stopResult = await (deps.stopRunner ?? defaultStopRunner)({});
|
|
13963
|
-
if (!flags.json) {
|
|
13964
|
-
if (stopResult.wasRunning) {
|
|
13965
|
-
if (stopResult.ok) {
|
|
13966
|
-
logOut("runner: stopped");
|
|
13967
|
-
} else {
|
|
13968
|
-
logError(`runner: stop failed: ${stopResult.message ?? "unknown"}`);
|
|
13969
|
-
}
|
|
13970
|
-
} else {
|
|
13971
|
-
logOut("runner: not running (skipping stop)");
|
|
13972
|
-
}
|
|
13973
|
-
}
|
|
13974
|
-
let revokeOk = false;
|
|
13975
|
-
let revokeMessage;
|
|
13976
|
-
try {
|
|
13977
|
-
const refreshResult = await tryRefresh("refresh");
|
|
13978
|
-
if (refreshResult.ok) {
|
|
13979
|
-
const url = new URL(
|
|
13980
|
-
`/api/instances/${encodeURIComponent(config.instanceId)}/revoke`,
|
|
13981
|
-
config.dashboardUrl
|
|
13982
|
-
).toString();
|
|
13983
|
-
const response = await fetchImpl(url, {
|
|
13984
|
-
method: "POST",
|
|
13985
|
-
headers: {
|
|
13986
|
-
authorization: `Bearer ${refreshResult.token.accessToken}`,
|
|
13987
|
-
"content-type": "application/json"
|
|
13988
|
-
},
|
|
13989
|
-
body: JSON.stringify({})
|
|
13990
|
-
});
|
|
13991
|
-
if (response.ok || response.status === 404) {
|
|
13992
|
-
revokeOk = true;
|
|
13993
|
-
} else {
|
|
13994
|
-
const detail = await safeReadError(response);
|
|
13995
|
-
revokeMessage = `dashboard returned ${response.status}${detail ? ` \u2014 ${detail}` : ""}`;
|
|
13996
|
-
}
|
|
13997
|
-
} else {
|
|
13998
|
-
revokeMessage = "could not refresh access token";
|
|
13999
|
-
}
|
|
14000
|
-
} catch (err) {
|
|
14001
|
-
revokeMessage = err instanceof Error ? err.message : String(err);
|
|
14002
|
-
}
|
|
14003
|
-
if (!flags.json) {
|
|
14004
|
-
if (revokeOk) {
|
|
14005
|
-
logOut(`runner: revoking refresh token at ${config.dashboardUrl}`);
|
|
14006
|
-
logOut("runner: refresh token revoked");
|
|
14007
|
-
} else {
|
|
14008
|
-
logError(
|
|
14009
|
-
`runner: revoke failed${revokeMessage ? `: ${revokeMessage}` : ""}`
|
|
14010
|
-
);
|
|
14011
|
-
logOut(
|
|
14012
|
-
"runner: WARNING \u2014 refresh token may still be valid until you revoke it from the dashboard UI."
|
|
14013
|
-
);
|
|
14014
|
-
}
|
|
14015
|
-
}
|
|
14016
|
-
removeConfig();
|
|
14017
|
-
removeMirror();
|
|
14018
|
-
if (flags.json) {
|
|
14019
|
-
logOut(
|
|
14020
|
-
JSON.stringify({
|
|
14021
|
-
ok: true,
|
|
14022
|
-
runner: stopResult,
|
|
14023
|
-
revoke: {
|
|
14024
|
-
ok: revokeOk,
|
|
14025
|
-
...revokeMessage ? { message: revokeMessage } : {}
|
|
14026
|
-
}
|
|
14027
|
-
})
|
|
14028
|
-
);
|
|
14029
|
-
} else {
|
|
14030
|
-
logOut(
|
|
14031
|
-
`runner: unpaired (credentials removed${stopResult.wasRunning ? ", runner stopped" : ""})`
|
|
14032
|
-
);
|
|
14033
|
-
}
|
|
14034
|
-
return 0;
|
|
14035
|
-
}
|
|
14036
|
-
logError(`Unknown runner subcommand: ${subcommand}`);
|
|
14037
|
-
logError(USAGE3);
|
|
14038
|
-
return 2;
|
|
14039
|
-
}
|
|
14040
|
-
function resolveRunnerEntry() {
|
|
14041
|
-
let here;
|
|
14042
|
-
try {
|
|
14043
|
-
here = fileURLToPath(import.meta.url);
|
|
14044
|
-
} catch {
|
|
14045
|
-
return null;
|
|
14046
|
-
}
|
|
14047
|
-
const candidates = [
|
|
14048
|
-
path6.join(path6.dirname(here), "runner.js"),
|
|
14049
|
-
// When invoked via `npm run start`, `here` may be the unbundled source
|
|
14050
|
-
// path. Walk up until we hit a `dist/` sibling.
|
|
14051
|
-
path6.join(path6.dirname(here), "..", "..", "..", "dist", "runner.js")
|
|
14052
|
-
];
|
|
14053
|
-
for (const candidate of candidates) {
|
|
14054
|
-
try {
|
|
14055
|
-
fs6.accessSync(candidate, fs6.constants.R_OK);
|
|
14056
|
-
return candidate;
|
|
14057
|
-
} catch {
|
|
14058
|
-
}
|
|
14059
|
-
}
|
|
14060
|
-
return null;
|
|
14061
|
-
}
|
|
14062
|
-
var RUNNER_ENV_ALLOWLIST = [
|
|
14063
|
-
"HOME",
|
|
14064
|
-
"PATH",
|
|
14065
|
-
"LANG",
|
|
14066
|
-
"LC_ALL",
|
|
14067
|
-
"ATHENA_DASHBOARD_ORIGIN"
|
|
14068
|
-
];
|
|
14069
|
-
function buildRunnerEnv(env2) {
|
|
14070
|
-
const out = {};
|
|
14071
|
-
for (const key of RUNNER_ENV_ALLOWLIST) {
|
|
14072
|
-
if (env2[key] !== void 0) out[key] = env2[key];
|
|
14073
|
-
}
|
|
14074
|
-
for (const [key, value] of Object.entries(env2)) {
|
|
14075
|
-
if (key.startsWith("XDG_") && value !== void 0) {
|
|
14076
|
-
out[key] = value;
|
|
14077
|
-
}
|
|
14078
|
-
}
|
|
14079
|
-
return out;
|
|
14080
|
-
}
|
|
14081
|
-
function readRunnerPid(paths) {
|
|
14082
|
-
const current = readPidLock(paths.pidPath);
|
|
14083
|
-
if (current.state === "held") return { ...current, pidPath: paths.pidPath };
|
|
14084
|
-
const legacy = readPidLock(paths.legacyPidPath);
|
|
14085
|
-
if (legacy.state === "held") return { ...legacy, pidPath: paths.legacyPidPath };
|
|
14086
|
-
return { ...current, pidPath: paths.pidPath };
|
|
14087
|
-
}
|
|
14088
|
-
function isProcessAlive(pid) {
|
|
14089
|
-
try {
|
|
14090
|
-
process.kill(pid, 0);
|
|
14091
|
-
return true;
|
|
14092
|
-
} catch (err) {
|
|
14093
|
-
return err.code === "EPERM";
|
|
14094
|
-
}
|
|
14095
|
-
}
|
|
14096
|
-
var SOCKET_PROBE_TIMEOUT_MS = 1e4;
|
|
14097
|
-
var SOCKET_PROBE_INTERVAL_MS = 200;
|
|
14098
|
-
async function defaultStartDetachedRunner(opts) {
|
|
14099
|
-
const paths = runnerStatePaths();
|
|
14100
|
-
const existing = readRunnerPid(paths);
|
|
14101
|
-
if (existing.state === "held") {
|
|
14102
|
-
return {
|
|
14103
|
-
ok: false,
|
|
14104
|
-
pid: existing.pid,
|
|
14105
|
-
message: alreadyRunningMessage(existing.pid, existing.pidPath)
|
|
14106
|
-
};
|
|
14107
|
-
}
|
|
14108
|
-
const entry = resolveRunnerEntry();
|
|
14109
|
-
if (!entry) {
|
|
14110
|
-
return { ok: false, message: "cannot resolve runner.js entry path" };
|
|
14111
|
-
}
|
|
14112
|
-
let child;
|
|
14113
|
-
try {
|
|
14114
|
-
child = spawn(process.execPath, [entry], {
|
|
14115
|
-
detached: true,
|
|
14116
|
-
stdio: "ignore",
|
|
14117
|
-
env: buildRunnerEnv(process.env)
|
|
14118
|
-
});
|
|
14119
|
-
} catch (err) {
|
|
14120
|
-
return {
|
|
14121
|
-
ok: false,
|
|
14122
|
-
message: err instanceof Error ? err.message : String(err)
|
|
14123
|
-
};
|
|
14124
|
-
}
|
|
14125
|
-
child.unref();
|
|
14126
|
-
let earlyExit;
|
|
14127
|
-
let spawnError;
|
|
14128
|
-
const onError = (err) => {
|
|
14129
|
-
spawnError = err.message;
|
|
14130
|
-
};
|
|
14131
|
-
const onExit = (code, signal) => {
|
|
14132
|
-
earlyExit = `runner exited early (code=${code ?? "null"}, signal=${signal ?? "null"})`;
|
|
14133
|
-
};
|
|
14134
|
-
child.once("error", onError);
|
|
14135
|
-
child.once("exit", onExit);
|
|
14136
|
-
const probeDeadline = Date.now() + SOCKET_PROBE_TIMEOUT_MS;
|
|
14137
|
-
let connected = false;
|
|
14138
|
-
let everReached = false;
|
|
14139
|
-
let pid;
|
|
14140
|
-
while (Date.now() < probeDeadline) {
|
|
14141
|
-
if (spawnError) break;
|
|
14142
|
-
if (earlyExit) break;
|
|
14143
|
-
const report = defaultReadRunnerStatus();
|
|
14144
|
-
if (report.running) {
|
|
14145
|
-
everReached = true;
|
|
14146
|
-
pid = report.status.pid;
|
|
14147
|
-
if (report.status.socketConnected) {
|
|
14148
|
-
connected = true;
|
|
14149
|
-
break;
|
|
14150
|
-
}
|
|
14151
|
-
}
|
|
14152
|
-
await new Promise((r) => setTimeout(r, SOCKET_PROBE_INTERVAL_MS));
|
|
14153
|
-
}
|
|
14154
|
-
child.off("error", onError);
|
|
14155
|
-
child.off("exit", onExit);
|
|
14156
|
-
if (spawnError) {
|
|
14157
|
-
return { ok: false, message: `runner failed to start: ${spawnError}` };
|
|
14158
|
-
}
|
|
14159
|
-
if (earlyExit && !connected) {
|
|
14160
|
-
return { ok: false, message: earlyExit };
|
|
14161
|
-
}
|
|
14162
|
-
if (!connected && !everReached) {
|
|
14163
|
-
return {
|
|
14164
|
-
ok: false,
|
|
14165
|
-
message: `runner did not report status within ${SOCKET_PROBE_TIMEOUT_MS}ms (see ${paths.logPath})`
|
|
14166
|
-
};
|
|
14167
|
-
}
|
|
14168
|
-
opts.log(
|
|
14169
|
-
connected ? `runner: socket verified${pid !== void 0 ? ` (pid ${pid})` : ""}` : "runner: started but socket not yet connected"
|
|
14170
|
-
);
|
|
14171
|
-
return {
|
|
14172
|
-
ok: true,
|
|
14173
|
-
connected,
|
|
14174
|
-
...pid !== void 0 ? { pid } : {},
|
|
14175
|
-
message: connected ? "started, socket verified" : "started, socket not yet verified"
|
|
14176
|
-
};
|
|
14177
|
-
}
|
|
14178
|
-
async function defaultStopRunner(opts = {}) {
|
|
14179
|
-
const paths = runnerStatePaths();
|
|
14180
|
-
const existing = readRunnerPid(paths);
|
|
14181
|
-
if (existing.state !== "held") {
|
|
14182
|
-
return { ok: true, wasRunning: false, message: "runner not running" };
|
|
14183
|
-
}
|
|
14184
|
-
try {
|
|
14185
|
-
process.kill(existing.pid, "SIGTERM");
|
|
14186
|
-
} catch (err) {
|
|
14187
|
-
if (err.code === "ESRCH") {
|
|
14188
|
-
return { ok: true, wasRunning: false, message: "runner not running" };
|
|
14189
|
-
}
|
|
14190
|
-
return {
|
|
14191
|
-
ok: false,
|
|
14192
|
-
wasRunning: true,
|
|
14193
|
-
message: err instanceof Error ? err.message : String(err)
|
|
14194
|
-
};
|
|
14195
|
-
}
|
|
14196
|
-
const deadline = Date.now() + (opts.timeoutMs ?? 5e3);
|
|
14197
|
-
while (Date.now() < deadline) {
|
|
14198
|
-
const after = readPidLock(existing.pidPath);
|
|
14199
|
-
if (after.state !== "held" || after.pid !== existing.pid) {
|
|
14200
|
-
return { ok: true, wasRunning: true };
|
|
14201
|
-
}
|
|
14202
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
14203
|
-
}
|
|
14204
|
-
return { ok: false, wasRunning: true, message: "runner did not exit in time" };
|
|
14205
|
-
}
|
|
14206
|
-
function defaultReadRunnerStatus() {
|
|
14207
|
-
const paths = runnerStatePaths();
|
|
14208
|
-
const existing = readRunnerPid(paths);
|
|
14209
|
-
if (existing.state !== "held") {
|
|
14210
|
-
return { running: false, error: "runner not running" };
|
|
14211
|
-
}
|
|
14212
|
-
const status = readRunnerStatusFile(paths.statusPath);
|
|
14213
|
-
if (!status || status.pid !== existing.pid || !isProcessAlive(status.pid)) {
|
|
14214
|
-
return {
|
|
14215
|
-
running: false,
|
|
14216
|
-
error: `runner pid ${existing.pid} is alive but has not written ${paths.statusPath} yet`
|
|
14217
|
-
};
|
|
14218
|
-
}
|
|
14219
|
-
return { running: true, status };
|
|
14220
|
-
}
|
|
14221
|
-
async function defaultTailRunnerLog(opts) {
|
|
14222
|
-
const paths = runnerStatePaths();
|
|
14223
|
-
let stream2 = null;
|
|
14224
|
-
let watcher = null;
|
|
14225
|
-
try {
|
|
14226
|
-
const stat = fs6.statSync(paths.logPath);
|
|
14227
|
-
const size = stat.size;
|
|
14228
|
-
const buf = Buffer.alloc(Math.min(size, opts.tail * 1024));
|
|
14229
|
-
const fd = fs6.openSync(paths.logPath, "r");
|
|
14230
|
-
try {
|
|
14231
|
-
fs6.readSync(fd, buf, 0, buf.length, Math.max(0, size - buf.length));
|
|
14232
|
-
} finally {
|
|
14233
|
-
fs6.closeSync(fd);
|
|
14234
|
-
}
|
|
14235
|
-
const lines = buf.toString("utf-8").split("\n").filter((l) => l.length > 0);
|
|
14236
|
-
const tail = lines.slice(-opts.tail);
|
|
14237
|
-
for (const line of tail) {
|
|
14238
|
-
process.stdout.write(line + "\n");
|
|
14239
|
-
}
|
|
14240
|
-
if (!opts.follow) return 0;
|
|
14241
|
-
} catch (err) {
|
|
14242
|
-
if (err.code === "ENOENT") {
|
|
14243
|
-
process.stderr.write(
|
|
14244
|
-
`runner logs: log file ${paths.logPath} does not exist yet
|
|
14245
|
-
`
|
|
14246
|
-
);
|
|
14247
|
-
return opts.follow ? 0 : 1;
|
|
14248
|
-
}
|
|
14249
|
-
throw err;
|
|
14250
|
-
}
|
|
14251
|
-
let position = fs6.statSync(paths.logPath).size;
|
|
14252
|
-
return await new Promise((resolve) => {
|
|
14253
|
-
let pollTimer = null;
|
|
14254
|
-
const drain = () => {
|
|
14255
|
-
try {
|
|
14256
|
-
const stat = fs6.statSync(paths.logPath);
|
|
14257
|
-
if (stat.size < position) {
|
|
14258
|
-
position = 0;
|
|
14259
|
-
}
|
|
14260
|
-
if (stat.size > position) {
|
|
14261
|
-
const fd = fs6.openSync(paths.logPath, "r");
|
|
14262
|
-
const buf = Buffer.alloc(stat.size - position);
|
|
14263
|
-
try {
|
|
14264
|
-
fs6.readSync(fd, buf, 0, buf.length, position);
|
|
14265
|
-
} finally {
|
|
14266
|
-
fs6.closeSync(fd);
|
|
14267
|
-
}
|
|
14268
|
-
position = stat.size;
|
|
14269
|
-
process.stdout.write(buf);
|
|
14270
|
-
}
|
|
14271
|
-
} catch (err) {
|
|
14272
|
-
if (err.code !== "ENOENT") {
|
|
14273
|
-
process.stderr.write(
|
|
14274
|
-
`runner logs: tail error: ${err instanceof Error ? err.message : String(err)}
|
|
14275
|
-
`
|
|
14276
|
-
);
|
|
14277
|
-
}
|
|
14278
|
-
}
|
|
14279
|
-
};
|
|
14280
|
-
try {
|
|
14281
|
-
watcher = fs6.watch(paths.logPath, { persistent: true }, () => {
|
|
14282
|
-
drain();
|
|
14283
|
-
});
|
|
14284
|
-
} catch {
|
|
14285
|
-
pollTimer = setInterval(drain, 500);
|
|
14286
|
-
pollTimer.unref();
|
|
14287
|
-
}
|
|
14288
|
-
const onSignal = () => {
|
|
14289
|
-
if (watcher) {
|
|
14290
|
-
watcher.close();
|
|
14291
|
-
watcher = null;
|
|
14292
|
-
}
|
|
14293
|
-
if (pollTimer) {
|
|
14294
|
-
clearInterval(pollTimer);
|
|
14295
|
-
pollTimer = null;
|
|
14296
|
-
}
|
|
14297
|
-
if (stream2) {
|
|
14298
|
-
stream2.close();
|
|
14299
|
-
stream2 = null;
|
|
14300
|
-
}
|
|
14301
|
-
resolve(0);
|
|
14302
|
-
};
|
|
14303
|
-
process.once("SIGINT", onSignal);
|
|
14304
|
-
process.once("SIGTERM", onSignal);
|
|
14305
|
-
});
|
|
14306
|
-
}
|
|
14307
|
-
function formatDuration2(seconds) {
|
|
14308
|
-
if (!Number.isFinite(seconds) || seconds < 0) return "?";
|
|
14309
|
-
if (seconds < 60) return `${seconds}s`;
|
|
14310
|
-
if (seconds < 3600) {
|
|
14311
|
-
const m = Math.floor(seconds / 60);
|
|
14312
|
-
const s = seconds % 60;
|
|
14313
|
-
return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
14314
|
-
}
|
|
14315
|
-
if (seconds < 86400) {
|
|
14316
|
-
const h2 = Math.floor(seconds / 3600);
|
|
14317
|
-
const m = Math.floor(seconds % 3600 / 60);
|
|
14318
|
-
return m > 0 ? `${h2}h${m}m` : `${h2}h`;
|
|
14319
|
-
}
|
|
14320
|
-
const d = Math.floor(seconds / 86400);
|
|
14321
|
-
const h = Math.floor(seconds % 86400 / 3600);
|
|
14322
|
-
return h > 0 ? `${d}d${h}h` : `${d}d`;
|
|
14323
|
-
}
|
|
14324
|
-
async function fetchRunnerHealth(fetchImpl, dashboardUrl, runnerId, token) {
|
|
14325
|
-
const url = new URL(
|
|
14326
|
-
`/api/runners/${encodeURIComponent(runnerId)}`,
|
|
14327
|
-
dashboardUrl
|
|
14328
|
-
).toString();
|
|
14329
|
-
let response;
|
|
14330
|
-
try {
|
|
14331
|
-
response = await fetchImpl(url, {
|
|
14332
|
-
method: "GET",
|
|
14333
|
-
headers: {
|
|
14334
|
-
authorization: `Bearer ${token.accessToken}`,
|
|
14335
|
-
accept: "application/json"
|
|
14336
|
-
}
|
|
14337
|
-
});
|
|
14338
|
-
} catch (err) {
|
|
14339
|
-
return {
|
|
14340
|
-
id: runnerId,
|
|
14341
|
-
matches: false,
|
|
14342
|
-
error: `request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
14343
|
-
};
|
|
14344
|
-
}
|
|
14345
|
-
if (!response.ok) {
|
|
14346
|
-
return {
|
|
14347
|
-
id: runnerId,
|
|
14348
|
-
matches: false,
|
|
14349
|
-
error: `dashboard returned ${response.status}`
|
|
14350
|
-
};
|
|
14351
|
-
}
|
|
14352
|
-
let body;
|
|
14353
|
-
try {
|
|
14354
|
-
body = await response.json();
|
|
14355
|
-
} catch (err) {
|
|
14356
|
-
return {
|
|
14357
|
-
id: runnerId,
|
|
14358
|
-
matches: false,
|
|
14359
|
-
error: `invalid response body: ${err instanceof Error ? err.message : String(err)}`
|
|
14360
|
-
};
|
|
14361
|
-
}
|
|
14362
|
-
const obj = typeof body === "object" && body !== null ? body : {};
|
|
14363
|
-
const executionTarget = typeof obj["executionTarget"] === "string" ? obj["executionTarget"] : void 0;
|
|
14364
|
-
const remoteInstanceId = typeof obj["remoteInstanceId"] === "string" ? obj["remoteInstanceId"] : void 0;
|
|
14365
|
-
const matches = executionTarget === "remote" && remoteInstanceId === token.instanceId;
|
|
14366
|
-
const reasons = [];
|
|
14367
|
-
if (executionTarget !== "remote") {
|
|
14368
|
-
reasons.push(
|
|
14369
|
-
`executionTarget=${executionTarget ?? "unset"} (expected "remote")`
|
|
14370
|
-
);
|
|
14371
|
-
}
|
|
14372
|
-
if (remoteInstanceId !== token.instanceId) {
|
|
14373
|
-
reasons.push(
|
|
14374
|
-
`remoteInstanceId=${remoteInstanceId ?? "unset"} (expected "${token.instanceId}")`
|
|
14375
|
-
);
|
|
14376
|
-
}
|
|
14377
|
-
return {
|
|
14378
|
-
id: runnerId,
|
|
14379
|
-
matches,
|
|
14380
|
-
...executionTarget !== void 0 ? { executionTarget } : {},
|
|
14381
|
-
...remoteInstanceId !== void 0 ? { remoteInstanceId } : {},
|
|
14382
|
-
...reasons.length > 0 ? { error: reasons.join("; ") } : {}
|
|
14383
|
-
};
|
|
14384
|
-
}
|
|
14385
|
-
function defaultInstallServiceUnit() {
|
|
14386
|
-
const entry = resolveRunnerEntry();
|
|
14387
|
-
if (!entry) {
|
|
14388
|
-
return {
|
|
14389
|
-
ok: false,
|
|
14390
|
-
platform: "unsupported",
|
|
14391
|
-
message: "cannot resolve runner.js entry path"
|
|
14392
|
-
};
|
|
14393
|
-
}
|
|
14394
|
-
return installServiceUnit({
|
|
14395
|
-
runnerEntry: entry,
|
|
14396
|
-
nodeBinary: process.execPath
|
|
14397
|
-
});
|
|
14398
|
-
}
|
|
14399
|
-
function compareSemver(a, b) {
|
|
14400
|
-
const parseN = (s) => {
|
|
14401
|
-
const parts = s.replace(/^v/, "").split("-")[0].split(".");
|
|
14402
|
-
return [
|
|
14403
|
-
Number.parseInt(parts[0] ?? "0", 10) || 0,
|
|
14404
|
-
Number.parseInt(parts[1] ?? "0", 10) || 0,
|
|
14405
|
-
Number.parseInt(parts[2] ?? "0", 10) || 0
|
|
14406
|
-
];
|
|
14407
|
-
};
|
|
14408
|
-
const av = parseN(a);
|
|
14409
|
-
const bv = parseN(b);
|
|
14410
|
-
for (let i = 0; i < 3; i += 1) {
|
|
14411
|
-
if (av[i] < bv[i]) return -1;
|
|
14412
|
-
if (av[i] > bv[i]) return 1;
|
|
14413
|
-
}
|
|
14414
|
-
return 0;
|
|
14415
|
-
}
|
|
14416
|
-
async function safeReadError(response) {
|
|
14417
|
-
try {
|
|
14418
|
-
const text = await response.text();
|
|
14419
|
-
if (text.length === 0) return "";
|
|
14420
|
-
try {
|
|
14421
|
-
const parsed = JSON.parse(text);
|
|
14422
|
-
if (typeof parsed === "object" && parsed !== null && typeof parsed["error"] === "string") {
|
|
14423
|
-
return parsed["error"];
|
|
14424
|
-
}
|
|
14425
|
-
} catch {
|
|
14426
|
-
}
|
|
14427
|
-
return text.length > 200 ? text.slice(0, 200) + "\u2026" : text;
|
|
14428
|
-
} catch {
|
|
14429
|
-
return "";
|
|
14430
|
-
}
|
|
14431
|
-
}
|
|
14432
|
-
function parsePairResponse(raw) {
|
|
14433
|
-
if (typeof raw !== "object" || raw === null) {
|
|
14434
|
-
throw new Error("expected object");
|
|
14435
|
-
}
|
|
14436
|
-
const obj = raw;
|
|
14437
|
-
const instanceId = obj["instanceId"];
|
|
14438
|
-
const refreshToken = obj["refreshToken"];
|
|
14439
|
-
if (typeof instanceId !== "string" || instanceId.length === 0) {
|
|
14440
|
-
throw new Error("missing instanceId");
|
|
14441
|
-
}
|
|
14442
|
-
if (typeof refreshToken !== "string" || refreshToken.length === 0) {
|
|
14443
|
-
throw new Error("missing refreshToken");
|
|
14444
|
-
}
|
|
14445
|
-
return {
|
|
14446
|
-
instanceId,
|
|
14447
|
-
refreshToken,
|
|
14448
|
-
...typeof obj["jti"] === "string" ? { jti: obj["jti"] } : {},
|
|
14449
|
-
...typeof obj["accessToken"] === "string" ? { accessToken: obj["accessToken"] } : {},
|
|
14450
|
-
...typeof obj["expiresInSec"] === "number" ? { expiresInSec: obj["expiresInSec"] } : {},
|
|
14451
|
-
...Array.isArray(obj["runners"]) ? {
|
|
14452
|
-
runners: obj["runners"].map(parsePairedRunner).filter((runner) => runner !== null)
|
|
14453
|
-
} : {},
|
|
14454
|
-
...typeof obj["requiredCliVersion"] === "string" ? { requiredCliVersion: obj["requiredCliVersion"] } : {},
|
|
14455
|
-
...typeof obj["capabilityAck"] === "object" && obj["capabilityAck"] !== null ? { capabilityAck: parseCapabilityAck(obj["capabilityAck"]) } : {}
|
|
14456
|
-
};
|
|
14457
|
-
}
|
|
14458
|
-
function parseCapabilityAck(raw) {
|
|
14459
|
-
if (typeof raw !== "object" || raw === null) return {};
|
|
14460
|
-
const obj = raw;
|
|
14461
|
-
const ack = {};
|
|
14462
|
-
if (typeof obj["runtimeDaemon"] === "boolean") {
|
|
14463
|
-
ack.runtimeDaemon = obj["runtimeDaemon"];
|
|
14464
|
-
}
|
|
14465
|
-
if (typeof obj["instanceSocket"] === "boolean") {
|
|
14466
|
-
ack.instanceSocket = obj["instanceSocket"];
|
|
14467
|
-
}
|
|
14468
|
-
return ack;
|
|
14469
|
-
}
|
|
14470
|
-
function parsePairedRunner(raw) {
|
|
14471
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
14472
|
-
const obj = raw;
|
|
14473
|
-
const runnerId = obj["runnerId"];
|
|
14474
|
-
if (typeof runnerId !== "string" || runnerId.length === 0) return null;
|
|
14475
|
-
return {
|
|
14476
|
-
runnerId,
|
|
14477
|
-
...typeof obj["name"] === "string" ? { name: obj["name"] } : {},
|
|
14478
|
-
...typeof obj["executionTarget"] === "string" ? { executionTarget: obj["executionTarget"] } : {},
|
|
14479
|
-
...typeof obj["remoteInstanceId"] === "string" ? { remoteInstanceId: obj["remoteInstanceId"] } : {}
|
|
14480
|
-
};
|
|
14481
|
-
}
|
|
14482
|
-
|
|
14483
|
-
// src/app/entry/dashboardCommand.ts
|
|
14484
|
-
var DASHBOARD_DEPRECATION_NOTICE = "drisp dashboard is deprecated and is removed in 0.7.0; use drisp runner instead.";
|
|
14485
|
-
function mapDashboardToRunner(input) {
|
|
14486
|
-
const { subcommand, subcommandArgs, flags } = input;
|
|
14487
|
-
const runner = (next, args = [], extraFlags = {}) => ({
|
|
14488
|
-
kind: "runner",
|
|
14489
|
-
input: {
|
|
14490
|
-
subcommand: next,
|
|
14491
|
-
subcommandArgs: args,
|
|
14492
|
-
flags: { ...flags, ...extraFlags }
|
|
14493
|
-
}
|
|
14494
|
-
});
|
|
14495
|
-
if (subcommand === "" || subcommand === "help" || subcommand === "--help") {
|
|
14496
|
-
return runner("help");
|
|
14497
|
-
}
|
|
14498
|
-
if (subcommand === "connect") return runner("", subcommandArgs);
|
|
14499
|
-
if (subcommand === "daemon") {
|
|
14500
|
-
const [mode, ...rest] = subcommandArgs;
|
|
14501
|
-
switch (mode) {
|
|
14502
|
-
case "foreground":
|
|
14503
|
-
return runner("", rest);
|
|
14504
|
-
case "start":
|
|
14505
|
-
return runner("", rest, { detach: true });
|
|
14506
|
-
case "stop":
|
|
14507
|
-
case "restart":
|
|
14508
|
-
case "install":
|
|
14509
|
-
return runner(mode, rest);
|
|
14510
|
-
case "reload":
|
|
14511
|
-
return {
|
|
14512
|
-
kind: "noop",
|
|
14513
|
-
message: 'runner: reload is a no-op (the runner re-reads its pairing on every reconnect); use "drisp runner restart" to cycle it.'
|
|
14514
|
-
};
|
|
14515
|
-
default:
|
|
14516
|
-
return runner("daemon", subcommandArgs);
|
|
14517
|
-
}
|
|
14518
|
-
}
|
|
14519
|
-
return runner(subcommand, subcommandArgs);
|
|
14520
|
-
}
|
|
14521
|
-
async function runDashboardCommand(input, deps = {}) {
|
|
14522
|
-
const logOut = deps.logOut ?? ((m) => process.stdout.write(m + "\n"));
|
|
14523
|
-
const logError = deps.logError ?? ((m) => process.stderr.write(m + "\n"));
|
|
14524
|
-
logError(DASHBOARD_DEPRECATION_NOTICE);
|
|
14525
|
-
const mapped = mapDashboardToRunner(input);
|
|
14526
|
-
if (mapped.kind === "noop") {
|
|
14527
|
-
logOut(mapped.message);
|
|
14528
|
-
return 0;
|
|
14529
|
-
}
|
|
14530
|
-
return runRunnerCommand(mapped.input, deps);
|
|
13513
|
+
throw new Error(`No Runner on this machine holds Run ${runId}.`);
|
|
14531
13514
|
}
|
|
14532
13515
|
|
|
14533
13516
|
// src/app/entry/doctorCommand.ts
|
|
@@ -14535,9 +13518,9 @@ import process7 from "process";
|
|
|
14535
13518
|
|
|
14536
13519
|
// src/harnesses/claude/system/doctorEnvironment.ts
|
|
14537
13520
|
import { execFileSync } from "child_process";
|
|
14538
|
-
import * as
|
|
14539
|
-
import * as
|
|
14540
|
-
import * as
|
|
13521
|
+
import * as fs5 from "fs";
|
|
13522
|
+
import * as os4 from "os";
|
|
13523
|
+
import * as path5 from "path";
|
|
14541
13524
|
var MANAGED_POLICY_KEYS = [
|
|
14542
13525
|
"forceLoginMethod",
|
|
14543
13526
|
"forceLoginOrgUUID",
|
|
@@ -14633,20 +13616,20 @@ function tryRichAuth(claudeBinary) {
|
|
|
14633
13616
|
}
|
|
14634
13617
|
}
|
|
14635
13618
|
function collectEnvironment(options = {}) {
|
|
14636
|
-
const
|
|
13619
|
+
const platform2 = options.platform ?? process.platform;
|
|
14637
13620
|
const env2 = options.env ?? process.env;
|
|
14638
|
-
const homeDir = options.homeDir ??
|
|
13621
|
+
const homeDir = options.homeDir ?? os4.homedir();
|
|
14639
13622
|
const cwd = options.cwd ?? process.cwd();
|
|
14640
13623
|
const resolveBinary = options.resolveClaudeBinaryFn ?? resolveClaudeBinary;
|
|
14641
13624
|
const detectVersion = options.detectClaudeVersionFn ?? detectClaudeVersion;
|
|
14642
13625
|
const runAuthStatus = options.runClaudeAuthStatusFn ?? runClaudeAuthStatus;
|
|
14643
13626
|
const resolveForwarder = options.resolveHookForwarderCommandFn ?? resolveHookForwarderCommand;
|
|
14644
|
-
const statFn = options.statFn ?? ((p) =>
|
|
14645
|
-
const readFileFn = options.readFileFn ?? ((p) =>
|
|
13627
|
+
const statFn = options.statFn ?? ((p) => fs5.statSync(p));
|
|
13628
|
+
const readFileFn = options.readFileFn ?? ((p) => fs5.readFileSync(p, "utf8"));
|
|
14646
13629
|
const settingsPaths = resolveClaudeSettingsSurfacePaths(
|
|
14647
13630
|
homeDir,
|
|
14648
13631
|
cwd,
|
|
14649
|
-
|
|
13632
|
+
platform2,
|
|
14650
13633
|
env2
|
|
14651
13634
|
);
|
|
14652
13635
|
const claudeBinary = resolveBinary();
|
|
@@ -14687,14 +13670,14 @@ function collectEnvironment(options = {}) {
|
|
|
14687
13670
|
});
|
|
14688
13671
|
let globalConfigPresent = false;
|
|
14689
13672
|
try {
|
|
14690
|
-
statFn(
|
|
13673
|
+
statFn(path5.join(homeDir, ".claude.json"));
|
|
14691
13674
|
globalConfigPresent = true;
|
|
14692
13675
|
} catch {
|
|
14693
13676
|
globalConfigPresent = false;
|
|
14694
13677
|
}
|
|
14695
13678
|
const hookForwarder = resolveForwarder();
|
|
14696
13679
|
return {
|
|
14697
|
-
platform,
|
|
13680
|
+
platform: platform2,
|
|
14698
13681
|
cwd,
|
|
14699
13682
|
claudeBinary,
|
|
14700
13683
|
claudeVersion,
|
|
@@ -15167,20 +14150,20 @@ function runRunsCommand(input) {
|
|
|
15167
14150
|
// src/app/entry/cli.tsx
|
|
15168
14151
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
15169
14152
|
function resolvePackageJsonPath(entryUrl) {
|
|
15170
|
-
let currentDir =
|
|
14153
|
+
let currentDir = path6.dirname(fileURLToPath(entryUrl));
|
|
15171
14154
|
for (; ; ) {
|
|
15172
|
-
const candidatePath =
|
|
15173
|
-
if (
|
|
14155
|
+
const candidatePath = path6.join(currentDir, "package.json");
|
|
14156
|
+
if (fs6.existsSync(candidatePath)) {
|
|
15174
14157
|
return candidatePath;
|
|
15175
14158
|
}
|
|
15176
|
-
const parentDir =
|
|
14159
|
+
const parentDir = path6.dirname(currentDir);
|
|
15177
14160
|
if (parentDir === currentDir) {
|
|
15178
14161
|
throw new Error("Could not locate package.json for CLI metadata.");
|
|
15179
14162
|
}
|
|
15180
14163
|
currentDir = parentDir;
|
|
15181
14164
|
}
|
|
15182
14165
|
}
|
|
15183
|
-
var require2 =
|
|
14166
|
+
var require2 = createRequire(import.meta.url);
|
|
15184
14167
|
var { version } = require2(resolvePackageJsonPath(import.meta.url));
|
|
15185
14168
|
var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
15186
14169
|
"setup",
|
|
@@ -15221,7 +14204,7 @@ async function flushTelemetryOptOut(config) {
|
|
|
15221
14204
|
deviceId: deviceId2,
|
|
15222
14205
|
telemetryEnabled: config.telemetry,
|
|
15223
14206
|
appVersion: version,
|
|
15224
|
-
os: `${
|
|
14207
|
+
os: `${os5.platform()}-${os5.arch()}`
|
|
15225
14208
|
});
|
|
15226
14209
|
trackTelemetryOptedOut();
|
|
15227
14210
|
await shutdownTelemetry();
|
|
@@ -15339,9 +14322,8 @@ var cli = meow(
|
|
|
15339
14322
|
mcp <sub> Manage personal MCP servers (add, remove, list)
|
|
15340
14323
|
skill <sub> Manage personal skills (install, remove, list)
|
|
15341
14324
|
marketplace <sub> Manage marketplace sources (add, refresh, remove, list)
|
|
15342
|
-
runner [sub] Run the runner that
|
|
15343
|
-
(no subcommand: foreground; --
|
|
15344
|
-
logs, runs, install, pair, unpair, refresh, doctor, list)
|
|
14325
|
+
runner [sub] Run the runner that connects this machine to a Team and executes its Runs
|
|
14326
|
+
(no subcommand: foreground; connect [code] --hub <url> [--replace], disconnect [team], status, recover, help)
|
|
15345
14327
|
telemetry [action] Manage anonymous telemetry (enable/disable/status)
|
|
15346
14328
|
doctor Diagnose Claude headless setup (use with --harness=claude)
|
|
15347
14329
|
|
|
@@ -15369,9 +14351,6 @@ var cli = meow(
|
|
|
15369
14351
|
--ephemeral Do not persist Athena session data (run mode)
|
|
15370
14352
|
--timeout-ms Hard timeout for the run in milliseconds
|
|
15371
14353
|
--workflow Override the active workflow for this run only (no config change)
|
|
15372
|
-
--detach Start the runner in the background (runner)
|
|
15373
|
-
--url Dashboard origin (runner pair)
|
|
15374
|
-
--name Friendly machine name (runner pair)
|
|
15375
14354
|
--dry-run Print resolved bootstrap (workflow, isolation, plugins, harness) and exit (run mode)
|
|
15376
14355
|
--project Scope workflow command to project config (workflow use)
|
|
15377
14356
|
--global Scope workflow command to global config (workflow use, default)
|
|
@@ -15466,33 +14445,21 @@ var cli = meow(
|
|
|
15466
14445
|
workflow: {
|
|
15467
14446
|
type: "string"
|
|
15468
14447
|
},
|
|
15469
|
-
|
|
15470
|
-
type: "string"
|
|
15471
|
-
},
|
|
15472
|
-
name: {
|
|
15473
|
-
type: "string"
|
|
15474
|
-
},
|
|
15475
|
-
runner: {
|
|
14448
|
+
hub: {
|
|
15476
14449
|
type: "string"
|
|
15477
14450
|
},
|
|
15478
|
-
|
|
15479
|
-
type: "number"
|
|
15480
|
-
},
|
|
15481
|
-
follow: {
|
|
14451
|
+
foreground: {
|
|
15482
14452
|
type: "boolean",
|
|
15483
14453
|
default: false
|
|
15484
14454
|
},
|
|
15485
|
-
|
|
14455
|
+
replace: {
|
|
15486
14456
|
type: "boolean",
|
|
15487
14457
|
default: false
|
|
15488
14458
|
},
|
|
15489
|
-
|
|
14459
|
+
detach: {
|
|
15490
14460
|
type: "boolean",
|
|
15491
14461
|
default: false
|
|
15492
14462
|
},
|
|
15493
|
-
limit: {
|
|
15494
|
-
type: "number"
|
|
15495
|
-
},
|
|
15496
14463
|
dryRun: {
|
|
15497
14464
|
type: "boolean",
|
|
15498
14465
|
default: false
|
|
@@ -15520,7 +14487,7 @@ var cli = meow(
|
|
|
15520
14487
|
}
|
|
15521
14488
|
);
|
|
15522
14489
|
async function main() {
|
|
15523
|
-
const projectDir =
|
|
14490
|
+
const projectDir = path6.resolve(cli.flags.projectDir);
|
|
15524
14491
|
const [command, ...commandArgs] = cli.input;
|
|
15525
14492
|
const isRunCommand = command === "run" || command === "exec";
|
|
15526
14493
|
if (command && !KNOWN_COMMANDS.has(command)) {
|
|
@@ -15570,7 +14537,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
15570
14537
|
await exitWith(1);
|
|
15571
14538
|
return;
|
|
15572
14539
|
}
|
|
15573
|
-
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-
|
|
14540
|
+
const { default: WorkflowInstallWizard } = await import("./WorkflowInstallWizard-TQ4SF5KB.js");
|
|
15574
14541
|
const { waitUntilExit } = render(
|
|
15575
14542
|
/* @__PURE__ */ jsx24(
|
|
15576
14543
|
WorkflowInstallWizard,
|
|
@@ -15632,23 +14599,18 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
15632
14599
|
}
|
|
15633
14600
|
if (command === "runner" || command === "dashboard") {
|
|
15634
14601
|
const [subcommand = "", ...subcommandArgs] = commandArgs;
|
|
15635
|
-
const input = {
|
|
15636
|
-
subcommand,
|
|
15637
|
-
subcommandArgs,
|
|
15638
|
-
flags: {
|
|
15639
|
-
url: typeof cli.flags.url === "string" ? cli.flags.url : void 0,
|
|
15640
|
-
name: typeof cli.flags.name === "string" ? cli.flags.name : void 0,
|
|
15641
|
-
runner: typeof cli.flags.runner === "string" ? cli.flags.runner : void 0,
|
|
15642
|
-
json: Boolean(cli.flags.json),
|
|
15643
|
-
...cli.flags.detach ? { detach: true } : {},
|
|
15644
|
-
...typeof cli.flags.tail === "number" ? { tail: cli.flags.tail } : {},
|
|
15645
|
-
...cli.flags.follow ? { follow: true } : {},
|
|
15646
|
-
...cli.flags.active ? { active: true } : {},
|
|
15647
|
-
...typeof cli.flags.limit === "number" ? { limit: cli.flags.limit } : {}
|
|
15648
|
-
}
|
|
15649
|
-
};
|
|
15650
14602
|
await exitWith(
|
|
15651
|
-
|
|
14603
|
+
await runRunnerCommandV2({
|
|
14604
|
+
subcommand,
|
|
14605
|
+
subcommandArgs,
|
|
14606
|
+
flags: {
|
|
14607
|
+
json: Boolean(cli.flags.json),
|
|
14608
|
+
...cli.flags.detach ? { detach: true } : {},
|
|
14609
|
+
...typeof cli.flags.hub === "string" ? { hub: cli.flags.hub } : {},
|
|
14610
|
+
...cli.flags.foreground ? { foreground: true } : {},
|
|
14611
|
+
...cli.flags.replace ? { replace: true } : {}
|
|
14612
|
+
}
|
|
14613
|
+
})
|
|
15652
14614
|
);
|
|
15653
14615
|
return;
|
|
15654
14616
|
}
|
|
@@ -15725,8 +14687,8 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
15725
14687
|
const showSetup = isRunCommand ? false : shouldShowSetup({
|
|
15726
14688
|
cliInput: cli.input,
|
|
15727
14689
|
setupComplete: globalConfig.setupComplete,
|
|
15728
|
-
globalConfigExists:
|
|
15729
|
-
|
|
14690
|
+
globalConfigExists: fs6.existsSync(
|
|
14691
|
+
path6.join(os5.homedir(), ".config", "athena", "config.json")
|
|
15730
14692
|
)
|
|
15731
14693
|
});
|
|
15732
14694
|
let runtimeConfig;
|
|
@@ -15783,7 +14745,7 @@ Available commands: ${[...KNOWN_COMMANDS].join(", ")}`
|
|
|
15783
14745
|
deviceId: resolvedDeviceId,
|
|
15784
14746
|
telemetryEnabled: globalConfig.telemetry,
|
|
15785
14747
|
appVersion: version,
|
|
15786
|
-
os: `${
|
|
14748
|
+
os: `${os5.platform()}-${os5.arch()}`
|
|
15787
14749
|
});
|
|
15788
14750
|
trackAppLaunched({ version, harness: runtimeConfig.harness });
|
|
15789
14751
|
if (!isRunCommand && isFirstRun && globalConfig.telemetry !== false) {
|