nolo-cli 0.1.23 → 0.1.26
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 +25 -3
- package/agent-runtime/hybridRecordStore.ts +30 -9
- package/agent-runtime/localLoop.ts +12 -4
- package/agent-runtime/localToolPolicy.ts +22 -10
- package/agent-runtime/localWorkspaceTools.ts +140 -374
- package/agent-runtime/runtimeToolPolicy.ts +43 -2
- package/agent-runtime/types.ts +5 -0
- package/agentAliases.ts +1 -1
- package/agentCommandSupport.ts +77 -0
- package/agentInternalCommandEntries.ts +34 -0
- package/agentListCommands.ts +123 -0
- package/agentListHelpers.ts +187 -0
- package/agentMachineCommands.ts +402 -0
- package/agentRecordCommands.ts +141 -0
- package/agentRecordHelpers.ts +395 -0
- package/agentRunCommand.ts +1 -2
- package/agentRuntimeCommands.ts +7 -701
- package/ai/agent/_executeModel.ts +29 -3
- package/ai/agent/buildSystemPrompt.ts +17 -0
- package/ai/agent/cliChatClient.ts +38 -4
- package/ai/agent/cliExecutor.ts +304 -25
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/guidedCreation/assistant.ts +82 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/draft.ts +62 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/machineRunPermissions.ts +21 -3
- package/ai/agent/runtimeGuidance.ts +1 -1
- package/ai/agent/server/fetchPublicAgents.ts +6 -1
- package/ai/agent/streamAgentChatTurn.ts +492 -19
- package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
- package/ai/ai.locale.ts +14 -2
- package/ai/chat/sendOpenAICompletionsRequest.ts +28 -3
- package/ai/chat/sendOpenAIResponseRequest.ts +3 -2
- package/ai/llm/getPricing.ts +14 -0
- package/ai/llm/kimi.ts +3 -0
- package/ai/llm/modelAvatar.ts +1 -0
- package/ai/llm/providers.ts +1 -1
- package/ai/taskRun/taskRunProtocol.ts +24 -10
- package/ai/token/modelUsageQuery.ts +17 -0
- package/ai/tools/agent/agentTools.ts +21 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/checkEnvTool.ts +52 -20
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
- package/ai/tools/index.ts +24 -14
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +4 -2
- package/ai/tools/readTool.ts +31 -0
- package/ai/tools/toolApiClient.ts +35 -1
- package/ai/tools/toolNameAliases.ts +13 -0
- package/cliCommandAdapters.ts +28 -0
- package/cliCommandDispatch.ts +66 -0
- package/cliCommandFactories.ts +34 -0
- package/cliCommandTypes.ts +49 -0
- package/cliEnvHelpers.ts +44 -0
- package/client/agentRun.test.ts +123 -19
- package/client/agentRun.ts +40 -1
- package/client/hybridRecordStore.test.ts +42 -0
- package/client/hybridRecordStore.ts +11 -0
- package/client/localRuntimeAdapter.test.ts +189 -21
- package/client/localRuntimeAdapter.ts +84 -8
- package/client/localRuntimeDryRun.test.ts +5 -2
- package/client/taskRunPrompt.ts +1 -0
- package/commandRegistry.ts +11 -61
- package/connector-experimental/capabilities.ts +1 -0
- package/database/server/coreDataProxy.ts +43 -26
- package/index.ts +54 -191
- package/internalCommandEntries.ts +12 -0
- package/machineCommands.ts +301 -32
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +20 -2
- package/runtimeDoctorCommands.ts +118 -0
- package/scriptCommandEntries.ts +71 -0
- package/systemInternalCommandEntries.ts +71 -0
- package/workflowInternalCommandEntries.ts +11 -0
package/machineCommands.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { MachineHeartbeat } from "./connector-experimental/protocol";
|
|
2
2
|
import { detectMachineInfo } from "./connector-experimental/machineInfo";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import { spawn } from "node:child_process";
|
|
4
|
-
import { mkdirSync, openSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, openSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
|
-
import { dirname, join } from "node:path";
|
|
7
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
7
8
|
import { runLocalAgentTurn, type LocalAgentToolEvent } from "./agent-runtime/localLoop";
|
|
8
9
|
import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
|
|
9
10
|
import {
|
|
@@ -55,6 +56,13 @@ type ConnectorRunProgress = {
|
|
|
55
56
|
toolCallCount?: number;
|
|
56
57
|
toolResultCount?: number;
|
|
57
58
|
lastToolNames?: string[];
|
|
59
|
+
provider?: string;
|
|
60
|
+
promptBytes?: number;
|
|
61
|
+
promptHash?: string;
|
|
62
|
+
promptRef?: string;
|
|
63
|
+
promptPageKey?: string;
|
|
64
|
+
promptPageHash?: string;
|
|
65
|
+
requestId?: string;
|
|
58
66
|
workspaceRoot?: string;
|
|
59
67
|
workspaceKind?: string;
|
|
60
68
|
message?: string;
|
|
@@ -82,15 +90,26 @@ type MachineCommandDeps = {
|
|
|
82
90
|
fetchImpl?: typeof fetch;
|
|
83
91
|
machineInfo?: () => MachineHeartbeat;
|
|
84
92
|
runHeartbeatLoop?: (options: HeartbeatLoopOptions) => Promise<void>;
|
|
85
|
-
connectWebSocket?: (url: string, options: ConnectorWebSocketOptions) => Promise<void>;
|
|
86
|
-
executeCli?: LocalCliExecutor;
|
|
87
|
-
|
|
93
|
+
connectWebSocket?: (url: string, options: ConnectorWebSocketOptions) => Promise<void>;
|
|
94
|
+
executeCli?: LocalCliExecutor;
|
|
95
|
+
validateWorkspaceLinks?: (cwd: string) => string[] | Promise<string[]>;
|
|
96
|
+
spawnDaemon?: (args: {
|
|
88
97
|
cmd: string[];
|
|
89
98
|
cwd: string;
|
|
90
99
|
env: EnvLike;
|
|
91
100
|
logPath: string;
|
|
92
101
|
}) => { pid?: number };
|
|
93
|
-
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
type PackageJson = {
|
|
105
|
+
name?: string;
|
|
106
|
+
workspaces?: string[] | { packages?: string[] };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const CONNECTOR_RUNTIME_WORKSPACE_PACKAGES = new Set([
|
|
110
|
+
"connector-experimental",
|
|
111
|
+
"ai",
|
|
112
|
+
]);
|
|
94
113
|
|
|
95
114
|
function resolveServerUrl(env: EnvLike) {
|
|
96
115
|
return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
|
|
@@ -106,9 +125,113 @@ function writeAuthMissing(output: OutputLike) {
|
|
|
106
125
|
);
|
|
107
126
|
}
|
|
108
127
|
|
|
109
|
-
function hasFlag(args: string[], flag: string) {
|
|
110
|
-
return args.includes(flag);
|
|
111
|
-
}
|
|
128
|
+
function hasFlag(args: string[], flag: string) {
|
|
129
|
+
return args.includes(flag);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizePath(path: string) {
|
|
133
|
+
const resolved = resolve(path);
|
|
134
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isInsideOrSame(path: string, parent: string) {
|
|
138
|
+
const relativePath = relative(parent, path);
|
|
139
|
+
return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function readJsonFile<T>(path: string): T | null {
|
|
143
|
+
try {
|
|
144
|
+
return JSON.parse(readFileSync(path, "utf8")) as T;
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getWorkspacePatterns(packageJson: PackageJson) {
|
|
151
|
+
if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
|
|
152
|
+
return packageJson.workspaces?.packages ?? [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function findWorkspaceRoot(startPath: string) {
|
|
156
|
+
let current = resolve(startPath);
|
|
157
|
+
while (true) {
|
|
158
|
+
const packageJsonPath = join(current, "package.json");
|
|
159
|
+
if (existsSync(packageJsonPath)) {
|
|
160
|
+
const packageJson = readJsonFile<PackageJson>(packageJsonPath);
|
|
161
|
+
if (packageJson && getWorkspacePatterns(packageJson).length > 0) {
|
|
162
|
+
return current;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const parent = dirname(current);
|
|
166
|
+
if (parent === current) return null;
|
|
167
|
+
current = parent;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function validateConnectorWorkspaceLinks(cwd: string) {
|
|
172
|
+
const repoRoot = findWorkspaceRoot(cwd);
|
|
173
|
+
if (!repoRoot) return [];
|
|
174
|
+
|
|
175
|
+
const rootPackageJson = readJsonFile<PackageJson>(join(repoRoot, "package.json"));
|
|
176
|
+
if (!rootPackageJson) return [];
|
|
177
|
+
|
|
178
|
+
const normalizedRepoRoot = normalizePath(realpathSync(repoRoot));
|
|
179
|
+
const errors: string[] = [];
|
|
180
|
+
|
|
181
|
+
for (const pattern of getWorkspacePatterns(rootPackageJson)) {
|
|
182
|
+
if (!pattern.endsWith("/*")) continue;
|
|
183
|
+
const workspaceParent = join(repoRoot, pattern.slice(0, -2));
|
|
184
|
+
if (!existsSync(workspaceParent)) continue;
|
|
185
|
+
|
|
186
|
+
for (const entry of Array.from(new Bun.Glob("*/package.json").scanSync({ cwd: workspaceParent }))) {
|
|
187
|
+
const packageDir = join(workspaceParent, dirname(entry));
|
|
188
|
+
const packageJson = readJsonFile<PackageJson>(join(workspaceParent, entry));
|
|
189
|
+
if (!packageJson?.name || !CONNECTOR_RUNTIME_WORKSPACE_PACKAGES.has(packageJson.name)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const linkPath = join(repoRoot, "node_modules", ...packageJson.name.split("/"));
|
|
194
|
+
if (!existsSync(linkPath)) {
|
|
195
|
+
errors.push(`${linkPath} is missing. Run bun install in ${repoRoot}.`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const actualPath = normalizePath(realpathSync(linkPath));
|
|
200
|
+
const expectedPath = normalizePath(realpathSync(packageDir));
|
|
201
|
+
if (!isInsideOrSame(actualPath, normalizedRepoRoot)) {
|
|
202
|
+
errors.push(
|
|
203
|
+
`${linkPath} points outside this checkout: ${actualPath}. Run bun install in ${repoRoot}.`,
|
|
204
|
+
);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (actualPath !== expectedPath) {
|
|
208
|
+
errors.push(
|
|
209
|
+
`${linkPath} points at ${actualPath}, expected ${expectedPath}. Run bun install in ${repoRoot}.`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return errors;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function checkConnectorWorkspaceLinks(
|
|
219
|
+
cwd: string,
|
|
220
|
+
output: OutputLike,
|
|
221
|
+
validateWorkspaceLinks: (cwd: string) => string[] | Promise<string[]>,
|
|
222
|
+
) {
|
|
223
|
+
const errors = await validateWorkspaceLinks(cwd);
|
|
224
|
+
if (errors.length === 0) return true;
|
|
225
|
+
output.write(
|
|
226
|
+
[
|
|
227
|
+
"[nolo] Connector workspace package links are unsafe.",
|
|
228
|
+
"The connector would import workspace packages from a different checkout, so machine capabilities and runtime behavior may be stale.",
|
|
229
|
+
...errors,
|
|
230
|
+
"",
|
|
231
|
+
].join("\n"),
|
|
232
|
+
);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
112
235
|
|
|
113
236
|
function readOption(args: string[], flag: string) {
|
|
114
237
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -139,6 +262,90 @@ function resolveDaemonLogPath(env: EnvLike) {
|
|
|
139
262
|
return env.NOLO_CONNECT_LOG || join(homedir(), ".nolo", "logs", "connector.log");
|
|
140
263
|
}
|
|
141
264
|
|
|
265
|
+
function byteLength(value: string) {
|
|
266
|
+
return Buffer.byteLength(value, "utf8");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function shortHash(value: string) {
|
|
270
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function resolvePromptInlineMaxBytes(env: EnvLike) {
|
|
274
|
+
const raw = Number(env.NOLO_CONNECTOR_PROMPT_INLINE_MAX_BYTES);
|
|
275
|
+
return Number.isFinite(raw) && raw >= 1024 ? raw : 32_000;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function materializeLargeConnectorPrompt(args: {
|
|
279
|
+
prompt: string;
|
|
280
|
+
cwd: string;
|
|
281
|
+
env: EnvLike;
|
|
282
|
+
requestId: string;
|
|
283
|
+
runtimePromptPage?: {
|
|
284
|
+
dbKey?: string;
|
|
285
|
+
promptHash?: string;
|
|
286
|
+
contentBytes?: number;
|
|
287
|
+
} | null;
|
|
288
|
+
}) {
|
|
289
|
+
const promptBytes = byteLength(args.prompt);
|
|
290
|
+
const promptHash = shortHash(args.prompt);
|
|
291
|
+
const maxInlineBytes = resolvePromptInlineMaxBytes(args.env);
|
|
292
|
+
if (promptBytes <= maxInlineBytes) {
|
|
293
|
+
return {
|
|
294
|
+
prompt: args.prompt,
|
|
295
|
+
promptBytes,
|
|
296
|
+
promptHash,
|
|
297
|
+
promptRef: null as string | null,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const promptDir = join(args.cwd, ".nolo", "agent-prompts");
|
|
302
|
+
mkdirSync(promptDir, { recursive: true });
|
|
303
|
+
const promptRef = join(promptDir, `${args.requestId}-${promptHash}.md`);
|
|
304
|
+
writeFileSync(promptRef, args.prompt, "utf8");
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
prompt: [
|
|
308
|
+
"A large Nolo runtime prompt has been materialized outside this command argument.",
|
|
309
|
+
...(args.runtimePromptPage?.dbKey
|
|
310
|
+
? [
|
|
311
|
+
"Cloud prompt page is the shared audit/context source.",
|
|
312
|
+
`Cloud prompt page: ${args.runtimePromptPage.dbKey}`,
|
|
313
|
+
`Read cloud page: bun packages/cli/index.ts doc read ${JSON.stringify(args.runtimePromptPage.dbKey)}`,
|
|
314
|
+
...(args.runtimePromptPage.promptHash
|
|
315
|
+
? [`Cloud prompt sha256 prefix: ${args.runtimePromptPage.promptHash}`]
|
|
316
|
+
: []),
|
|
317
|
+
"",
|
|
318
|
+
]
|
|
319
|
+
: []),
|
|
320
|
+
"Local prompt file is the execution-machine fallback and should match the cloud page content at run start.",
|
|
321
|
+
`Prompt file: ${promptRef}`,
|
|
322
|
+
`Prompt bytes: ${promptBytes}`,
|
|
323
|
+
`Prompt sha256 prefix: ${promptHash}`,
|
|
324
|
+
"",
|
|
325
|
+
"Prefer the cloud prompt page for shared context when readable; use the local prompt file as fallback.",
|
|
326
|
+
"Local repository edits, diffs, test output, and commits are still authoritative for local execution facts.",
|
|
327
|
+
"If neither source is readable, stop and report PROMPT_REF_UNREADABLE with both references.",
|
|
328
|
+
].join("\n"),
|
|
329
|
+
promptBytes,
|
|
330
|
+
promptHash,
|
|
331
|
+
promptRef,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function readRuntimePromptPageMeta(parsed: any) {
|
|
336
|
+
const page = parsed?.payload?.meta?.runtimePromptPage;
|
|
337
|
+
if (!page || typeof page !== "object") return null;
|
|
338
|
+
return {
|
|
339
|
+
dbKey: typeof page.dbKey === "string" ? page.dbKey : undefined,
|
|
340
|
+
promptHash: typeof page.promptHash === "string" ? page.promptHash : undefined,
|
|
341
|
+
contentBytes: typeof page.contentBytes === "number" ? page.contentBytes : undefined,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function logConnectorRun(message: string, fields: Record<string, unknown> = {}) {
|
|
346
|
+
console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
142
349
|
function buildDaemonCommand(cliEntrypointPath: string | undefined) {
|
|
143
350
|
return [process.execPath, cliEntrypointPath || import.meta.path, "connect", "--ws"];
|
|
144
351
|
}
|
|
@@ -272,6 +479,7 @@ function buildTaskRunBridgePrompt(args: {
|
|
|
272
479
|
"Run commands from the repository root. The runner already provides server URL and auth in the environment.",
|
|
273
480
|
"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 task context.",
|
|
274
481
|
"Cost boundary: read taskRun.control from context. Prefer low-cost/free-window models for routine work, and reserve Codex for supervision, quality gates, complex unblocking, and main/release decisions.",
|
|
482
|
+
"Handoff context: start from read-context, then inspect existing refs such as workItems.dialogIds, workItems.outcome, review.dialogId, activityRefs, agentRuns.dialogId/resultSummary, and artifactRefs. Use dialog read/search for exact evidence; do not infer completion from handoff text alone.",
|
|
275
483
|
`Read context: bun packages/cli/index.ts task-run read-context --row-dbkey ${JSON.stringify(rowDbKey)}`,
|
|
276
484
|
...(workItemId
|
|
277
485
|
? [
|
|
@@ -416,11 +624,9 @@ function buildArtifactProgress(args: {
|
|
|
416
624
|
const runtimeTools = Array.isArray(args.runtimePolicy?.runtimeTools)
|
|
417
625
|
? args.runtimePolicy.runtimeTools
|
|
418
626
|
: [];
|
|
419
|
-
const shellTool = runtimeTools.includes("
|
|
420
|
-
? "
|
|
421
|
-
:
|
|
422
|
-
? "execShell"
|
|
423
|
-
: "workspaceArtifact";
|
|
627
|
+
const shellTool = runtimeTools.includes("execShell")
|
|
628
|
+
? "execShell"
|
|
629
|
+
: "workspaceArtifact";
|
|
424
630
|
return {
|
|
425
631
|
eventType: "tool-result",
|
|
426
632
|
toolCallCount: 1,
|
|
@@ -611,12 +817,57 @@ export async function handleConnectorRunMessage(
|
|
|
611
817
|
assertMachineRunAllowed(userInput, effectivePermissionPolicy);
|
|
612
818
|
baseSha = await readConnectorGitHead(artifactCwd);
|
|
613
819
|
const provider = String(agentConfig.cliProvider || "copilot");
|
|
820
|
+
const builtPrompt = buildConnectorCliPrompt(agentConfig, userInput, {
|
|
821
|
+
agentKey: String(parsed.payload?.agentKey ?? ""),
|
|
822
|
+
runtimeContext: parsed.payload?.runtimeContext,
|
|
823
|
+
}, effectivePermissionPolicy);
|
|
824
|
+
const materializedPrompt = materializeLargeConnectorPrompt({
|
|
825
|
+
prompt: builtPrompt,
|
|
826
|
+
cwd,
|
|
827
|
+
env: runtimeEnv,
|
|
828
|
+
requestId: parsed.requestId,
|
|
829
|
+
runtimePromptPage: readRuntimePromptPageMeta(parsed),
|
|
830
|
+
});
|
|
831
|
+
const runtimePromptPage = readRuntimePromptPageMeta(parsed);
|
|
832
|
+
const startAt = Date.now();
|
|
833
|
+
const progressBase = {
|
|
834
|
+
provider,
|
|
835
|
+
requestId: parsed.requestId,
|
|
836
|
+
promptBytes: materializedPrompt.promptBytes,
|
|
837
|
+
promptHash: materializedPrompt.promptHash,
|
|
838
|
+
...(materializedPrompt.promptRef ? { promptRef: materializedPrompt.promptRef } : {}),
|
|
839
|
+
...(runtimePromptPage?.dbKey ? { promptPageKey: runtimePromptPage.dbKey } : {}),
|
|
840
|
+
...(runtimePromptPage?.promptHash ? { promptPageHash: runtimePromptPage.promptHash } : {}),
|
|
841
|
+
workspaceRoot: artifactCwd,
|
|
842
|
+
workspaceKind: requestsTaskWorktree(runtimePolicy) ? "task-worktree" : "current",
|
|
843
|
+
};
|
|
844
|
+
logConnectorRun("agent.run.cli.start", {
|
|
845
|
+
requestId: parsed.requestId,
|
|
846
|
+
agentKey: parsed.payload?.agentKey,
|
|
847
|
+
provider,
|
|
848
|
+
cwd,
|
|
849
|
+
artifactCwd,
|
|
850
|
+
timeout,
|
|
851
|
+
promptBytes: materializedPrompt.promptBytes,
|
|
852
|
+
promptHash: materializedPrompt.promptHash,
|
|
853
|
+
promptRef: materializedPrompt.promptRef,
|
|
854
|
+
promptPageKey: runtimePromptPage?.dbKey,
|
|
855
|
+
});
|
|
856
|
+
sendProgress({
|
|
857
|
+
...progressBase,
|
|
858
|
+
message: "cli-started",
|
|
859
|
+
updatedAt: startAt,
|
|
860
|
+
});
|
|
861
|
+
const heartbeat = setInterval(() => {
|
|
862
|
+
sendProgress({
|
|
863
|
+
...progressBase,
|
|
864
|
+
message: "cli-running",
|
|
865
|
+
updatedAt: Date.now(),
|
|
866
|
+
});
|
|
867
|
+
}, 15_000);
|
|
614
868
|
const result = await executeCli(
|
|
615
869
|
provider,
|
|
616
|
-
|
|
617
|
-
agentKey: String(parsed.payload?.agentKey ?? ""),
|
|
618
|
-
runtimeContext: parsed.payload?.runtimeContext,
|
|
619
|
-
}, effectivePermissionPolicy),
|
|
870
|
+
materializedPrompt.prompt,
|
|
620
871
|
{
|
|
621
872
|
model: agentConfig.model || undefined,
|
|
622
873
|
timeout,
|
|
@@ -630,9 +881,15 @@ export async function handleConnectorRunMessage(
|
|
|
630
881
|
NOLO_MACHINE_API_KEY: runtimeEnv.NOLO_MACHINE_API_KEY || runtimeEnv.AUTH_TOKEN || runtimeEnv.AUTH,
|
|
631
882
|
},
|
|
632
883
|
}
|
|
633
|
-
);
|
|
884
|
+
).finally(() => clearInterval(heartbeat));
|
|
885
|
+
logConnectorRun("agent.run.cli.result", {
|
|
886
|
+
requestId: parsed.requestId,
|
|
887
|
+
provider,
|
|
888
|
+
elapsed: result.elapsed,
|
|
889
|
+
outputBytes: byteLength(result.raw ?? result.text ?? ""),
|
|
890
|
+
});
|
|
634
891
|
runContent = result.text;
|
|
635
|
-
runModel = agentConfig.model
|
|
892
|
+
runModel = String(agentConfig.model || "").trim() || provider;
|
|
636
893
|
runTrace = [{ role: "assistant", content: result.text }];
|
|
637
894
|
const artifacts = await collectConnectorRunArtifact({
|
|
638
895
|
cwd: artifactCwd,
|
|
@@ -696,11 +953,16 @@ export async function handleConnectorRunMessage(
|
|
|
696
953
|
artifacts,
|
|
697
954
|
},
|
|
698
955
|
}));
|
|
699
|
-
} catch (error) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
error: error instanceof Error ? error.message : String(error),
|
|
956
|
+
} catch (error) {
|
|
957
|
+
logConnectorRun("agent.run.error", {
|
|
958
|
+
requestId: parsed.requestId,
|
|
959
|
+
agentKey: parsed.payload?.agentKey,
|
|
960
|
+
error: error instanceof Error ? error.message : String(error),
|
|
961
|
+
});
|
|
962
|
+
send(JSON.stringify({
|
|
963
|
+
type: "agent.run.result",
|
|
964
|
+
requestId: parsed.requestId,
|
|
965
|
+
error: error instanceof Error ? error.message : String(error),
|
|
704
966
|
}));
|
|
705
967
|
}
|
|
706
968
|
}
|
|
@@ -804,10 +1066,11 @@ export async function runMachineConnectCommand(
|
|
|
804
1066
|
return 1;
|
|
805
1067
|
}
|
|
806
1068
|
|
|
807
|
-
const serverUrl = resolveConnectServerUrl(args, env);
|
|
808
|
-
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
809
|
-
const machine = (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
|
810
|
-
const
|
|
1069
|
+
const serverUrl = resolveConnectServerUrl(args, env);
|
|
1070
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
1071
|
+
const machine = (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
|
1072
|
+
const validateWorkspaceLinks = deps.validateWorkspaceLinks ?? validateConnectorWorkspaceLinks;
|
|
1073
|
+
const sendHeartbeat = async () => {
|
|
811
1074
|
const res = await fetchImpl(`${serverUrl}/api/machines/heartbeat`, {
|
|
812
1075
|
method: "POST",
|
|
813
1076
|
headers: {
|
|
@@ -822,8 +1085,11 @@ export async function runMachineConnectCommand(
|
|
|
822
1085
|
throw new Error(`HTTP ${res.status}\n${text}`);
|
|
823
1086
|
}
|
|
824
1087
|
};
|
|
825
|
-
|
|
1088
|
+
|
|
826
1089
|
if (hasFlag(args, "--daemon") || hasFlag(args, "--background")) {
|
|
1090
|
+
if (!(await checkConnectorWorkspaceLinks(process.cwd(), output, validateWorkspaceLinks))) {
|
|
1091
|
+
return 1;
|
|
1092
|
+
}
|
|
827
1093
|
const logPath = resolveDaemonLogPath(env);
|
|
828
1094
|
const result = (deps.spawnDaemon ?? defaultSpawnDaemon)({
|
|
829
1095
|
cmd: buildDaemonCommand(deps.cliEntrypointPath),
|
|
@@ -833,9 +1099,12 @@ export async function runMachineConnectCommand(
|
|
|
833
1099
|
});
|
|
834
1100
|
output.write(`Connector daemon started${result.pid ? ` pid=${result.pid}` : ""}. Log: ${logPath}\n`);
|
|
835
1101
|
return 0;
|
|
836
|
-
}
|
|
837
|
-
|
|
1102
|
+
}
|
|
1103
|
+
|
|
838
1104
|
if (hasFlag(args, "--ws")) {
|
|
1105
|
+
if (!(await checkConnectorWorkspaceLinks(process.cwd(), output, validateWorkspaceLinks))) {
|
|
1106
|
+
return 1;
|
|
1107
|
+
}
|
|
839
1108
|
const maxAttempts = deps.maxConnectorAttempts ?? Infinity;
|
|
840
1109
|
const sleep = deps.sleep ?? defaultSleep;
|
|
841
1110
|
const reconnectDelayMs = resolveConnectorReconnectDelayMs(env);
|