nolo-cli 0.1.29 → 0.1.30
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/agentCreateCustomCommands.ts +9 -4
- package/agentDialogsCommands.ts +4 -3
- package/agentDoctorCommands.ts +3 -3
- package/agentListCommands.ts +3 -3
- package/agentMachineCommands.ts +6 -6
- package/agentPullCommand.ts +4 -12
- package/agentRecordCommands.ts +4 -2
- package/agentRecordHelpers.ts +8 -4
- package/agentRunCommand.ts +2 -6
- package/agentUnpublishCommands.ts +3 -2
- package/cliEnvHelpers.ts +60 -13
- package/connectorWebSocketTarget.ts +31 -10
- package/dialogReadCommands.ts +2 -1
- package/dialogStatusCommands.ts +2 -1
- package/machineCommands.ts +1 -0
- package/machineStatusCommands.ts +4 -12
- package/machineWsSession.ts +7 -0
- package/package.json +17 -17
- package/pageCreateCommands.ts +2 -2
- package/pageDeleteCommands.ts +3 -3
- package/pageReadCommands.ts +3 -3
- package/pageUpdateCommands.ts +3 -3
- package/runtimeDoctorCommands.ts +3 -3
- package/spaceCategoryCommands.ts +6 -6
- package/spaceCommands.ts +3 -3
- package/spaceDeleteCommands.ts +2 -2
- package/spaceReadCommands.ts +3 -3
- package/tableMetaCommands.ts +2 -2
- package/taskRunCommand.ts +11 -0
|
@@ -3,6 +3,7 @@ import { createSpaceKey } from "./create/space/spaceKeys";
|
|
|
3
3
|
import { readDbRecord, writeDbRecord } from "./agentRecordHelpers";
|
|
4
4
|
import {
|
|
5
5
|
parseUserIdFromAuthToken,
|
|
6
|
+
readCliOptionValue,
|
|
6
7
|
resolveAuthToken,
|
|
7
8
|
resolveServerUrl,
|
|
8
9
|
type EnvLike,
|
|
@@ -68,11 +69,13 @@ function buildAgentKeys(agentId: string, userId: string) {
|
|
|
68
69
|
|
|
69
70
|
async function createCustomAgentWithCliAuth(
|
|
70
71
|
options: CreateCustomCodingAgentOptions,
|
|
71
|
-
deps: Pick<AgentCreateCustomDeps, "env" | "fallbackFetchImpl" | "fetchImpl">
|
|
72
|
+
deps: Pick<AgentCreateCustomDeps, "env" | "fallbackFetchImpl" | "fetchImpl"> & {
|
|
73
|
+
cliArgs?: string[];
|
|
74
|
+
}
|
|
72
75
|
) {
|
|
73
76
|
const env = deps.env ?? process.env;
|
|
74
77
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
75
|
-
const authToken = resolveAuthToken(env);
|
|
78
|
+
const authToken = readCliOptionValue(deps.cliArgs ?? [], "--token") || resolveAuthToken(env);
|
|
76
79
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
77
80
|
if (!authToken || !userId) {
|
|
78
81
|
throw new Error("agent create-custom requires an auth token with userId. Run `nolo login`.");
|
|
@@ -232,9 +235,11 @@ export async function runAgentCreateCustomCommand(
|
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
try {
|
|
235
|
-
const createCustomAgent =
|
|
238
|
+
const createCustomAgent =
|
|
239
|
+
deps.createCustomAgent ??
|
|
240
|
+
((options) => createCustomAgentWithCliAuth(options, { ...deps, cliArgs: args }));
|
|
236
241
|
const result = await createCustomAgent({
|
|
237
|
-
baseUrl: resolveServerUrl(env),
|
|
242
|
+
baseUrl: resolveServerUrl(args, env),
|
|
238
243
|
spaceId: readOption(args, "--space-id"),
|
|
239
244
|
agentId: env.CUSTOM_AGENT_ID,
|
|
240
245
|
apiKey,
|
package/agentDialogsCommands.ts
CHANGED
|
@@ -93,7 +93,7 @@ export async function runAgentDialogsCommand(
|
|
|
93
93
|
return agentInput ? 0 : 1;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
const authToken = resolveAuthToken(env);
|
|
96
|
+
const authToken = resolveAuthToken(args, env);
|
|
97
97
|
if (!authToken) {
|
|
98
98
|
output.write("[nolo] agent dialogs requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
99
99
|
return 1;
|
|
@@ -112,12 +112,13 @@ export async function runAgentDialogsCommand(
|
|
|
112
112
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
113
113
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
114
114
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
115
|
-
const serverUrl = resolveServerUrl(env);
|
|
116
|
-
const serverCandidates = resolveServerCandidates(env, serverUrl);
|
|
115
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
116
|
+
const serverCandidates = resolveServerCandidates(args, env, serverUrl);
|
|
117
117
|
|
|
118
118
|
try {
|
|
119
119
|
const resolvedAgent = await resolveAgentRecordFromHybridStore({
|
|
120
120
|
agentInput,
|
|
121
|
+
cliArgs: args,
|
|
121
122
|
env,
|
|
122
123
|
db,
|
|
123
124
|
fetchImpl,
|
package/agentDoctorCommands.ts
CHANGED
|
@@ -45,7 +45,7 @@ export async function runAgentDoctorCommand(
|
|
|
45
45
|
return 0;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const authToken = resolveAuthToken(env);
|
|
48
|
+
const authToken = resolveAuthToken(args, env);
|
|
49
49
|
if (!authToken) {
|
|
50
50
|
output.write("[nolo] agent doctor requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
51
51
|
return 1;
|
|
@@ -60,8 +60,8 @@ export async function runAgentDoctorCommand(
|
|
|
60
60
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
61
61
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
62
62
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
63
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
64
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
63
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
64
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
65
65
|
|
|
66
66
|
try {
|
|
67
67
|
const [profile, settings, agents] = await Promise.all([
|
package/agentListCommands.ts
CHANGED
|
@@ -25,7 +25,7 @@ export async function runAgentListCommand(
|
|
|
25
25
|
const output = deps.output ?? process.stdout;
|
|
26
26
|
const { wantJson, publicOnly, idsOnly, includeLegacy } = parseAgentListArgs(args);
|
|
27
27
|
|
|
28
|
-
const authToken = resolveAuthToken(env);
|
|
28
|
+
const authToken = resolveAuthToken(args, env);
|
|
29
29
|
if (!authToken) {
|
|
30
30
|
output.write("[nolo] agent list requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
31
31
|
return 1;
|
|
@@ -40,8 +40,8 @@ export async function runAgentListCommand(
|
|
|
40
40
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
41
41
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
42
42
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
43
|
-
const serverUrl = resolveServerUrl(env);
|
|
44
|
-
const serverCandidates = resolveServerCandidates(env, serverUrl);
|
|
43
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
44
|
+
const serverCandidates = resolveServerCandidates(args, env, serverUrl);
|
|
45
45
|
|
|
46
46
|
try {
|
|
47
47
|
const loadRemoteAgents = async () => {
|
package/agentMachineCommands.ts
CHANGED
|
@@ -225,7 +225,7 @@ export async function runAgentBindCurrentCommand(
|
|
|
225
225
|
return agentKey ? 0 : 1;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
const authToken = resolveAuthToken(env);
|
|
228
|
+
const authToken = resolveAuthToken(args, env);
|
|
229
229
|
if (!authToken) {
|
|
230
230
|
output.write("[nolo] agent bind-current requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
231
231
|
return 1;
|
|
@@ -237,7 +237,7 @@ export async function runAgentBindCurrentCommand(
|
|
|
237
237
|
return 1;
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
const serverUrl = resolveServerUrl(env);
|
|
240
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
241
241
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
242
242
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
243
243
|
const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
|
@@ -292,7 +292,7 @@ export async function runAgentSmokeCurrentCommand(
|
|
|
292
292
|
}
|
|
293
293
|
const agentKey = resolveCliAgentKeyInput(agentInput);
|
|
294
294
|
|
|
295
|
-
const authToken = resolveAuthToken(env);
|
|
295
|
+
const authToken = resolveAuthToken(args, env);
|
|
296
296
|
if (!authToken) {
|
|
297
297
|
output.write("[nolo] agent smoke-current requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
298
298
|
return 1;
|
|
@@ -304,7 +304,7 @@ export async function runAgentSmokeCurrentCommand(
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
const userInput = readOption(args, "--msg") ?? "Smoke test from nolo connector.";
|
|
307
|
-
const serverUrl = resolveServerUrl(env);
|
|
307
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
308
308
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
309
309
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
310
310
|
const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
|
@@ -415,13 +415,13 @@ export async function runAgentRuntimeDoctorCommand(
|
|
|
415
415
|
}
|
|
416
416
|
const agentKey = resolveCliAgentKeyInput(agentInput);
|
|
417
417
|
|
|
418
|
-
const authToken = resolveAuthToken(env);
|
|
418
|
+
const authToken = resolveAuthToken(args, env);
|
|
419
419
|
if (!authToken) {
|
|
420
420
|
output.write("[nolo] agent runtime-doctor requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
421
421
|
return 1;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
const serverUrl = resolveServerUrl(env);
|
|
424
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
425
425
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
426
426
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
427
427
|
const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
|
package/agentPullCommand.ts
CHANGED
|
@@ -4,8 +4,8 @@ import type { OutputLike } from "./cliIoTypes";
|
|
|
4
4
|
import { resolveCliAgentKeyInput } from "./agentAliases";
|
|
5
5
|
import {
|
|
6
6
|
readCliOptionValue,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
resolveAuthToken,
|
|
8
|
+
resolveServerUrl,
|
|
9
9
|
} from "./cliEnvHelpers";
|
|
10
10
|
|
|
11
11
|
type EnvLike = Record<string, string | undefined>;
|
|
@@ -44,14 +44,6 @@ export function parseAgentPullArgs(args: string[]): ParsedAgentPullArgs | null {
|
|
|
44
44
|
return { agentKey: resolveCliAgentKeyInput(agentKey) };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
function resolveServerUrl(env: EnvLike) {
|
|
48
|
-
return resolveServerUrlFromEnv(env);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function resolveAuthToken(env: EnvLike) {
|
|
52
|
-
return resolveAuthTokenFromEnv(env);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
47
|
function extractToolNames(record: any) {
|
|
56
48
|
if (Array.isArray(record?.toolNames)) return record.toolNames.filter((tool: unknown) => typeof tool === "string");
|
|
57
49
|
if (!Array.isArray(record?.tools)) return [];
|
|
@@ -113,8 +105,8 @@ export async function runAgentPullCommand(args: string[], deps: AgentPullCommand
|
|
|
113
105
|
return 1;
|
|
114
106
|
}
|
|
115
107
|
|
|
116
|
-
const serverUrl = resolveServerUrl(env);
|
|
117
|
-
const authToken = resolveAuthToken(env);
|
|
108
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
109
|
+
const authToken = resolveAuthToken(args, env);
|
|
118
110
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
119
111
|
const db = deps.db ?? await getDefaultCliLocalRuntimeDb();
|
|
120
112
|
|
package/agentRecordCommands.ts
CHANGED
|
@@ -21,7 +21,7 @@ export async function runAgentReadCommand(
|
|
|
21
21
|
return agentInput ? 0 : 1;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const authToken = resolveAuthToken(env);
|
|
24
|
+
const authToken = resolveAuthToken(args, env);
|
|
25
25
|
if (!authToken) {
|
|
26
26
|
output.write("[nolo] agent read requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
27
27
|
return 1;
|
|
@@ -35,6 +35,7 @@ export async function runAgentReadCommand(
|
|
|
35
35
|
try {
|
|
36
36
|
const result = await resolveAgentRecordFromHybridStore({
|
|
37
37
|
agentInput,
|
|
38
|
+
cliArgs: args,
|
|
38
39
|
env,
|
|
39
40
|
db,
|
|
40
41
|
fetchImpl,
|
|
@@ -79,7 +80,7 @@ export async function runAgentUpdateCommand(
|
|
|
79
80
|
return args[0] ? 0 : 1;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
const authToken = resolveAuthToken(env);
|
|
83
|
+
const authToken = resolveAuthToken(args, env);
|
|
83
84
|
if (!authToken) {
|
|
84
85
|
output.write("[nolo] agent update requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
85
86
|
return 1;
|
|
@@ -97,6 +98,7 @@ export async function runAgentUpdateCommand(
|
|
|
97
98
|
|
|
98
99
|
try {
|
|
99
100
|
const built = await buildUpdatedAgentRecord({
|
|
101
|
+
cliArgs: args,
|
|
100
102
|
parsed,
|
|
101
103
|
env,
|
|
102
104
|
db,
|
package/agentRecordHelpers.ts
CHANGED
|
@@ -262,14 +262,15 @@ export async function deleteDbRecord(args: {
|
|
|
262
262
|
|
|
263
263
|
export async function resolveAgentRecordFromHybridStore(args: {
|
|
264
264
|
agentInput: string;
|
|
265
|
+
cliArgs?: string[];
|
|
265
266
|
env: EnvLike;
|
|
266
267
|
db: CliKvDb;
|
|
267
268
|
fetchImpl: typeof fetch;
|
|
268
269
|
fallbackFetchImpl?: typeof fetch;
|
|
269
270
|
}) {
|
|
270
271
|
const agentKey = resolveCliAgentKeyInput(args.agentInput);
|
|
271
|
-
const authToken = resolveAuthToken(args.env);
|
|
272
|
-
const defaultServerUrl = resolveServerUrl(args.env);
|
|
272
|
+
const authToken = resolveAuthToken(args.cliArgs ?? [], args.env);
|
|
273
|
+
const defaultServerUrl = resolveServerUrl(args.cliArgs ?? [], args.env);
|
|
273
274
|
|
|
274
275
|
for (const key of buildLocalAgentLookupKeys({
|
|
275
276
|
agentRef: agentKey,
|
|
@@ -289,7 +290,7 @@ export async function resolveAgentRecordFromHybridStore(args: {
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
if (!shouldReadAgentKeyRemotely(key)) continue;
|
|
292
|
-
for (const serverUrl of resolveServerCandidates(args.env, defaultServerUrl)) {
|
|
293
|
+
for (const serverUrl of resolveServerCandidates(args.cliArgs ?? [], args.env, defaultServerUrl)) {
|
|
293
294
|
try {
|
|
294
295
|
const remoteRecord = await readAgentRecord({
|
|
295
296
|
agentKey: key,
|
|
@@ -380,6 +381,7 @@ export function parseAgentUpdateArgs(args: string[]) {
|
|
|
380
381
|
}
|
|
381
382
|
|
|
382
383
|
export async function buildUpdatedAgentRecord(args: {
|
|
384
|
+
cliArgs?: string[];
|
|
383
385
|
parsed: NonNullable<ReturnType<typeof parseAgentUpdateArgs>>;
|
|
384
386
|
env: EnvLike;
|
|
385
387
|
db: CliKvDb;
|
|
@@ -389,13 +391,14 @@ export async function buildUpdatedAgentRecord(args: {
|
|
|
389
391
|
}) {
|
|
390
392
|
const cached = await resolveAgentRecordFromHybridStore({
|
|
391
393
|
agentInput: args.parsed.agentInput,
|
|
394
|
+
cliArgs: args.cliArgs,
|
|
392
395
|
env: args.env,
|
|
393
396
|
db: args.db,
|
|
394
397
|
fetchImpl: args.fetchImpl,
|
|
395
398
|
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
396
399
|
});
|
|
397
400
|
const agentKey = cached?.agentKey ?? resolveCliAgentKeyInput(args.parsed.agentInput);
|
|
398
|
-
const serverUrl = cached?.record?.serverOrigin ?? resolveServerUrl(args.env);
|
|
401
|
+
const serverUrl = cached?.record?.serverOrigin ?? resolveServerUrl(args.cliArgs ?? [], args.env);
|
|
399
402
|
const currentRecord = await readAgentRecord({
|
|
400
403
|
agentKey,
|
|
401
404
|
authToken: args.authToken,
|
|
@@ -407,6 +410,7 @@ export async function buildUpdatedAgentRecord(args: {
|
|
|
407
410
|
if (args.parsed.copyProviderFrom) {
|
|
408
411
|
const providerSource = await resolveAgentRecordFromHybridStore({
|
|
409
412
|
agentInput: args.parsed.copyProviderFrom,
|
|
413
|
+
cliArgs: args.cliArgs,
|
|
410
414
|
env: args.env,
|
|
411
415
|
db: args.db,
|
|
412
416
|
fetchImpl: args.fetchImpl,
|
package/agentRunCommand.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
|
|
2
1
|
import { runAgentTurn, type RunAgentTurnResult } from "./client/agentRun";
|
|
3
2
|
import type { AgentRuntimeHostAdapter } from "./agentRuntimeLocal";
|
|
4
3
|
import {
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
type ParsedAgentRunArgs,
|
|
21
20
|
type ResolvedWorkflowReference,
|
|
22
21
|
} from "./agentRunParsing";
|
|
22
|
+
import { resolveServerUrl } from "./cliEnvHelpers";
|
|
23
23
|
|
|
24
24
|
type AgentRunCommandDeps = {
|
|
25
25
|
env?: EnvLike;
|
|
@@ -49,10 +49,6 @@ function shouldPrintLocalRunSummary(args: {
|
|
|
49
49
|
return Boolean(args.localRuntimeCwd && args.parsed.runtimeMode === "local");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function resolveServerUrl(env: EnvLike) {
|
|
53
|
-
return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
52
|
function writeUsage(output: OutputLike) {
|
|
57
53
|
output.write(
|
|
58
54
|
"Usage: nolo agent run <agent> <message> [--local|--server|--auto] [--continue <dialogId>] [--cwd <path>] [--max-tool-rounds <n>]\n" +
|
|
@@ -95,7 +91,7 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
95
91
|
const result: RunAgentTurnResult = await runner({
|
|
96
92
|
agentName: parsed.agentKey,
|
|
97
93
|
agentKey: parsed.agentKey,
|
|
98
|
-
serverUrl: resolveServerUrl(env),
|
|
94
|
+
serverUrl: resolveServerUrl(args, env),
|
|
99
95
|
message: prependWorkflowReferencePrompt(parsed.message, workflowReference),
|
|
100
96
|
imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
|
|
101
97
|
scriptDir: deps.scriptDir,
|
|
@@ -36,7 +36,7 @@ export async function runAgentUnpublishCommand(
|
|
|
36
36
|
return 1;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const authToken = resolveAuthToken(env);
|
|
39
|
+
const authToken = resolveAuthToken(args, env);
|
|
40
40
|
if (!authToken) {
|
|
41
41
|
output.write("[nolo] agent unpublish requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
42
42
|
return 1;
|
|
@@ -51,13 +51,14 @@ export async function runAgentUnpublishCommand(
|
|
|
51
51
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
52
52
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
53
53
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
54
|
-
const serverUrl = resolveServerUrl(env);
|
|
54
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
55
55
|
const privateKey = `agent-${userId}-${agentId}`;
|
|
56
56
|
const publicKey = `agent-pub-${agentId}`;
|
|
57
57
|
|
|
58
58
|
try {
|
|
59
59
|
const resolved = await resolveAgentRecordFromHybridStore({
|
|
60
60
|
agentInput: privateKey,
|
|
61
|
+
cliArgs: args,
|
|
61
62
|
env,
|
|
62
63
|
db,
|
|
63
64
|
fetchImpl,
|
package/cliEnvHelpers.ts
CHANGED
|
@@ -49,19 +49,13 @@ export function resolveServerUrlFromArgs(args: string[], env: EnvLike) {
|
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
type ServerResolverInput = string[] | EnvLike;
|
|
53
|
+
|
|
54
|
+
function isCliArgs(value: ServerResolverInput): value is string[] {
|
|
55
|
+
return Array.isArray(value);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
const values = [
|
|
58
|
-
preferred?.trim(),
|
|
59
|
-
env.NOLO_SERVER,
|
|
60
|
-
env.BASE_URL,
|
|
61
|
-
env.NOLO_SERVER_URL,
|
|
62
|
-
DEFAULT_NOLO_SERVER_URL,
|
|
63
|
-
...NOLO_CLUSTER_SERVERS,
|
|
64
|
-
];
|
|
58
|
+
function buildServerCandidates(values: Array<string | null | undefined>) {
|
|
65
59
|
return [...new Set(
|
|
66
60
|
values
|
|
67
61
|
.filter((value): value is string => typeof value === "string" && value.trim().length > 0)
|
|
@@ -69,8 +63,61 @@ export function resolveServerCandidates(env: EnvLike, preferred?: string | null)
|
|
|
69
63
|
)];
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
export function
|
|
73
|
-
|
|
66
|
+
export function resolveServerUrl(env: EnvLike): string;
|
|
67
|
+
export function resolveServerUrl(args: string[], env: EnvLike): string;
|
|
68
|
+
export function resolveServerUrl(input: ServerResolverInput, env?: EnvLike) {
|
|
69
|
+
if (isCliArgs(input)) {
|
|
70
|
+
return resolveServerUrlFromArgs(input, env ?? {});
|
|
71
|
+
}
|
|
72
|
+
return resolveServerUrlFromEnv(input);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveServerCandidates(env: EnvLike, preferred?: string | null): string[];
|
|
76
|
+
export function resolveServerCandidates(
|
|
77
|
+
args: string[],
|
|
78
|
+
env: EnvLike,
|
|
79
|
+
preferred?: string | null,
|
|
80
|
+
): string[];
|
|
81
|
+
export function resolveServerCandidates(
|
|
82
|
+
input: ServerResolverInput,
|
|
83
|
+
envOrPreferred?: EnvLike | string | null,
|
|
84
|
+
preferred?: string | null,
|
|
85
|
+
) {
|
|
86
|
+
if (isCliArgs(input)) {
|
|
87
|
+
const env = (envOrPreferred ?? {}) as EnvLike;
|
|
88
|
+
const explicitServer = resolveServerUrlFromArgs(input, env);
|
|
89
|
+
return buildServerCandidates([
|
|
90
|
+
explicitServer,
|
|
91
|
+
preferred?.trim(),
|
|
92
|
+
env.NOLO_SERVER,
|
|
93
|
+
env.BASE_URL,
|
|
94
|
+
env.NOLO_SERVER_URL,
|
|
95
|
+
DEFAULT_NOLO_SERVER_URL,
|
|
96
|
+
...NOLO_CLUSTER_SERVERS,
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
const env = input;
|
|
100
|
+
return buildServerCandidates([
|
|
101
|
+
typeof envOrPreferred === "string" ? envOrPreferred.trim() : undefined,
|
|
102
|
+
env.NOLO_SERVER,
|
|
103
|
+
env.BASE_URL,
|
|
104
|
+
env.NOLO_SERVER_URL,
|
|
105
|
+
DEFAULT_NOLO_SERVER_URL,
|
|
106
|
+
...NOLO_CLUSTER_SERVERS,
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function resolveAuthToken(env: EnvLike, extraEnvKeys?: string[]): string;
|
|
111
|
+
export function resolveAuthToken(args: string[], env: EnvLike, extraEnvKeys?: string[]): string;
|
|
112
|
+
export function resolveAuthToken(
|
|
113
|
+
input: ServerResolverInput,
|
|
114
|
+
envOrExtraEnvKeys?: EnvLike | string[],
|
|
115
|
+
extraEnvKeys: string[] = [],
|
|
116
|
+
) {
|
|
117
|
+
if (isCliArgs(input)) {
|
|
118
|
+
return resolveAuthTokenFromArgs(input, (envOrExtraEnvKeys ?? {}) as EnvLike, extraEnvKeys);
|
|
119
|
+
}
|
|
120
|
+
return resolveAuthTokenFromEnv(input, (envOrExtraEnvKeys as string[] | undefined) ?? []);
|
|
74
121
|
}
|
|
75
122
|
|
|
76
123
|
export function resolveAuthTokenFromEnv(env: EnvLike, extraEnvKeys: string[] = []) {
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export class ConnectorWebSocketAuthError extends Error {
|
|
2
|
+
code: string;
|
|
3
|
+
|
|
4
|
+
constructor(message: string, code = "CONNECTOR_AUTH_FAILED") {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "ConnectorWebSocketAuthError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isConnectorWebSocketAuthError(error: unknown): error is ConnectorWebSocketAuthError {
|
|
12
|
+
return error instanceof ConnectorWebSocketAuthError;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function resolveConnectorWebSocketTarget(input: {
|
|
16
|
+
serverUrl: string;
|
|
17
|
+
machineId: string;
|
|
4
18
|
headers: Record<string, string>;
|
|
5
19
|
fetchImpl?: typeof fetch;
|
|
6
20
|
}) {
|
|
@@ -14,13 +28,20 @@ export async function resolveConnectorWebSocketTarget(input: {
|
|
|
14
28
|
probeUrl.pathname = "/api/connector/ws";
|
|
15
29
|
probeUrl.search = directWsUrl.search;
|
|
16
30
|
|
|
17
|
-
const response = await (input.fetchImpl ?? fetch)(probeUrl, {
|
|
18
|
-
headers: input.headers,
|
|
19
|
-
}).catch(() => null);
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
const response = await (input.fetchImpl ?? fetch)(probeUrl, {
|
|
32
|
+
headers: input.headers,
|
|
33
|
+
}).catch(() => null);
|
|
34
|
+
|
|
35
|
+
if (response && (response.status === 401 || response.status === 403)) {
|
|
36
|
+
const json = await response.json().catch(() => null);
|
|
37
|
+
const code = typeof json?.code === "string" ? json.code : "CONNECTOR_AUTH_FAILED";
|
|
38
|
+
const message = typeof json?.error === "string" ? json.error : `Connector auth failed with HTTP ${response.status}`;
|
|
39
|
+
throw new ConnectorWebSocketAuthError(`${code}: ${message}`, code);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!response || !response.ok) {
|
|
43
|
+
return directWsUrl.toString();
|
|
44
|
+
}
|
|
24
45
|
|
|
25
46
|
const json = await response.json().catch(() => null);
|
|
26
47
|
return typeof json?.wsUrl === "string" && json.wsUrl
|
package/dialogReadCommands.ts
CHANGED
|
@@ -60,7 +60,7 @@ export async function runDialogReadCommand(
|
|
|
60
60
|
return args[0] ? 0 : 1;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const authToken = resolveAuthToken(env);
|
|
63
|
+
const authToken = resolveAuthToken(args, env);
|
|
64
64
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
65
65
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
66
66
|
|
|
@@ -68,6 +68,7 @@ export async function runDialogReadCommand(
|
|
|
68
68
|
const { base, dialogId, userId, spaceId } = parseDialogInputForCli(parsedArgs.rawInput, env);
|
|
69
69
|
const dialogKey = `dialog-${userId}-${dialogId}`;
|
|
70
70
|
const candidateBases = resolveServerCandidates(
|
|
71
|
+
args,
|
|
71
72
|
{
|
|
72
73
|
...env,
|
|
73
74
|
BASE_URL: env.READ_DIALOG_BASE?.trim() || base || env.BASE_URL,
|
package/dialogStatusCommands.ts
CHANGED
|
@@ -31,7 +31,7 @@ export async function runDialogStatusCommand(
|
|
|
31
31
|
return rawInput ? 0 : 1;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const authToken = resolveAuthToken(env);
|
|
34
|
+
const authToken = resolveAuthToken(args, env);
|
|
35
35
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
36
36
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
37
37
|
|
|
@@ -39,6 +39,7 @@ export async function runDialogStatusCommand(
|
|
|
39
39
|
const { base, dialogId, userId } = parseDialogInputForCli(rawInput, env);
|
|
40
40
|
const dialogKey = `dialog-${userId}-${dialogId}`;
|
|
41
41
|
const candidateBases = resolveServerCandidates(
|
|
42
|
+
args,
|
|
42
43
|
{
|
|
43
44
|
...env,
|
|
44
45
|
BASE_URL: env.READ_DIALOG_BASE?.trim() || base || env.BASE_URL,
|
package/machineCommands.ts
CHANGED
|
@@ -161,6 +161,7 @@ export async function runMachineConnectCommand(
|
|
|
161
161
|
}
|
|
162
162
|
),
|
|
163
163
|
});
|
|
164
|
+
if (exitCode === 2) return 1;
|
|
164
165
|
if (attempt >= maxAttempts) return exitCode;
|
|
165
166
|
output.write(`[nolo] Connector websocket disconnected. Reconnecting in ${reconnectDelayMs}ms.\n`);
|
|
166
167
|
await sleep(reconnectDelayMs);
|
package/machineStatusCommands.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
|
|
2
1
|
import type { OutputLike } from "./cliIoTypes";
|
|
2
|
+
import { resolveAuthToken, resolveServerUrl } from "./cliEnvHelpers";
|
|
3
3
|
|
|
4
4
|
type EnvLike = Record<string, string | undefined>;
|
|
5
5
|
|
|
@@ -21,14 +21,6 @@ export type MachineStatusCommandDeps = {
|
|
|
21
21
|
fetchImpl?: typeof fetch;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
function resolveServerUrl(env: EnvLike) {
|
|
25
|
-
return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function resolveAuthToken(env: EnvLike) {
|
|
29
|
-
return env.AUTH_TOKEN || env.AUTH || "";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
24
|
function writeAuthMissing(output: OutputLike) {
|
|
33
25
|
output.write(
|
|
34
26
|
"[nolo] Machine commands require an auth token. Run `nolo login` or set AUTH_TOKEN.\n"
|
|
@@ -52,18 +44,18 @@ export function formatMachineStatus(machines: MachineSummary[]) {
|
|
|
52
44
|
}
|
|
53
45
|
|
|
54
46
|
export async function runMachineStatusCommand(
|
|
55
|
-
|
|
47
|
+
args: string[],
|
|
56
48
|
deps: MachineStatusCommandDeps = {}
|
|
57
49
|
) {
|
|
58
50
|
const env = deps.env ?? process.env;
|
|
59
51
|
const output = deps.output ?? process.stdout;
|
|
60
|
-
const authToken = resolveAuthToken(env);
|
|
52
|
+
const authToken = resolveAuthToken(args, env);
|
|
61
53
|
if (!authToken) {
|
|
62
54
|
writeAuthMissing(output);
|
|
63
55
|
return 1;
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
const serverUrl = resolveServerUrl(env);
|
|
58
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
67
59
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
68
60
|
let res: Response;
|
|
69
61
|
try {
|
package/machineWsSession.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HeartbeatLoopOptions } from "./connector-experimental/heartbeatLoop";
|
|
2
2
|
import type { MachineHeartbeat } from "./connector-experimental/protocol";
|
|
3
|
+
import { isConnectorWebSocketAuthError } from "./connectorWebSocketTarget";
|
|
3
4
|
|
|
4
5
|
type EnvLike = Record<string, string | undefined>;
|
|
5
6
|
type OutputLike = { write(chunk: string): unknown };
|
|
@@ -138,6 +139,12 @@ export async function runMachineWsSession(options: {
|
|
|
138
139
|
await Promise.allSettled(
|
|
139
140
|
[websocketPromise, heartbeatLoopPromise].filter(Boolean) as Promise<void>[],
|
|
140
141
|
);
|
|
142
|
+
if (isConnectorWebSocketAuthError(error)) {
|
|
143
|
+
options.output.write(
|
|
144
|
+
`[nolo] Connector websocket auth failed: ${error.message}. Re-register this machine or update NOLO_MACHINE_API_KEY.\n`
|
|
145
|
+
);
|
|
146
|
+
return 2;
|
|
147
|
+
}
|
|
141
148
|
options.output.write(
|
|
142
149
|
`[nolo] Connector websocket failed: ${
|
|
143
150
|
error instanceof Error ? error.message : String(error)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nolo-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first terminal workspace for Nolo",
|
|
6
6
|
"bin": {
|
|
@@ -110,10 +110,10 @@
|
|
|
110
110
|
"ai/**/*.ts",
|
|
111
111
|
"chat/**/*.ts",
|
|
112
112
|
"render/**/*.ts",
|
|
113
|
-
"core/**/*.ts",
|
|
114
113
|
"auth/**/*.ts",
|
|
115
|
-
"life/**/*.ts",
|
|
116
114
|
"share/**/*.ts",
|
|
115
|
+
"life/**/*.ts",
|
|
116
|
+
"core/**/*.ts",
|
|
117
117
|
"server/**/*.ts",
|
|
118
118
|
"integrations/**/*.ts"
|
|
119
119
|
],
|
|
@@ -136,24 +136,23 @@
|
|
|
136
136
|
"tweetnacl": "^1.0.3",
|
|
137
137
|
"@reduxjs/toolkit": "^2.5.0",
|
|
138
138
|
"react-redux": "^9.2.0",
|
|
139
|
-
"react-hot-toast": "^2.4.1",
|
|
140
139
|
"react": "^19.2.1",
|
|
141
|
-
"
|
|
140
|
+
"react-hot-toast": "^2.4.1",
|
|
142
141
|
"react-i18next": "^14.1.2",
|
|
143
142
|
"i18next": "^23.11.5",
|
|
144
|
-
"
|
|
143
|
+
"browser-image-compression": "^2.0.2",
|
|
145
144
|
"pino": "^9.9.0",
|
|
145
|
+
"ulid": "^2.3.0",
|
|
146
146
|
"slate": "^0.112.0",
|
|
147
|
+
"slate-react": "^0.112.1",
|
|
148
|
+
"pdfjs-dist": "^5.2.133",
|
|
149
|
+
"slate-history": "^0.113.1",
|
|
147
150
|
"mdast-util-from-markdown": "^2.0.2",
|
|
148
151
|
"mdast-util-gfm": "^3.0.0",
|
|
149
152
|
"micromark-extension-gfm": "^3.0.0",
|
|
150
|
-
"slate-history": "^0.113.1",
|
|
151
|
-
"slate-react": "^0.112.1",
|
|
152
|
-
"pdfjs-dist": "^5.2.133",
|
|
153
|
-
"rambda": "^8.6.0",
|
|
154
|
-
"react-native-blob-util": "^0.24.7",
|
|
155
153
|
"@react-native-community/netinfo": "^11.5.0",
|
|
156
|
-
"react-native-
|
|
154
|
+
"react-native-blob-util": "^0.24.7",
|
|
155
|
+
"rambda": "^8.6.0",
|
|
157
156
|
"@lobehub/icons": "^5.0.1",
|
|
158
157
|
"pizzip": "^3.2.0",
|
|
159
158
|
"docxtemplater": "^3.67.5",
|
|
@@ -161,16 +160,17 @@
|
|
|
161
160
|
"zod": "^3.25.20",
|
|
162
161
|
"react-hook-form": "^7.51.5",
|
|
163
162
|
"@hookform/resolvers": "^3.3.2",
|
|
163
|
+
"react-native-sse": "^1.2.1",
|
|
164
|
+
"xlsx": "^0.18.5",
|
|
164
165
|
"react-router-dom": "^6.23.1",
|
|
165
166
|
"croner": "^8.0.1",
|
|
166
|
-
"xlsx": "^0.18.5",
|
|
167
167
|
"mermaid": "^11.6.0",
|
|
168
|
-
"js-base64": "^3.7.7",
|
|
169
168
|
"react-native-keychain": "^9.2.2",
|
|
170
169
|
"async-mutex": "^0.5.0",
|
|
171
|
-
"
|
|
170
|
+
"js-base64": "^3.7.7",
|
|
172
171
|
"react-dom": "^19.2.1",
|
|
173
|
-
"
|
|
174
|
-
"playwright": "^1.59.1"
|
|
172
|
+
"diff": "^8.0.2",
|
|
173
|
+
"playwright": "^1.59.1",
|
|
174
|
+
"@waffo/pancake-ts": "^0.5.2"
|
|
175
175
|
}
|
|
176
176
|
}
|
package/pageCreateCommands.ts
CHANGED
|
@@ -66,7 +66,7 @@ async function runBasePageCreateCommand(
|
|
|
66
66
|
return 1;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const authToken = resolveAuthToken(env);
|
|
69
|
+
const authToken = resolveAuthToken(args, env);
|
|
70
70
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
71
71
|
if (!authToken || !userId) {
|
|
72
72
|
output.write(`[nolo] ${mode} create requires an auth token with userId. Run \`nolo login\`.\n`);
|
|
@@ -82,7 +82,7 @@ async function runBasePageCreateCommand(
|
|
|
82
82
|
return 1;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const serverUrl = resolveServerUrl(env);
|
|
85
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
86
86
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
87
87
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
88
88
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
package/pageDeleteCommands.ts
CHANGED
|
@@ -54,7 +54,7 @@ export async function runBasePageDeleteCommand(
|
|
|
54
54
|
return dbKey ? 0 : 1;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
const authToken = resolveAuthToken(env);
|
|
57
|
+
const authToken = resolveAuthToken(args, env);
|
|
58
58
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
59
59
|
if (!authToken || !userId) {
|
|
60
60
|
output.write(`[nolo] ${mode} delete requires an auth token with userId. Run \`nolo login\`.\n`);
|
|
@@ -64,8 +64,8 @@ export async function runBasePageDeleteCommand(
|
|
|
64
64
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
65
65
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
66
66
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
67
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
68
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
67
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
68
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
const existing = await readRecord({
|
package/pageReadCommands.ts
CHANGED
|
@@ -76,7 +76,7 @@ async function runBasePageReadCommand(
|
|
|
76
76
|
return raw ? 0 : 1;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const authToken = resolveAuthToken(env);
|
|
79
|
+
const authToken = resolveAuthToken(args, env);
|
|
80
80
|
if (!authToken) {
|
|
81
81
|
output.write(`[nolo] ${mode} read requires an auth token. Run \`nolo login\` or set AUTH_TOKEN.\n`);
|
|
82
82
|
return 1;
|
|
@@ -85,8 +85,8 @@ async function runBasePageReadCommand(
|
|
|
85
85
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
86
86
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
87
87
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
88
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
89
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
88
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
89
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
90
90
|
|
|
91
91
|
try {
|
|
92
92
|
const record = await readPageRecord({
|
package/pageUpdateCommands.ts
CHANGED
|
@@ -63,7 +63,7 @@ async function runBasePageUpdateCommand(
|
|
|
63
63
|
return dbKey ? 0 : 1;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const authToken = resolveAuthToken(env);
|
|
66
|
+
const authToken = resolveAuthToken(args, env);
|
|
67
67
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
68
68
|
if (!authToken || !userId) {
|
|
69
69
|
output.write(`[nolo] ${mode} update requires an auth token with userId. Run \`nolo login\`.\n`);
|
|
@@ -73,8 +73,8 @@ async function runBasePageUpdateCommand(
|
|
|
73
73
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
74
74
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
75
75
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
76
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
77
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
76
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
77
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
78
78
|
const writeTargets = [defaultServerUrl];
|
|
79
79
|
|
|
80
80
|
try {
|
package/runtimeDoctorCommands.ts
CHANGED
|
@@ -64,13 +64,13 @@ async function defaultLocalRuntimeProbe(env: EnvLike): Promise<LocalRuntimeProbe
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export async function runDoctorRuntimeCommand(
|
|
67
|
-
|
|
67
|
+
args: string[],
|
|
68
68
|
deps: AgentCommandDeps = {}
|
|
69
69
|
) {
|
|
70
70
|
const env = deps.env ?? process.env;
|
|
71
71
|
const output = deps.output ?? process.stdout;
|
|
72
|
-
const authToken = resolveAuthToken(env);
|
|
73
|
-
const serverUrl = resolveServerUrl(env);
|
|
72
|
+
const authToken = resolveAuthToken(args, env);
|
|
73
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
74
74
|
const localProbe = await (deps.localRuntimeProbe ?? defaultLocalRuntimeProbe)(env);
|
|
75
75
|
const hasLocalAgentConfig = detectLocalAgentConfig(env) && localProbe.ok && localProbe.agentFound;
|
|
76
76
|
const hasLocalProvider = detectLocalProvider(env);
|
package/spaceCategoryCommands.ts
CHANGED
|
@@ -49,7 +49,7 @@ export async function runSpaceCategoryCommand(
|
|
|
49
49
|
return 1;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const authToken = resolveAuthToken(env);
|
|
52
|
+
const authToken = resolveAuthToken(args, env);
|
|
53
53
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
54
54
|
if (!authToken || !userId) {
|
|
55
55
|
output.write("[nolo] space category requires an auth token with userId. Run `nolo login`.\n");
|
|
@@ -66,8 +66,8 @@ export async function runSpaceCategoryCommand(
|
|
|
66
66
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
67
67
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
68
68
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
69
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
70
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
69
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
70
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
71
71
|
|
|
72
72
|
try {
|
|
73
73
|
const spaceId = parseSpaceIdFromInput(rawSpace);
|
|
@@ -163,7 +163,7 @@ export async function runSpaceContentCategoryCommand(
|
|
|
163
163
|
return 1;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
const authToken = resolveAuthToken(env);
|
|
166
|
+
const authToken = resolveAuthToken(args, env);
|
|
167
167
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
168
168
|
if (!authToken || !userId) {
|
|
169
169
|
output.write("[nolo] space content-category requires an auth token with userId. Run `nolo login`.\n");
|
|
@@ -189,8 +189,8 @@ export async function runSpaceContentCategoryCommand(
|
|
|
189
189
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
190
190
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
191
191
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
192
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
193
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
192
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
193
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
194
194
|
|
|
195
195
|
try {
|
|
196
196
|
const spaceId = parseSpaceIdFromInput(rawSpace);
|
package/spaceCommands.ts
CHANGED
|
@@ -181,7 +181,7 @@ export async function runSpaceListCommand(
|
|
|
181
181
|
const output = deps.output ?? process.stdout;
|
|
182
182
|
const filters = parseSpaceListArgs(args);
|
|
183
183
|
|
|
184
|
-
const authToken = resolveAuthToken(env);
|
|
184
|
+
const authToken = resolveAuthToken(args, env);
|
|
185
185
|
if (!authToken) {
|
|
186
186
|
output.write("[nolo] space list requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
187
187
|
return 1;
|
|
@@ -196,8 +196,8 @@ export async function runSpaceListCommand(
|
|
|
196
196
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
197
197
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
198
198
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
199
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
200
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
199
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
200
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
201
201
|
|
|
202
202
|
try {
|
|
203
203
|
let spaces: ListedSpace[];
|
package/spaceDeleteCommands.ts
CHANGED
|
@@ -195,7 +195,7 @@ export async function runSpaceDeleteCommand(
|
|
|
195
195
|
return 1;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
const authToken = resolveAuthToken(env);
|
|
198
|
+
const authToken = resolveAuthToken(args, env);
|
|
199
199
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
200
200
|
if (!authToken || !userId) {
|
|
201
201
|
output.write("[nolo] space delete requires an auth token with userId. Run `nolo login`.\n");
|
|
@@ -207,7 +207,7 @@ export async function runSpaceDeleteCommand(
|
|
|
207
207
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
208
208
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
209
209
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
210
|
-
const serverUrl = resolveServerUrl(env);
|
|
210
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
211
211
|
|
|
212
212
|
try {
|
|
213
213
|
const memberships = await fetchMemberships({
|
package/spaceReadCommands.ts
CHANGED
|
@@ -79,7 +79,7 @@ export async function runSpaceReadCommand(
|
|
|
79
79
|
const output = deps.output ?? process.stdout;
|
|
80
80
|
const { raw, brief, contentKeyFilter, includeRecord } = parseSpaceReadArgs(args);
|
|
81
81
|
|
|
82
|
-
const authToken = resolveAuthToken(env);
|
|
82
|
+
const authToken = resolveAuthToken(args, env);
|
|
83
83
|
if (!authToken) {
|
|
84
84
|
output.write("[nolo] space read requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
85
85
|
return 1;
|
|
@@ -94,8 +94,8 @@ export async function runSpaceReadCommand(
|
|
|
94
94
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
95
95
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
96
96
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
97
|
-
const defaultServerUrl = resolveServerUrl(env);
|
|
98
|
-
const serverCandidates = resolveServerCandidates(env, defaultServerUrl);
|
|
97
|
+
const defaultServerUrl = resolveServerUrl(args, env);
|
|
98
|
+
const serverCandidates = resolveServerCandidates(args, env, defaultServerUrl);
|
|
99
99
|
|
|
100
100
|
try {
|
|
101
101
|
const spaceId = parseSpaceIdFromInput(raw);
|
package/tableMetaCommands.ts
CHANGED
|
@@ -158,7 +158,7 @@ export async function runTableMetaCommand(
|
|
|
158
158
|
return 1;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
const authToken = resolveAuthToken(env);
|
|
161
|
+
const authToken = resolveAuthToken(args, env);
|
|
162
162
|
const userId = parseUserIdFromAuthToken(authToken);
|
|
163
163
|
if (!authToken || !userId) {
|
|
164
164
|
output.write("[nolo] table meta requires an auth token with userId. Run `nolo login`.\n");
|
|
@@ -168,7 +168,7 @@ export async function runTableMetaCommand(
|
|
|
168
168
|
const name = rawName.trim();
|
|
169
169
|
const description = readOption(args, "--description")?.trim() || undefined;
|
|
170
170
|
const tags = parseTags(readOption(args, "--tags"));
|
|
171
|
-
const serverUrl = resolveServerUrl(env);
|
|
171
|
+
const serverUrl = resolveServerUrl(args, env);
|
|
172
172
|
const db = deps.db ?? await getReadableCliDb(output);
|
|
173
173
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
174
174
|
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
package/taskRunCommand.ts
CHANGED
|
@@ -13,6 +13,15 @@ type TaskRunCommandDeps = {
|
|
|
13
13
|
fetchImpl?: typeof fetch;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
const DEFAULT_TASK_RUN_TIMEOUT_MS = 10 * 60 * 1000;
|
|
17
|
+
|
|
18
|
+
function resolveTaskRunTimeoutMs(env: EnvLike) {
|
|
19
|
+
const raw = env.NOLO_TASK_RUN_TIMEOUT_MS;
|
|
20
|
+
if (!raw) return DEFAULT_TASK_RUN_TIMEOUT_MS;
|
|
21
|
+
const value = Number(raw);
|
|
22
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_TASK_RUN_TIMEOUT_MS;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
export async function runTaskRunCommand(args: string[], deps: TaskRunCommandDeps = {}) {
|
|
17
26
|
const env = loadTaskRunEnv(deps.env ?? process.env, { includeDefaultEnvFiles: deps.env === undefined });
|
|
18
27
|
const output = deps.output ?? process.stdout;
|
|
@@ -38,6 +47,7 @@ export async function runTaskRunCommand(args: string[], deps: TaskRunCommandDeps
|
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
const serverUrl = resolveServerUrl(args, env);
|
|
50
|
+
const timeoutMs = resolveTaskRunTimeoutMs(env);
|
|
41
51
|
let response: Response;
|
|
42
52
|
try {
|
|
43
53
|
response = await fetchImpl(`${serverUrl}/api/task-run`, {
|
|
@@ -47,6 +57,7 @@ export async function runTaskRunCommand(args: string[], deps: TaskRunCommandDeps
|
|
|
47
57
|
"Content-Type": "application/json",
|
|
48
58
|
},
|
|
49
59
|
body: JSON.stringify(body),
|
|
60
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
50
61
|
});
|
|
51
62
|
} catch (error) {
|
|
52
63
|
output.write(`[nolo] task-run failed: ${error instanceof Error ? error.message : String(error)}\n`);
|