nolo-cli 0.8.0-alpha.2 → 0.8.0-alpha.21
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 +1 -1
- package/_executeModel-42U3VOVZ.js +205 -0
- package/action-TLI3V62L.js +28 -0
- package/addContentAction-57RSIIMR.js +26 -0
- package/addDialogAgentAction-G3VWEA6E.js +68 -0
- package/agentDeleteCommand-5DD2IM3J.js +327 -0
- package/agentEmailCommands-6FACJBU4.js +628 -0
- package/agentListCommands-I6KZYCIG.js +223 -0
- package/agentMachineCommands-VKLIGMZA.js +425 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-7TZICYDT.js +96 -0
- package/agentRunCommand-L4OM23KV.js +114 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-ZYEIPTY4.js +104 -0
- package/agentSlice-BX7GXSNO.js +42 -0
- package/authCommand-EB5JSOX5.js +581 -0
- package/authCommands-XPEHTKSM.js +35 -0
- package/chunk-25LCXTML.js +175 -0
- package/chunk-2ECQG4YX.js +22296 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-2JCXITPK.js +1078 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4O3CARJ4.js +231 -0
- package/chunk-4QEWT7KD.js +933 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-4XENYTF5.js +222 -0
- package/chunk-4ZDM73SD.js +34 -0
- package/chunk-5CS33IBL.js +301 -0
- package/chunk-5EFXE3O7.js +138 -0
- package/chunk-5KLGBAAR.js +386 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6KHNO3SK.js +1948 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7DAJ4AI4.js +191 -0
- package/chunk-7GNGZRLZ.js +1939 -0
- package/chunk-7KCUSYT2.js +354 -0
- package/chunk-7U44LYTR.js +238 -0
- package/chunk-AFON4QUU.js +112 -0
- package/chunk-AGFH73SK.js +239 -0
- package/chunk-AL2KDGR2.js +27339 -0
- package/chunk-AS4PVWA4.js +221 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BBYVOSU2.js +792 -0
- package/chunk-C3RUNRHT.js +51 -0
- package/chunk-C4FTUXVF.js +146 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-CMKUBIGW.js +1550 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DCOA4QSN.js +105 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DSZUWUYB.js +478 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-ENOG22KJ.js +939 -0
- package/chunk-FSO5B7GH.js +171 -0
- package/chunk-FVYY6MCS.js +1447 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GU3PEVGZ.js +4723 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-HNW7IJSK.js +71 -0
- package/chunk-I42VC5KR.js +15 -0
- package/chunk-I6XMPTMR.js +113 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IWX4UW2E.js +548 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JEXVIMYZ.js +55 -0
- package/chunk-JRKKFRAV.js +537 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-LTRXGFMS.js +83 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-MTU75CZ2.js +91 -0
- package/chunk-N54XAP77.js +19 -0
- package/chunk-O2ZNTTET.js +3272 -0
- package/chunk-O4BU24WZ.js +3212 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OOSFSFMA.js +271 -0
- package/chunk-OPZYGHUC.js +89 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-P3PKED2I.js +962 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-RCSJFQPB.js +9036 -0
- package/chunk-RL776AGR.js +190 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SDJDLO5W.js +628 -0
- package/chunk-SOTZ6RDN.js +69 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-UB6SN4NJ.js +421 -0
- package/chunk-URUUZLPA.js +807 -0
- package/chunk-UTLZINAP.js +3066 -0
- package/chunk-VC6OZXO5.js +298 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-VUW5FBDB.js +573 -0
- package/chunk-VVX233PH.js +482 -0
- package/chunk-VX3SXJ3N.js +2052 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-WZSRQNQG.js +2587 -0
- package/chunk-XBUBEFNX.js +34 -0
- package/chunk-XENYHRUE.js +104 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YPRZSPF3.js +148 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/cliExecutor-K3F7DMRO.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-RJF3N2QY.js +866 -0
- package/createDialogAction-DRSPS6CE.js +55 -0
- package/createPageAction-6B2WMNO3.js +172 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-Q3TM2FJV.js +60 -0
- package/deleteFile-OW53A6BX.js +41 -0
- package/dialogCommands-4IX4XIZE.js +63 -0
- package/dialogSlice-3M7KX2TX.js +148 -0
- package/docCreateCommands-SIL3NPGT.js +220 -0
- package/docDeleteCommands-7IOMLUS4.js +181 -0
- package/docReadCommands-FNFXUA5L.js +134 -0
- package/docStore-SV7E34D4.js +98 -0
- package/docUpdateCommands-X2HNDVUC.js +318 -0
- package/fetchMessages-KTNM7QAR.js +17 -0
- package/getPricing-3E6UNPKL.js +30 -0
- package/handleSendMessageAction-JYETO5RZ.js +185 -0
- package/index.js +188 -112958
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-2VRI24F4.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-RIO3RP5Q.js +781 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-W7UYLGB5.js +112 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-4OPOOLFS.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +4 -24
- package/read-GW7ZEI23.js +17 -0
- package/readDocTool-HUFU6OCT.js +16 -0
- package/readlineWorkspace-27JR7WPW.js +5682 -0
- package/removeDialogAgentAction-CXKR4NJI.js +73 -0
- package/runAgentClientLoop-HZFAPJHS.js +293 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-6RKUSXHX.js +60 -0
- package/setPrimaryDialogAgentAction-T3UTBEFG.js +80 -0
- package/settingSlice-IVKXJXMG.js +199 -0
- package/skillDiscovery-UGSQGX2U.js +16 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-E7VOF4LO.js +450 -0
- package/spaceSlice-TSVROA5U.js +149 -0
- package/streamAgentChatTurn-3C5RQZJF.js +4510 -0
- package/tableCommands-O7O2LBEG.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-EQDEKR6N.js +87 -0
- package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
- package/understanding-3CUQQ6SV.js +271 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-3XV7VWSZ.js +188 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildMachinePermissionPromptBlock,
|
|
3
|
+
handleConnectorRunMessage,
|
|
4
|
+
resolveMachineRunPermissionPolicy
|
|
5
|
+
} from "./chunk-2JCXITPK.js";
|
|
6
|
+
import {
|
|
7
|
+
isConnectorWebSocketAuthError,
|
|
8
|
+
isConnectorWebSocketRetryableError,
|
|
9
|
+
resolveConnectorWebSocketTarget
|
|
10
|
+
} from "./chunk-AVMKZHBC.js";
|
|
11
|
+
import {
|
|
12
|
+
detectMachineInfo
|
|
13
|
+
} from "./chunk-6HYVJV7B.js";
|
|
14
|
+
import "./chunk-RCSJFQPB.js";
|
|
15
|
+
import "./chunk-EHLHSX7R.js";
|
|
16
|
+
import "./chunk-N54XAP77.js";
|
|
17
|
+
import "./chunk-VHBWHNVT.js";
|
|
18
|
+
import "./chunk-4O3CARJ4.js";
|
|
19
|
+
import "./chunk-CMKUBIGW.js";
|
|
20
|
+
import "./chunk-JBTJAPUF.js";
|
|
21
|
+
import "./chunk-ATNTHIZI.js";
|
|
22
|
+
import "./chunk-FSO5B7GH.js";
|
|
23
|
+
import "./chunk-C3RUNRHT.js";
|
|
24
|
+
import "./chunk-AL2KDGR2.js";
|
|
25
|
+
import "./chunk-GAKEBQD6.js";
|
|
26
|
+
import "./chunk-KQHGZWSV.js";
|
|
27
|
+
import "./chunk-AS4PVWA4.js";
|
|
28
|
+
import "./chunk-3LY4F7SV.js";
|
|
29
|
+
import "./chunk-VP4WJWKW.js";
|
|
30
|
+
import "./chunk-63W5N6P3.js";
|
|
31
|
+
import "./chunk-VF4K2QE6.js";
|
|
32
|
+
import "./chunk-UB6SN4NJ.js";
|
|
33
|
+
import "./chunk-ENOG22KJ.js";
|
|
34
|
+
import "./chunk-VC6OZXO5.js";
|
|
35
|
+
import "./chunk-OWOHNGCN.js";
|
|
36
|
+
import "./chunk-25LCXTML.js";
|
|
37
|
+
import "./chunk-XENYHRUE.js";
|
|
38
|
+
import "./chunk-2ECQG4YX.js";
|
|
39
|
+
import "./chunk-4ZDM73SD.js";
|
|
40
|
+
import "./chunk-MRC4Q34M.js";
|
|
41
|
+
import "./chunk-MTU75CZ2.js";
|
|
42
|
+
import "./chunk-CWMSB67P.js";
|
|
43
|
+
import "./chunk-VUW5FBDB.js";
|
|
44
|
+
import "./chunk-5EFXE3O7.js";
|
|
45
|
+
import "./chunk-Z4NR32XR.js";
|
|
46
|
+
import "./chunk-M76ECQRD.js";
|
|
47
|
+
import "./chunk-SOTZ6RDN.js";
|
|
48
|
+
import "./chunk-I6XMPTMR.js";
|
|
49
|
+
import "./chunk-UAD2WT54.js";
|
|
50
|
+
import "./chunk-DCOA4QSN.js";
|
|
51
|
+
import "./chunk-IDZTNRP2.js";
|
|
52
|
+
import {
|
|
53
|
+
isCompiledBinary
|
|
54
|
+
} from "./chunk-ISNCLGUZ.js";
|
|
55
|
+
import {
|
|
56
|
+
DEFAULT_NOLO_SERVER_URL
|
|
57
|
+
} from "./chunk-OLZBQWZU.js";
|
|
58
|
+
import "./chunk-WZSRQNQG.js";
|
|
59
|
+
import "./chunk-O2ZNTTET.js";
|
|
60
|
+
import "./chunk-5KLGBAAR.js";
|
|
61
|
+
import "./chunk-ZNFJWEN4.js";
|
|
62
|
+
import "./chunk-TILFE6LV.js";
|
|
63
|
+
import "./chunk-4QEWT7KD.js";
|
|
64
|
+
import "./chunk-OPZYGHUC.js";
|
|
65
|
+
import "./chunk-AGFH73SK.js";
|
|
66
|
+
import "./chunk-JRKKFRAV.js";
|
|
67
|
+
import "./chunk-P3PKED2I.js";
|
|
68
|
+
import "./chunk-5MMAK75Q.js";
|
|
69
|
+
import "./chunk-BBYVOSU2.js";
|
|
70
|
+
import "./chunk-5CS33IBL.js";
|
|
71
|
+
import "./chunk-JZPBVBIY.js";
|
|
72
|
+
import "./chunk-IWX4UW2E.js";
|
|
73
|
+
import "./chunk-5MIDPSSK.js";
|
|
74
|
+
import "./chunk-DFFIZ25G.js";
|
|
75
|
+
import "./chunk-3ASDTTEI.js";
|
|
76
|
+
import "./chunk-DWLEOOGR.js";
|
|
77
|
+
import {
|
|
78
|
+
toErrorMessage
|
|
79
|
+
} from "./chunk-HNMXYYJR.js";
|
|
80
|
+
import "./chunk-H33743XH.js";
|
|
81
|
+
import "./chunk-LM6IYQ3F.js";
|
|
82
|
+
import "./chunk-VX3SXJ3N.js";
|
|
83
|
+
import {
|
|
84
|
+
parsePositiveFiniteNumberOrFallback
|
|
85
|
+
} from "./chunk-Y5GCAMGP.js";
|
|
86
|
+
import "./chunk-URUUZLPA.js";
|
|
87
|
+
import "./chunk-AZPCASRE.js";
|
|
88
|
+
import "./chunk-7U44LYTR.js";
|
|
89
|
+
import "./chunk-KX6YBIHG.js";
|
|
90
|
+
import "./chunk-O4BU24WZ.js";
|
|
91
|
+
import "./chunk-I42VC5KR.js";
|
|
92
|
+
import "./chunk-WWC7I6TA.js";
|
|
93
|
+
import "./chunk-QSJ3HFEF.js";
|
|
94
|
+
import "./chunk-DSZUWUYB.js";
|
|
95
|
+
import "./chunk-UTLZINAP.js";
|
|
96
|
+
import "./chunk-OOSFSFMA.js";
|
|
97
|
+
import "./chunk-GBRG75BQ.js";
|
|
98
|
+
import "./chunk-4SN6J65J.js";
|
|
99
|
+
import "./chunk-LCBAMY2M.js";
|
|
100
|
+
import {
|
|
101
|
+
asTrimmedString
|
|
102
|
+
} from "./chunk-3EUW3WEO.js";
|
|
103
|
+
import "./chunk-U6VC4MU4.js";
|
|
104
|
+
import {
|
|
105
|
+
normalizeServerOrigin
|
|
106
|
+
} from "./chunk-CF7PE763.js";
|
|
107
|
+
import "./chunk-G3SUCT4W.js";
|
|
108
|
+
import {
|
|
109
|
+
isRecord
|
|
110
|
+
} from "./chunk-KUG52IAP.js";
|
|
111
|
+
import "./chunk-4VNS5WPM.js";
|
|
112
|
+
|
|
113
|
+
// packages/connector-experimental/heartbeatLoop.ts
|
|
114
|
+
var defaultSleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
115
|
+
async function runHeartbeatLoop(options) {
|
|
116
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
117
|
+
let beats = 0;
|
|
118
|
+
while (!options.signal?.aborted) {
|
|
119
|
+
await options.sendHeartbeat();
|
|
120
|
+
beats += 1;
|
|
121
|
+
if (options.maxBeats && beats >= options.maxBeats) return;
|
|
122
|
+
if (options.signal?.aborted) return;
|
|
123
|
+
await sleep(options.intervalMs);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// packages/cli/machineDaemonCommands.ts
|
|
128
|
+
import { spawn } from "node:child_process";
|
|
129
|
+
import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
130
|
+
import { homedir } from "node:os";
|
|
131
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
132
|
+
import { fileURLToPath } from "node:url";
|
|
133
|
+
var CONNECTOR_RUNTIME_WORKSPACE_PACKAGES = /* @__PURE__ */ new Set([
|
|
134
|
+
"connector-experimental",
|
|
135
|
+
"ai"
|
|
136
|
+
]);
|
|
137
|
+
function readJsonFile(path) {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function getWorkspacePatterns(packageJson) {
|
|
145
|
+
if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
|
|
146
|
+
return packageJson.workspaces?.packages ?? [];
|
|
147
|
+
}
|
|
148
|
+
function normalizePath(path) {
|
|
149
|
+
const resolved = resolve(path);
|
|
150
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
151
|
+
}
|
|
152
|
+
function isInsideOrSame(path, parent) {
|
|
153
|
+
const relativePath = relative(parent, path);
|
|
154
|
+
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
155
|
+
}
|
|
156
|
+
function findWorkspaceRoot(startPath) {
|
|
157
|
+
let current = resolve(startPath);
|
|
158
|
+
while (true) {
|
|
159
|
+
const packageJsonPath = join(current, "package.json");
|
|
160
|
+
if (existsSync(packageJsonPath)) {
|
|
161
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
162
|
+
if (packageJson && getWorkspacePatterns(packageJson).length > 0) {
|
|
163
|
+
return current;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const parent = dirname(current);
|
|
167
|
+
if (parent === current) return null;
|
|
168
|
+
current = parent;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function listWorkspacePackageJsonFiles(workspaceParent) {
|
|
172
|
+
let entries;
|
|
173
|
+
try {
|
|
174
|
+
entries = readdirSync(workspaceParent, { withFileTypes: true });
|
|
175
|
+
} catch {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
const results = [];
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
181
|
+
const relativePath = `${entry.name}/package.json`;
|
|
182
|
+
if (existsSync(join(workspaceParent, relativePath))) {
|
|
183
|
+
results.push(relativePath);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return results;
|
|
187
|
+
}
|
|
188
|
+
function validateConnectorWorkspaceLinks(cwd) {
|
|
189
|
+
const repoRoot = findWorkspaceRoot(cwd);
|
|
190
|
+
if (!repoRoot) return [];
|
|
191
|
+
const rootPackageJson = readJsonFile(join(repoRoot, "package.json"));
|
|
192
|
+
if (!rootPackageJson) return [];
|
|
193
|
+
const normalizedRepoRoot = normalizePath(realpathSync(repoRoot));
|
|
194
|
+
const errors = [];
|
|
195
|
+
for (const pattern of getWorkspacePatterns(rootPackageJson)) {
|
|
196
|
+
if (!pattern.endsWith("/*")) continue;
|
|
197
|
+
const workspaceParent = join(repoRoot, pattern.slice(0, -2));
|
|
198
|
+
if (!existsSync(workspaceParent)) continue;
|
|
199
|
+
for (const entry of listWorkspacePackageJsonFiles(workspaceParent)) {
|
|
200
|
+
const packageDir = join(workspaceParent, dirname(entry));
|
|
201
|
+
const packageJson = readJsonFile(join(workspaceParent, entry));
|
|
202
|
+
if (!packageJson?.name || !CONNECTOR_RUNTIME_WORKSPACE_PACKAGES.has(packageJson.name)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const linkPath = join(repoRoot, "node_modules", ...packageJson.name.split("/"));
|
|
206
|
+
if (!existsSync(linkPath)) {
|
|
207
|
+
errors.push(`${linkPath} is missing. Run bun install in ${repoRoot}.`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const actualPath = normalizePath(realpathSync(linkPath));
|
|
211
|
+
const expectedPath = normalizePath(realpathSync(packageDir));
|
|
212
|
+
if (!isInsideOrSame(actualPath, normalizedRepoRoot)) {
|
|
213
|
+
errors.push(
|
|
214
|
+
`${linkPath} points outside this checkout: ${actualPath}. Run bun install in ${repoRoot}.`
|
|
215
|
+
);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (actualPath !== expectedPath) {
|
|
219
|
+
errors.push(
|
|
220
|
+
`${linkPath} points at ${actualPath}, expected ${expectedPath}. Run bun install in ${repoRoot}.`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return errors;
|
|
226
|
+
}
|
|
227
|
+
async function checkConnectorWorkspaceLinks(cwd, output, validateWorkspaceLinks) {
|
|
228
|
+
const errors = await validateWorkspaceLinks(cwd);
|
|
229
|
+
if (errors.length === 0) return true;
|
|
230
|
+
output.write(
|
|
231
|
+
[
|
|
232
|
+
"[nolo] Connector workspace package links are unsafe.",
|
|
233
|
+
"The connector would import workspace packages from a different checkout, so machine capabilities and runtime behavior may be stale.",
|
|
234
|
+
...errors,
|
|
235
|
+
""
|
|
236
|
+
].join("\n")
|
|
237
|
+
);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
function resolveDaemonLogPath(env) {
|
|
241
|
+
return env.NOLO_CONNECT_LOG || join(homedir(), ".nolo", "logs", "connector.log");
|
|
242
|
+
}
|
|
243
|
+
function buildDaemonCommand(cliEntrypointPath) {
|
|
244
|
+
const execPath = process.execPath;
|
|
245
|
+
const entrypoint = cliEntrypointPath || fileURLToPath(import.meta.url);
|
|
246
|
+
if (isCompiledBinary() || entrypoint === execPath) {
|
|
247
|
+
return [execPath, "connect", "--ws"];
|
|
248
|
+
}
|
|
249
|
+
return [execPath, entrypoint, "connect", "--ws"];
|
|
250
|
+
}
|
|
251
|
+
function defaultSpawnDaemon(args) {
|
|
252
|
+
mkdirSync(dirname(args.logPath), { recursive: true });
|
|
253
|
+
const out = openSync(args.logPath, "a");
|
|
254
|
+
const env = Object.fromEntries(
|
|
255
|
+
Object.entries(args.env).filter((entry) => typeof entry[1] === "string")
|
|
256
|
+
);
|
|
257
|
+
const proc = spawn(args.cmd[0], args.cmd.slice(1), {
|
|
258
|
+
cwd: args.cwd,
|
|
259
|
+
env,
|
|
260
|
+
detached: true,
|
|
261
|
+
stdio: ["ignore", out, out]
|
|
262
|
+
});
|
|
263
|
+
proc.unref();
|
|
264
|
+
return { pid: proc.pid };
|
|
265
|
+
}
|
|
266
|
+
async function runMachineDaemonCommand(deps = {}) {
|
|
267
|
+
const env = deps.env ?? process.env;
|
|
268
|
+
const output = deps.output ?? process.stdout;
|
|
269
|
+
const validateWorkspaceLinks = deps.validateWorkspaceLinks ?? validateConnectorWorkspaceLinks;
|
|
270
|
+
if (!await checkConnectorWorkspaceLinks(process.cwd(), output, validateWorkspaceLinks)) {
|
|
271
|
+
return 1;
|
|
272
|
+
}
|
|
273
|
+
const logPath = resolveDaemonLogPath(env);
|
|
274
|
+
const result = (deps.spawnDaemon ?? defaultSpawnDaemon)({
|
|
275
|
+
cmd: buildDaemonCommand(deps.cliEntrypointPath),
|
|
276
|
+
cwd: process.cwd(),
|
|
277
|
+
env,
|
|
278
|
+
logPath
|
|
279
|
+
});
|
|
280
|
+
output.write(`Connector daemon started${result.pid ? ` pid=${result.pid}` : ""}. Log: ${logPath}
|
|
281
|
+
`);
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// packages/cli/machineHeartbeatCommands.ts
|
|
286
|
+
async function runMachineHeartbeatConnectCommand(deps) {
|
|
287
|
+
const output = deps.output ?? process.stdout;
|
|
288
|
+
const machine = deps.machine;
|
|
289
|
+
try {
|
|
290
|
+
await deps.sendHeartbeat();
|
|
291
|
+
} catch (error) {
|
|
292
|
+
output.write(
|
|
293
|
+
`[nolo] Machine connect failed: ${toErrorMessage(error)}
|
|
294
|
+
`
|
|
295
|
+
);
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
output.write(`Connected machine: ${machine.name} (${machine.platform}/${machine.arch})
|
|
299
|
+
`);
|
|
300
|
+
return 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// packages/cli/machineWatchCommands.ts
|
|
304
|
+
function resolveHeartbeatIntervalMs(env) {
|
|
305
|
+
return parsePositiveFiniteNumberOrFallback(
|
|
306
|
+
env.NOLO_CONNECT_HEARTBEAT_MS,
|
|
307
|
+
3e4
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
async function runMachineWatchCommand(deps) {
|
|
311
|
+
const env = deps.env ?? process.env;
|
|
312
|
+
const output = deps.output ?? process.stdout;
|
|
313
|
+
const machine = deps.machine;
|
|
314
|
+
output.write(`Connecting machine heartbeat loop: ${machine.name} (${machine.platform}/${machine.arch})
|
|
315
|
+
`);
|
|
316
|
+
try {
|
|
317
|
+
await deps.runHeartbeatLoop({
|
|
318
|
+
intervalMs: resolveHeartbeatIntervalMs(env),
|
|
319
|
+
sendHeartbeat: deps.sendHeartbeat
|
|
320
|
+
});
|
|
321
|
+
return 0;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
output.write(
|
|
324
|
+
`[nolo] Machine heartbeat loop failed: ${toErrorMessage(error)}
|
|
325
|
+
`
|
|
326
|
+
);
|
|
327
|
+
return 1;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// packages/cli/machineWsSession.ts
|
|
332
|
+
var CONNECTOR_WS_KEEPALIVE_MS = 25e3;
|
|
333
|
+
function buildConnectorKeepaliveMessage() {
|
|
334
|
+
return JSON.stringify({ type: "connector.keepalive", sentAt: Date.now() });
|
|
335
|
+
}
|
|
336
|
+
function resolveHeartbeatIntervalMs2(env) {
|
|
337
|
+
return parsePositiveFiniteNumberOrFallback(
|
|
338
|
+
env.NOLO_CONNECT_HEARTBEAT_MS,
|
|
339
|
+
3e4
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
async function defaultConnectWebSocket(url, options) {
|
|
343
|
+
const WebSocketCtor = globalThis.WebSocket;
|
|
344
|
+
if (!WebSocketCtor) {
|
|
345
|
+
throw new Error("WebSocket is not available in this runtime");
|
|
346
|
+
}
|
|
347
|
+
await new Promise((resolve2, reject) => {
|
|
348
|
+
const ws = new WebSocketCtor(url, {
|
|
349
|
+
headers: options.headers
|
|
350
|
+
});
|
|
351
|
+
let opened = false;
|
|
352
|
+
let aborted = options.signal?.aborted ?? false;
|
|
353
|
+
let keepalive = null;
|
|
354
|
+
const clearKeepalive = () => {
|
|
355
|
+
if (keepalive) {
|
|
356
|
+
clearInterval(keepalive);
|
|
357
|
+
keepalive = null;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
const handleAbort = () => {
|
|
361
|
+
aborted = true;
|
|
362
|
+
clearKeepalive();
|
|
363
|
+
try {
|
|
364
|
+
ws.close();
|
|
365
|
+
} catch {
|
|
366
|
+
resolve2();
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
options.signal?.addEventListener("abort", handleAbort, { once: true });
|
|
370
|
+
ws.addEventListener(
|
|
371
|
+
"open",
|
|
372
|
+
() => {
|
|
373
|
+
opened = true;
|
|
374
|
+
keepalive = setInterval(() => {
|
|
375
|
+
try {
|
|
376
|
+
ws.send(buildConnectorKeepaliveMessage());
|
|
377
|
+
} catch {
|
|
378
|
+
clearKeepalive();
|
|
379
|
+
}
|
|
380
|
+
}, CONNECTOR_WS_KEEPALIVE_MS);
|
|
381
|
+
},
|
|
382
|
+
{ once: true }
|
|
383
|
+
);
|
|
384
|
+
ws.addEventListener("error", () => {
|
|
385
|
+
clearKeepalive();
|
|
386
|
+
if (aborted) {
|
|
387
|
+
resolve2();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
reject(new Error("connector websocket failed"));
|
|
391
|
+
});
|
|
392
|
+
ws.addEventListener(
|
|
393
|
+
"close",
|
|
394
|
+
() => {
|
|
395
|
+
clearKeepalive();
|
|
396
|
+
if (opened || aborted) resolve2();
|
|
397
|
+
else reject(new Error("connector websocket closed before opening"));
|
|
398
|
+
},
|
|
399
|
+
{ once: true }
|
|
400
|
+
);
|
|
401
|
+
ws.addEventListener("message", (event) => {
|
|
402
|
+
const startIndex = options.sentMessages.length;
|
|
403
|
+
Promise.resolve(options.onMessage(String(event.data))).then(() => {
|
|
404
|
+
for (const message of options.sentMessages.slice(startIndex)) {
|
|
405
|
+
ws.send(message);
|
|
406
|
+
}
|
|
407
|
+
}).catch(() => void 0);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
async function runMachineWsSession(options) {
|
|
412
|
+
const sentMessages = [];
|
|
413
|
+
const heartbeatAbort = new AbortController();
|
|
414
|
+
let heartbeatLoopPromise = null;
|
|
415
|
+
let websocketPromise = null;
|
|
416
|
+
try {
|
|
417
|
+
if (options.signal?.aborted) {
|
|
418
|
+
return { exitCode: 0 };
|
|
419
|
+
}
|
|
420
|
+
await options.sendHeartbeat();
|
|
421
|
+
options.output.write(
|
|
422
|
+
`Connector websocket connected: ${options.machine.name} (${options.machine.machineId})
|
|
423
|
+
`
|
|
424
|
+
);
|
|
425
|
+
heartbeatLoopPromise = options.runHeartbeatLoop({
|
|
426
|
+
intervalMs: resolveHeartbeatIntervalMs2(options.env),
|
|
427
|
+
sendHeartbeat: options.sendHeartbeat,
|
|
428
|
+
signal: heartbeatAbort.signal
|
|
429
|
+
});
|
|
430
|
+
const heartbeatMonitorPromise = heartbeatLoopPromise.then(() => {
|
|
431
|
+
if (!heartbeatAbort.signal.aborted) {
|
|
432
|
+
throw new Error("connector heartbeat loop ended unexpectedly");
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
const wsTarget = await options.resolveConnectorWebSocketTarget({
|
|
436
|
+
serverUrl: options.serverUrl,
|
|
437
|
+
machineId: options.machine.machineId,
|
|
438
|
+
connectorSurface: "cli",
|
|
439
|
+
headers: { Authorization: `Bearer ${options.authToken}` }
|
|
440
|
+
});
|
|
441
|
+
websocketPromise = options.connectWebSocket(wsTarget, {
|
|
442
|
+
headers: { Authorization: `Bearer ${options.authToken}` },
|
|
443
|
+
signal: options.signal,
|
|
444
|
+
sentMessages,
|
|
445
|
+
onMessage: (message) => options.onMessage(message, (response) => {
|
|
446
|
+
sentMessages.push(response);
|
|
447
|
+
})
|
|
448
|
+
});
|
|
449
|
+
const abortPromise = options.signal ? new Promise((resolve2) => {
|
|
450
|
+
if (options.signal?.aborted) {
|
|
451
|
+
resolve2("aborted");
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
options.signal?.addEventListener("abort", () => resolve2("aborted"), { once: true });
|
|
455
|
+
}) : null;
|
|
456
|
+
const raceResult = await Promise.race(
|
|
457
|
+
[websocketPromise, heartbeatMonitorPromise, abortPromise].filter(Boolean)
|
|
458
|
+
);
|
|
459
|
+
heartbeatAbort.abort();
|
|
460
|
+
await Promise.allSettled([websocketPromise, heartbeatLoopPromise]);
|
|
461
|
+
if (raceResult === "aborted") {
|
|
462
|
+
return { exitCode: 0 };
|
|
463
|
+
}
|
|
464
|
+
return { exitCode: 0 };
|
|
465
|
+
} catch (error) {
|
|
466
|
+
heartbeatAbort.abort();
|
|
467
|
+
await Promise.allSettled(
|
|
468
|
+
[websocketPromise, heartbeatLoopPromise].filter(Boolean)
|
|
469
|
+
);
|
|
470
|
+
if (isConnectorWebSocketAuthError(error)) {
|
|
471
|
+
options.output.write(
|
|
472
|
+
`[nolo] Connector websocket auth failed: ${error.message}. Re-register this machine or update NOLO_MACHINE_API_KEY.
|
|
473
|
+
`
|
|
474
|
+
);
|
|
475
|
+
return { exitCode: 2 };
|
|
476
|
+
}
|
|
477
|
+
if (isConnectorWebSocketRetryableError(error)) {
|
|
478
|
+
const description = error.reason === "core_draining" ? "core draining" : error.reason;
|
|
479
|
+
options.output.write(
|
|
480
|
+
`[nolo] Connector websocket unavailable: ${description} (${error.message}).
|
|
481
|
+
`
|
|
482
|
+
);
|
|
483
|
+
return {
|
|
484
|
+
exitCode: 1,
|
|
485
|
+
retryAfterMs: error.retryAfterMs,
|
|
486
|
+
reconnectReason: error.reason
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
options.output.write(
|
|
490
|
+
`[nolo] Connector websocket failed: ${toErrorMessage(error)}
|
|
491
|
+
`
|
|
492
|
+
);
|
|
493
|
+
return { exitCode: 1 };
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// packages/cli/machineStatusCommands.ts
|
|
498
|
+
function resolveServerUrl(env) {
|
|
499
|
+
return normalizeServerOrigin(
|
|
500
|
+
env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
function resolveAuthToken(env) {
|
|
504
|
+
return env.AUTH_TOKEN || env.AUTH || "";
|
|
505
|
+
}
|
|
506
|
+
function writeAuthMissing(output) {
|
|
507
|
+
output.write(
|
|
508
|
+
"[nolo] Machine commands require an auth token. Run `nolo login` or set AUTH_TOKEN.\n"
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
function formatMachineStatus(machines) {
|
|
512
|
+
if (machines.length === 0) {
|
|
513
|
+
return "No connected machines.\nRun `nolo connect` on this computer to register it once.\n";
|
|
514
|
+
}
|
|
515
|
+
return [
|
|
516
|
+
"Connected machines:",
|
|
517
|
+
...machines.map((machine) => {
|
|
518
|
+
const caps = machine.capabilities.length ? machine.capabilities.join(", ") : "no capabilities";
|
|
519
|
+
const connector = machine.connectorStatus ? ` ws:${machine.connectorStatus}` : "";
|
|
520
|
+
return `- ${machine.name} ${machine.status}${connector} ${machine.platform}/${machine.arch} ${caps}`;
|
|
521
|
+
}),
|
|
522
|
+
""
|
|
523
|
+
].join("\n");
|
|
524
|
+
}
|
|
525
|
+
async function runMachineStatusCommand(_args, deps = {}) {
|
|
526
|
+
const env = deps.env ?? process.env;
|
|
527
|
+
const output = deps.output ?? process.stdout;
|
|
528
|
+
const authToken = resolveAuthToken(env);
|
|
529
|
+
if (!authToken) {
|
|
530
|
+
writeAuthMissing(output);
|
|
531
|
+
return 1;
|
|
532
|
+
}
|
|
533
|
+
const serverUrl = resolveServerUrl(env);
|
|
534
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
535
|
+
let res;
|
|
536
|
+
try {
|
|
537
|
+
res = await fetchImpl(`${serverUrl}/api/machines`, {
|
|
538
|
+
method: "GET",
|
|
539
|
+
headers: {
|
|
540
|
+
Authorization: `Bearer ${authToken}`
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
} catch (error) {
|
|
544
|
+
output.write(
|
|
545
|
+
`[nolo] Machine status failed: ${toErrorMessage(error)}
|
|
546
|
+
`
|
|
547
|
+
);
|
|
548
|
+
return 1;
|
|
549
|
+
}
|
|
550
|
+
if (!res.ok) {
|
|
551
|
+
const text = await res.text().catch(() => "");
|
|
552
|
+
output.write(`[nolo] Machine status failed: HTTP ${res.status}
|
|
553
|
+
${text}
|
|
554
|
+
`);
|
|
555
|
+
return 1;
|
|
556
|
+
}
|
|
557
|
+
const data = await res.json().catch(() => ({ machines: [] }));
|
|
558
|
+
const machines = Array.isArray(data?.machines) ? data.machines : [];
|
|
559
|
+
output.write(formatMachineStatus(machines));
|
|
560
|
+
return 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// packages/cli/machineCommands.ts
|
|
564
|
+
function resolveAuthToken2(env) {
|
|
565
|
+
return env.AUTH_TOKEN || env.AUTH || "";
|
|
566
|
+
}
|
|
567
|
+
function writeAuthMissing2(output) {
|
|
568
|
+
output.write(
|
|
569
|
+
"[nolo] Machine commands require an auth token. Run `nolo login` or set AUTH_TOKEN.\n"
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
function hasFlag(args, flag) {
|
|
573
|
+
return args.includes(flag);
|
|
574
|
+
}
|
|
575
|
+
function readOption(args, flag) {
|
|
576
|
+
for (let i = 0; i < args.length; i++) {
|
|
577
|
+
const arg = args[i];
|
|
578
|
+
if (arg === flag) {
|
|
579
|
+
const next = args[i + 1];
|
|
580
|
+
if (typeof next === "string" && !next.startsWith("-")) return next;
|
|
581
|
+
return "";
|
|
582
|
+
}
|
|
583
|
+
if (arg.startsWith(flag + "=")) {
|
|
584
|
+
return arg.slice(flag.length + 1);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return "";
|
|
588
|
+
}
|
|
589
|
+
function resolveConnectServerUrl(args, env) {
|
|
590
|
+
const fromFlag = readOption(args, "--server-url") || readOption(args, "--server");
|
|
591
|
+
const raw = fromFlag || env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL;
|
|
592
|
+
return String(raw).replace(/\/+$|\s+$/g, "").replace(/\/+$/g, "").replace(/\s+$/g, "");
|
|
593
|
+
}
|
|
594
|
+
function resolveConnectAuthToken(args, env) {
|
|
595
|
+
return readOption(args, "--machine-key") || readOption(args, "--token") || resolveAuthToken2(env);
|
|
596
|
+
}
|
|
597
|
+
var defaultSleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
598
|
+
async function sleepWithAbort(ms, sleep, signal) {
|
|
599
|
+
if (!signal) {
|
|
600
|
+
await sleep(ms);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (signal.aborted) return;
|
|
604
|
+
await Promise.race([
|
|
605
|
+
sleep(ms),
|
|
606
|
+
new Promise((resolve2) => {
|
|
607
|
+
signal.addEventListener("abort", () => resolve2(), { once: true });
|
|
608
|
+
})
|
|
609
|
+
]);
|
|
610
|
+
}
|
|
611
|
+
function resolveConnectorReconnectDelayMs(env) {
|
|
612
|
+
const raw = Number(env.NOLO_CONNECT_RECONNECT_MS ?? "");
|
|
613
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 5e3;
|
|
614
|
+
}
|
|
615
|
+
async function defaultExecuteCli(provider, prompt, options) {
|
|
616
|
+
const { executeCli } = await import("./cliExecutor-K3F7DMRO.js");
|
|
617
|
+
return executeCli(provider, prompt, options);
|
|
618
|
+
}
|
|
619
|
+
function detectLaunchableMachineInfo() {
|
|
620
|
+
return detectMachineInfo({ probeLaunchable: true });
|
|
621
|
+
}
|
|
622
|
+
function findTaskRowSubjectRef(runtimeContext) {
|
|
623
|
+
const subjectRefs = Array.isArray(runtimeContext?.subjectRefs) ? runtimeContext.subjectRefs : [];
|
|
624
|
+
for (const ref of subjectRefs) {
|
|
625
|
+
if (!isRecord(ref)) continue;
|
|
626
|
+
if (ref.kind !== "table-row") continue;
|
|
627
|
+
const id = asTrimmedString(ref.id);
|
|
628
|
+
if (id) return id;
|
|
629
|
+
}
|
|
630
|
+
return "";
|
|
631
|
+
}
|
|
632
|
+
function buildTaskEvidencePrompt(args) {
|
|
633
|
+
const rowDbKey = findTaskRowSubjectRef(args.runtimeContext);
|
|
634
|
+
if (!rowDbKey) return "";
|
|
635
|
+
return [
|
|
636
|
+
"--- Nolo task evidence context ---",
|
|
637
|
+
"This CLI runtime does not receive server-side function tools directly.",
|
|
638
|
+
"Use the task row subjectRef and linked dialog evidence as durable task evidence.",
|
|
639
|
+
"Run commands from the repository root. The runner already provides server URL and auth in the environment.",
|
|
640
|
+
"Release boundary: after review passes, AI/reviewer/Codex may advance alpha for verification. Do not merge, push, or release main/release unless the human owner explicitly authorizes it in the current request.",
|
|
641
|
+
"Handoff context: query dialog.subjectRefs first, then inspect dialog checkpoints, artifacts, commits, and test evidence. Treat row activityRefs/latestActivityRef as cache hints, not state truth.",
|
|
642
|
+
"Reviewer autonomy: if you are reviewing and find a concrete fix, you may directly dispatch the rework agent/dialog instead of sending the task back to PM first.",
|
|
643
|
+
"Rework evidence rule: every implementation, review, and rework dialog must preserve the same table-row subjectRef and report any child/rework dialog id it starts.",
|
|
644
|
+
`Read task row: bun packages/cli/index.ts table query --table meta-0e95801d90-01KWSK4Q4TESXQ06SW39JN2TTJ --row ${JSON.stringify(rowDbKey)} --include-activity --output json`,
|
|
645
|
+
`Query linked dialogs: bun packages/cli/index.ts dialog query --row-dbkey ${JSON.stringify(rowDbKey)} --json`,
|
|
646
|
+
"If this run already has a dialog id, exclude it from evidence queries: bun packages/cli/index.ts dialog query --row-dbkey <rowDbKey> --exclude-dialog <currentDialogId> --json",
|
|
647
|
+
"Then read exact dialog traces with: bun packages/cli/index.ts dialog read <dialogId>",
|
|
648
|
+
"Reviewers must end with exactly one line: Review decision: approved | needs_changes | blocked",
|
|
649
|
+
"Report progress, blockers, worktree, branch, commit/diff, tests, and unverified items in the dialog."
|
|
650
|
+
].join("\n");
|
|
651
|
+
}
|
|
652
|
+
function buildConnectorCliPrompt(agentConfig, userInput, bridgeArgs, permissionPolicy) {
|
|
653
|
+
const policy = permissionPolicy ?? resolveMachineRunPermissionPolicy(agentConfig);
|
|
654
|
+
return [
|
|
655
|
+
asTrimmedString(agentConfig?.prompt),
|
|
656
|
+
bridgeArgs ? buildTaskEvidencePrompt({
|
|
657
|
+
agentKey: bridgeArgs.agentKey,
|
|
658
|
+
agentConfig,
|
|
659
|
+
runtimeContext: bridgeArgs.runtimeContext
|
|
660
|
+
}) : "",
|
|
661
|
+
buildMachinePermissionPromptBlock(policy),
|
|
662
|
+
`--- User task ---
|
|
663
|
+
${userInput}`
|
|
664
|
+
].filter(Boolean).join("\n\n");
|
|
665
|
+
}
|
|
666
|
+
async function runMachineConnectCommand(args, deps = {}) {
|
|
667
|
+
const env = deps.env ?? process.env;
|
|
668
|
+
const output = deps.output ?? process.stdout;
|
|
669
|
+
const authToken = resolveConnectAuthToken(args, env);
|
|
670
|
+
if (!authToken) {
|
|
671
|
+
writeAuthMissing2(output);
|
|
672
|
+
return 1;
|
|
673
|
+
}
|
|
674
|
+
const serverUrl = resolveConnectServerUrl(args, env);
|
|
675
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
676
|
+
if (hasFlag(args, "--daemon") || hasFlag(args, "--background")) {
|
|
677
|
+
return runMachineDaemonCommand({
|
|
678
|
+
env,
|
|
679
|
+
output,
|
|
680
|
+
cliEntrypointPath: deps.cliEntrypointPath,
|
|
681
|
+
validateWorkspaceLinks: deps.validateWorkspaceLinks,
|
|
682
|
+
spawnDaemon: deps.spawnDaemon
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
const machine = (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
|
686
|
+
const sendHeartbeat = async () => {
|
|
687
|
+
const res = await fetchImpl(`${serverUrl}/api/machines/heartbeat`, {
|
|
688
|
+
method: "POST",
|
|
689
|
+
headers: {
|
|
690
|
+
Authorization: `Bearer ${authToken}`,
|
|
691
|
+
"Content-Type": "application/json"
|
|
692
|
+
},
|
|
693
|
+
body: JSON.stringify(machine)
|
|
694
|
+
});
|
|
695
|
+
if (!res.ok) {
|
|
696
|
+
const text = await res.text().catch(() => "");
|
|
697
|
+
throw new Error(`HTTP ${res.status}
|
|
698
|
+
${text}`);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
if (hasFlag(args, "--ws")) {
|
|
702
|
+
const validateWorkspaceLinks = deps.validateWorkspaceLinks ?? (() => []);
|
|
703
|
+
if (!await checkConnectorWorkspaceLinks(process.cwd(), output, validateWorkspaceLinks)) {
|
|
704
|
+
return 1;
|
|
705
|
+
}
|
|
706
|
+
const maxAttempts = deps.maxConnectorAttempts ?? Infinity;
|
|
707
|
+
const sleep = deps.sleep ?? defaultSleep2;
|
|
708
|
+
const reconnectDelayMs = resolveConnectorReconnectDelayMs(env);
|
|
709
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
710
|
+
if (deps.signal?.aborted) {
|
|
711
|
+
return 0;
|
|
712
|
+
}
|
|
713
|
+
const runtimeServerUrl = env.NOLO_CONNECT_RUNTIME_SERVER_URL || env.NOLO_RUNTIME_SERVER_URL || serverUrl;
|
|
714
|
+
const sessionResult = await runMachineWsSession({
|
|
715
|
+
env,
|
|
716
|
+
output,
|
|
717
|
+
signal: deps.signal,
|
|
718
|
+
machine,
|
|
719
|
+
sendHeartbeat,
|
|
720
|
+
runHeartbeatLoop: deps.runHeartbeatLoop ?? runHeartbeatLoop,
|
|
721
|
+
connectWebSocket: deps.connectWebSocket ?? defaultConnectWebSocket,
|
|
722
|
+
serverUrl,
|
|
723
|
+
authToken,
|
|
724
|
+
resolveConnectorWebSocketTarget: (options) => resolveConnectorWebSocketTarget({
|
|
725
|
+
...options,
|
|
726
|
+
fetchImpl
|
|
727
|
+
}),
|
|
728
|
+
onMessage: (message, send) => handleConnectorRunMessage(
|
|
729
|
+
message,
|
|
730
|
+
send,
|
|
731
|
+
deps.executeCli ?? defaultExecuteCli,
|
|
732
|
+
{
|
|
733
|
+
...env,
|
|
734
|
+
NOLO_SERVER: runtimeServerUrl,
|
|
735
|
+
NOLO_SERVER_URL: runtimeServerUrl,
|
|
736
|
+
BASE_URL: runtimeServerUrl,
|
|
737
|
+
AUTH_TOKEN: authToken,
|
|
738
|
+
NOLO_MACHINE_API_KEY: authToken
|
|
739
|
+
},
|
|
740
|
+
fetchImpl,
|
|
741
|
+
{
|
|
742
|
+
buildConnectorCliPrompt
|
|
743
|
+
}
|
|
744
|
+
)
|
|
745
|
+
});
|
|
746
|
+
const exitCode = sessionResult.exitCode;
|
|
747
|
+
if (exitCode === 2) return 1;
|
|
748
|
+
if (deps.signal?.aborted) {
|
|
749
|
+
return 0;
|
|
750
|
+
}
|
|
751
|
+
if (attempt >= maxAttempts) return exitCode;
|
|
752
|
+
const retryDelayMs = sessionResult.retryAfterMs ?? reconnectDelayMs;
|
|
753
|
+
const reconnectContext = sessionResult.reconnectReason === "core_draining" ? " during core draining" : "";
|
|
754
|
+
output.write(
|
|
755
|
+
`[nolo] Connector websocket disconnected${reconnectContext}. Reconnecting in ${retryDelayMs}ms.
|
|
756
|
+
`
|
|
757
|
+
);
|
|
758
|
+
await sleepWithAbort(retryDelayMs, sleep, deps.signal);
|
|
759
|
+
}
|
|
760
|
+
return 0;
|
|
761
|
+
}
|
|
762
|
+
if (hasFlag(args, "--watch")) {
|
|
763
|
+
return runMachineWatchCommand({
|
|
764
|
+
env,
|
|
765
|
+
output,
|
|
766
|
+
machine,
|
|
767
|
+
sendHeartbeat,
|
|
768
|
+
runHeartbeatLoop: deps.runHeartbeatLoop ?? runHeartbeatLoop
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
return runMachineHeartbeatConnectCommand({
|
|
772
|
+
output,
|
|
773
|
+
machine,
|
|
774
|
+
sendHeartbeat
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
export {
|
|
778
|
+
formatMachineStatus,
|
|
779
|
+
runMachineConnectCommand,
|
|
780
|
+
runMachineStatusCommand
|
|
781
|
+
};
|