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.
Files changed (76) hide show
  1. package/README.md +25 -3
  2. package/agent-runtime/hybridRecordStore.ts +30 -9
  3. package/agent-runtime/localLoop.ts +12 -4
  4. package/agent-runtime/localToolPolicy.ts +22 -10
  5. package/agent-runtime/localWorkspaceTools.ts +140 -374
  6. package/agent-runtime/runtimeToolPolicy.ts +43 -2
  7. package/agent-runtime/types.ts +5 -0
  8. package/agentAliases.ts +1 -1
  9. package/agentCommandSupport.ts +77 -0
  10. package/agentInternalCommandEntries.ts +34 -0
  11. package/agentListCommands.ts +123 -0
  12. package/agentListHelpers.ts +187 -0
  13. package/agentMachineCommands.ts +402 -0
  14. package/agentRecordCommands.ts +141 -0
  15. package/agentRecordHelpers.ts +395 -0
  16. package/agentRunCommand.ts +1 -2
  17. package/agentRuntimeCommands.ts +7 -701
  18. package/ai/agent/_executeModel.ts +29 -3
  19. package/ai/agent/buildSystemPrompt.ts +17 -0
  20. package/ai/agent/cliChatClient.ts +38 -4
  21. package/ai/agent/cliExecutor.ts +304 -25
  22. package/ai/agent/createAgentSchema.ts +2 -2
  23. package/ai/agent/guidedCreation/assistant.ts +82 -0
  24. package/ai/agent/guidedCreation/capabilities.ts +84 -0
  25. package/ai/agent/guidedCreation/draft.ts +62 -0
  26. package/ai/agent/guidedCreation/types.ts +38 -0
  27. package/ai/agent/machineRunPermissions.ts +21 -3
  28. package/ai/agent/runtimeGuidance.ts +1 -1
  29. package/ai/agent/server/fetchPublicAgents.ts +6 -1
  30. package/ai/agent/streamAgentChatTurn.ts +492 -19
  31. package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
  32. package/ai/ai.locale.ts +14 -2
  33. package/ai/chat/sendOpenAICompletionsRequest.ts +28 -3
  34. package/ai/chat/sendOpenAIResponseRequest.ts +3 -2
  35. package/ai/llm/getPricing.ts +14 -0
  36. package/ai/llm/kimi.ts +3 -0
  37. package/ai/llm/modelAvatar.ts +1 -0
  38. package/ai/llm/providers.ts +1 -1
  39. package/ai/taskRun/taskRunProtocol.ts +24 -10
  40. package/ai/token/modelUsageQuery.ts +17 -0
  41. package/ai/tools/agent/agentTools.ts +21 -0
  42. package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
  43. package/ai/tools/checkEnvTool.ts +52 -20
  44. package/ai/tools/convertMarxistsBookTool.ts +103 -0
  45. package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
  46. package/ai/tools/index.ts +24 -14
  47. package/ai/tools/marxistsOfflineBook.ts +253 -0
  48. package/ai/tools/modelUsageTools.ts +4 -2
  49. package/ai/tools/readTool.ts +31 -0
  50. package/ai/tools/toolApiClient.ts +35 -1
  51. package/ai/tools/toolNameAliases.ts +13 -0
  52. package/cliCommandAdapters.ts +28 -0
  53. package/cliCommandDispatch.ts +66 -0
  54. package/cliCommandFactories.ts +34 -0
  55. package/cliCommandTypes.ts +49 -0
  56. package/cliEnvHelpers.ts +44 -0
  57. package/client/agentRun.test.ts +123 -19
  58. package/client/agentRun.ts +40 -1
  59. package/client/hybridRecordStore.test.ts +42 -0
  60. package/client/hybridRecordStore.ts +11 -0
  61. package/client/localRuntimeAdapter.test.ts +189 -21
  62. package/client/localRuntimeAdapter.ts +84 -8
  63. package/client/localRuntimeDryRun.test.ts +5 -2
  64. package/client/taskRunPrompt.ts +1 -0
  65. package/commandRegistry.ts +11 -61
  66. package/connector-experimental/capabilities.ts +1 -0
  67. package/database/server/coreDataProxy.ts +43 -26
  68. package/index.ts +54 -191
  69. package/internalCommandEntries.ts +12 -0
  70. package/machineCommands.ts +301 -32
  71. package/offlineMarxistsAgentCommand.ts +314 -0
  72. package/package.json +20 -2
  73. package/runtimeDoctorCommands.ts +118 -0
  74. package/scriptCommandEntries.ts +71 -0
  75. package/systemInternalCommandEntries.ts +71 -0
  76. package/workflowInternalCommandEntries.ts +11 -0
@@ -0,0 +1,314 @@
1
+ import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
2
+
3
+ type EnvLike = Record<string, string | undefined>;
4
+
5
+ type OutputLike = {
6
+ write(chunk: string): unknown;
7
+ };
8
+
9
+ type SetupOfflineMarxistsAgentDeps = {
10
+ env?: EnvLike;
11
+ output?: OutputLike;
12
+ fetchImpl?: typeof fetch;
13
+ now?: () => number;
14
+ };
15
+
16
+ type ParsedArgs = {
17
+ serverUrl: string;
18
+ authToken: string;
19
+ userId: string;
20
+ sourceAgentKey: string;
21
+ targetAgentId: string;
22
+ targetAgentKey: string;
23
+ spaceId: string;
24
+ name: string;
25
+ publicAgent: boolean;
26
+ json: boolean;
27
+ };
28
+
29
+ const DEFAULT_USER_ID = "b2e06f801f";
30
+ const DEFAULT_SPACE_ID = "01KKY77TT0DA9NY7TNW3R7255N";
31
+ const DEFAULT_SOURCE_AGENT_ID = "01MIMO25MONTH00000001PGT4X";
32
+ const DEFAULT_TARGET_AGENT_ID = "01OFFMARXBOOK000000010AHL1";
33
+ const DEFAULT_AGENT_NAME = "离线马克思主义文库书籍转换助手";
34
+ const TOOL_NAME = "convertMarxistsBookToOfflineHtml";
35
+ const AGENT_TOOLS = [TOOL_NAME];
36
+
37
+ function readFlagValue(args: string[], flag: string) {
38
+ const index = args.indexOf(flag);
39
+ return index >= 0 ? args[index + 1] : undefined;
40
+ }
41
+
42
+ function parseUserIdFromToken(token: string) {
43
+ const payload = token.split(".")[0];
44
+ if (!payload) return "";
45
+ try {
46
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
47
+ const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4);
48
+ const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
49
+ return typeof parsed?.userId === "string" ? parsed.userId : "";
50
+ } catch {
51
+ return "";
52
+ }
53
+ }
54
+
55
+ function normalizeBaseUrl(value: string) {
56
+ return value.trim().replace(/\/+$/, "");
57
+ }
58
+
59
+ function parseArgs(args: string[], env: EnvLike): ParsedArgs | null {
60
+ if (args.includes("--help") || args.includes("-h")) return null;
61
+ const serverUrl = normalizeBaseUrl(
62
+ readFlagValue(args, "--server") ??
63
+ readFlagValue(args, "--server-url") ??
64
+ env.NOLO_SERVER ??
65
+ env.BASE_URL ??
66
+ DEFAULT_NOLO_SERVER_URL
67
+ );
68
+ const authToken = readFlagValue(args, "--token") ?? env.AUTH_TOKEN ?? env.NOLO_AUTH_TOKEN ?? "";
69
+ const userId =
70
+ readFlagValue(args, "--user-id") ??
71
+ env.NOLO_USER_ID ??
72
+ parseUserIdFromToken(authToken) ??
73
+ DEFAULT_USER_ID;
74
+ const sourceAgentKey =
75
+ readFlagValue(args, "--source-agent") ??
76
+ env.NOLO_MIMO_SOURCE_AGENT_KEY ??
77
+ `agent-${userId}-${DEFAULT_SOURCE_AGENT_ID}`;
78
+ const targetAgentId =
79
+ readFlagValue(args, "--target-agent-id") ??
80
+ env.NOLO_OFFLINE_MARXISTS_AGENT_ID ??
81
+ DEFAULT_TARGET_AGENT_ID;
82
+ const targetAgentKey =
83
+ readFlagValue(args, "--target-agent") ??
84
+ env.NOLO_OFFLINE_MARXISTS_AGENT_KEY ??
85
+ `agent-${userId}-${targetAgentId}`;
86
+ const spaceId =
87
+ readFlagValue(args, "--space") ??
88
+ env.NOLO_OFFLINE_MARXISTS_SPACE_ID ??
89
+ DEFAULT_SPACE_ID;
90
+
91
+ if (!authToken.trim()) return null;
92
+ return {
93
+ serverUrl,
94
+ authToken,
95
+ userId,
96
+ sourceAgentKey,
97
+ targetAgentId,
98
+ targetAgentKey,
99
+ spaceId,
100
+ name: readFlagValue(args, "--name") ?? DEFAULT_AGENT_NAME,
101
+ publicAgent: args.includes("--public"),
102
+ json: args.includes("--json"),
103
+ };
104
+ }
105
+
106
+ async function readDbRecord(args: {
107
+ fetchImpl: typeof fetch;
108
+ serverUrl: string;
109
+ authToken: string;
110
+ dbKey: string;
111
+ }) {
112
+ const res = await args.fetchImpl(
113
+ `${args.serverUrl}/api/v1/db/read/${encodeURIComponent(args.dbKey)}`,
114
+ { headers: { Authorization: `Bearer ${args.authToken}` } }
115
+ );
116
+ const data = await res.json().catch(() => ({}));
117
+ if (!res.ok) {
118
+ throw new Error(`read ${args.dbKey} failed (${res.status}): ${JSON.stringify(data)}`);
119
+ }
120
+ return (data as any)?.data ?? data;
121
+ }
122
+
123
+ async function writeDbRecord(args: {
124
+ fetchImpl: typeof fetch;
125
+ serverUrl: string;
126
+ authToken: string;
127
+ userId: string;
128
+ dbKey: string;
129
+ data: Record<string, any>;
130
+ }) {
131
+ const res = await args.fetchImpl(`${args.serverUrl}/api/v1/db/write/`, {
132
+ method: "POST",
133
+ headers: {
134
+ "Content-Type": "application/json",
135
+ Authorization: `Bearer ${args.authToken}`,
136
+ },
137
+ body: JSON.stringify({
138
+ customKey: args.dbKey,
139
+ userId: args.userId,
140
+ data: { ...args.data, dbKey: args.dbKey },
141
+ }),
142
+ });
143
+ const data = await res.json().catch(() => ({}));
144
+ if (!res.ok) {
145
+ throw new Error(`write ${args.dbKey} failed (${res.status}): ${JSON.stringify(data)}`);
146
+ }
147
+ }
148
+
149
+ function buildTargetAgent(args: {
150
+ parsed: ParsedArgs;
151
+ source: Record<string, any>;
152
+ now: number;
153
+ }) {
154
+ const { parsed, source, now } = args;
155
+ if (!source.apiKey) {
156
+ throw new Error(`source agent ${parsed.sourceAgentKey} has no apiKey`);
157
+ }
158
+ if (!source.customProviderUrl) {
159
+ throw new Error(`source agent ${parsed.sourceAgentKey} has no customProviderUrl`);
160
+ }
161
+ return {
162
+ id: parsed.targetAgentId,
163
+ type: "agent",
164
+ name: parsed.name,
165
+ description:
166
+ "把 Marxists.org 中文旧式书籍页面转换为可离线打开的单文件 HTML,保留原 CSS、表格排版和背景纹理。",
167
+ prompt:
168
+ "你是离线书籍转换助手。用户给出 Marxists.org 中文书籍页面或要求保存离线书籍时,只能调用 convertMarxistsBookToOfflineHtml,不要使用普通网页抓取流程。最终回答必须逐字给出 rawData.fileUrl、rawData.pageCount、rawData.hasNetworkUrls、rawData.byteLength。",
169
+ isPublic: parsed.publicAgent,
170
+ model: source.model ?? "mimo-v2.5-pro",
171
+ provider: source.provider ?? "custom",
172
+ apiSource: source.apiSource ?? "custom",
173
+ apiKey: source.apiKey,
174
+ customProviderUrl: source.customProviderUrl,
175
+ apiKeyHeader: source.apiKeyHeader ?? "api-key",
176
+ tools: AGENT_TOOLS,
177
+ temperature: source.temperature ?? 0.2,
178
+ maxTokens: source.maxTokens ?? 4096,
179
+ category: "document-tools",
180
+ tags: ["offline-book", "marxists", "mimo"],
181
+ userId: parsed.userId,
182
+ createdAt: source.createdAt ?? now,
183
+ updatedAt: now,
184
+ };
185
+ }
186
+
187
+ async function attachAgentToSpace(args: {
188
+ fetchImpl: typeof fetch;
189
+ parsed: ParsedArgs;
190
+ contentKey: string;
191
+ title: string;
192
+ now: number;
193
+ }) {
194
+ const spaceKey = `space-${args.parsed.spaceId}`;
195
+ const space = await readDbRecord({
196
+ fetchImpl: args.fetchImpl,
197
+ serverUrl: args.parsed.serverUrl,
198
+ authToken: args.parsed.authToken,
199
+ dbKey: spaceKey,
200
+ });
201
+ await writeDbRecord({
202
+ fetchImpl: args.fetchImpl,
203
+ serverUrl: args.parsed.serverUrl,
204
+ authToken: args.parsed.authToken,
205
+ userId: args.parsed.userId,
206
+ dbKey: spaceKey,
207
+ data: {
208
+ ...space,
209
+ contents: {
210
+ ...(space.contents ?? {}),
211
+ [args.contentKey]: {
212
+ ...(space.contents?.[args.contentKey] ?? {}),
213
+ title: args.title,
214
+ type: "agent",
215
+ contentKey: args.contentKey,
216
+ createdAt: space.contents?.[args.contentKey]?.createdAt ?? args.now,
217
+ updatedAt: args.now,
218
+ },
219
+ },
220
+ updatedAt: args.now,
221
+ },
222
+ });
223
+ }
224
+
225
+ function writeUsage(output: OutputLike) {
226
+ output.write(
227
+ "Usage: nolo agent setup-offline-marxists [--server https://nolo.chat] [--source-agent <key>] [--space <spaceId>] [--target-agent-id <id>] [--json]\n" +
228
+ "Requires AUTH_TOKEN from `nolo login` or --token.\n"
229
+ );
230
+ }
231
+
232
+ export async function runSetupOfflineMarxistsAgentCommand(
233
+ args: string[],
234
+ deps: SetupOfflineMarxistsAgentDeps = {}
235
+ ) {
236
+ const env = deps.env ?? process.env;
237
+ const output = deps.output ?? process.stdout;
238
+ const parsed = parseArgs(args, env);
239
+ if (!parsed) {
240
+ writeUsage(output);
241
+ return 1;
242
+ }
243
+ const fetchImpl = deps.fetchImpl ?? fetch;
244
+ const now = deps.now?.() ?? Date.now();
245
+
246
+ try {
247
+ const source = await readDbRecord({
248
+ fetchImpl,
249
+ serverUrl: parsed.serverUrl,
250
+ authToken: parsed.authToken,
251
+ dbKey: parsed.sourceAgentKey,
252
+ });
253
+ const target = buildTargetAgent({ parsed, source, now });
254
+ await writeDbRecord({
255
+ fetchImpl,
256
+ serverUrl: parsed.serverUrl,
257
+ authToken: parsed.authToken,
258
+ userId: parsed.userId,
259
+ dbKey: parsed.targetAgentKey,
260
+ data: target,
261
+ });
262
+ const publicKey = `agent-pub-${parsed.targetAgentId}`;
263
+ if (parsed.publicAgent) {
264
+ await writeDbRecord({
265
+ fetchImpl,
266
+ serverUrl: parsed.serverUrl,
267
+ authToken: parsed.authToken,
268
+ userId: parsed.userId,
269
+ dbKey: publicKey,
270
+ data: target,
271
+ });
272
+ }
273
+ await attachAgentToSpace({
274
+ fetchImpl,
275
+ parsed,
276
+ contentKey: parsed.publicAgent ? publicKey : parsed.targetAgentKey,
277
+ title: parsed.name,
278
+ now,
279
+ });
280
+
281
+ const result = {
282
+ serverUrl: parsed.serverUrl,
283
+ sourceAgentKey: parsed.sourceAgentKey,
284
+ agentKey: parsed.targetAgentKey,
285
+ agentUrl: `${parsed.serverUrl}/${parsed.targetAgentKey}`,
286
+ publicKey: parsed.publicAgent ? publicKey : null,
287
+ spaceId: parsed.spaceId,
288
+ model: target.model,
289
+ provider: target.provider,
290
+ customProviderUrl: target.customProviderUrl,
291
+ tools: target.tools,
292
+ };
293
+ if (parsed.json) {
294
+ output.write(`${JSON.stringify(result, null, 2)}\n`);
295
+ } else {
296
+ output.write(
297
+ [
298
+ `created/updated: ${result.agentKey}`,
299
+ `url: ${result.agentUrl}`,
300
+ `space: ${result.spaceId}`,
301
+ `model: ${result.model}`,
302
+ `tools: ${result.tools.join(", ")}`,
303
+ "",
304
+ ].join("\n")
305
+ );
306
+ }
307
+ return 0;
308
+ } catch (error) {
309
+ output.write(
310
+ `setup-offline-marxists failed: ${error instanceof Error ? error.message : String(error)}\n`
311
+ );
312
+ return 1;
313
+ }
314
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {
@@ -11,17 +11,35 @@
11
11
  "index.ts",
12
12
  "agentRuntimeLocal.ts",
13
13
  "agentAliases.ts",
14
+ "agentCommandSupport.ts",
15
+ "agentListCommands.ts",
14
16
  "localRuntimeDb.ts",
15
17
  "agentRuntimeCommands.ts",
18
+ "agentListHelpers.ts",
19
+ "agentMachineCommands.ts",
16
20
  "agentPullCommand.ts",
21
+ "agentRecordCommands.ts",
22
+ "agentRecordHelpers.ts",
17
23
  "agentRunCommand.ts",
18
24
  "authCommands.ts",
25
+ "cliCommandAdapters.ts",
26
+ "cliCommandFactories.ts",
27
+ "cliCommandDispatch.ts",
28
+ "cliCommandTypes.ts",
29
+ "cliEnvHelpers.ts",
19
30
  "commandRegistry.ts",
20
31
  "connectorRunArtifact.ts",
21
32
  "connectorWebSocketTarget.ts",
22
33
  "defaultServer.ts",
34
+ "agentInternalCommandEntries.ts",
35
+ "internalCommandEntries.ts",
23
36
  "machineCommands.ts",
37
+ "offlineMarxistsAgentCommand.ts",
38
+ "runtimeDoctorCommands.ts",
39
+ "scriptCommandEntries.ts",
40
+ "systemInternalCommandEntries.ts",
24
41
  "taskRunCommand.ts",
42
+ "workflowInternalCommandEntries.ts",
25
43
  "runtimeModeArgs.ts",
26
44
  "tableCommands.ts",
27
45
  "updateCommands.ts",
@@ -32,8 +50,8 @@
32
50
  "connector-experimental/**/*.ts",
33
51
  "agent-runtime/**/*.ts",
34
52
  "database/**/*.ts",
35
- "connector-experimental/**/*.ts",
36
53
  "ai/**/*.ts",
54
+ "connector-experimental/**/*.ts",
37
55
  "render/**/*.ts"
38
56
  ],
39
57
  "publishConfig": {
@@ -0,0 +1,118 @@
1
+ import { resolveAgentRuntimeDecision } from "./agent-runtime/runtimeDecision";
2
+ import {
3
+ type AgentCommandDeps,
4
+ type LocalRuntimeProbeResult,
5
+ } from "./agentCommandSupport";
6
+ import {
7
+ resolveAuthToken,
8
+ resolveServerUrl,
9
+ type EnvLike,
10
+ } from "./cliEnvHelpers";
11
+
12
+ function detectLocalAgentConfig(env: EnvLike) {
13
+ return Boolean(readLocalAgentKey(env) || env.NOLO_AGENT_CACHE_READY);
14
+ }
15
+
16
+ function readLocalAgentKey(env: EnvLike) {
17
+ return env.NOLO_LOCAL_AGENT_KEY || env.NOLO_AGENT || "";
18
+ }
19
+
20
+ function detectLocalProvider(env: EnvLike) {
21
+ return Boolean(
22
+ env.OPENAI_API_KEY ||
23
+ env.ANTHROPIC_API_KEY ||
24
+ env.GOOGLE_API_KEY ||
25
+ env.NOLO_LOCAL_OPENAI_BASE_URL ||
26
+ env.OLLAMA_BASE_URL
27
+ );
28
+ }
29
+
30
+ function detectProviderLabel(env: EnvLike) {
31
+ if (env.NOLO_LOCAL_OPENAI_BASE_URL) return `openai-compatible endpoint ${env.NOLO_LOCAL_OPENAI_BASE_URL}`;
32
+ if (env.OLLAMA_BASE_URL) return `ollama-compatible endpoint ${env.OLLAMA_BASE_URL}`;
33
+ if (env.OPENAI_API_KEY) return "openai via env OPENAI_API_KEY";
34
+ if (env.ANTHROPIC_API_KEY) return "anthropic via env ANTHROPIC_API_KEY";
35
+ if (env.GOOGLE_API_KEY) return "google via env GOOGLE_API_KEY";
36
+ return "missing";
37
+ }
38
+
39
+ async function defaultLocalRuntimeProbe(env: EnvLike): Promise<LocalRuntimeProbeResult> {
40
+ const { resolveCliLocalRuntimeDbPath } = await import("./localRuntimeDb");
41
+ const dbPath = resolveCliLocalRuntimeDbPath({ env });
42
+ const agentKey = readLocalAgentKey(env);
43
+ try {
44
+ const { getDefaultCliLocalRuntimeDb } = await import("./localRuntimeDb");
45
+ const db = await getDefaultCliLocalRuntimeDb();
46
+ if (!agentKey) {
47
+ return { ok: true, dbPath, agentFound: false };
48
+ }
49
+ try {
50
+ await db.get(agentKey);
51
+ return { ok: true, dbPath, agentFound: true, agentKey };
52
+ } catch {
53
+ return { ok: true, dbPath, agentFound: false, agentKey };
54
+ }
55
+ } catch (error) {
56
+ return {
57
+ ok: false,
58
+ dbPath,
59
+ agentFound: false,
60
+ ...(agentKey ? { agentKey } : {}),
61
+ error: error instanceof Error ? error.message : String(error),
62
+ };
63
+ }
64
+ }
65
+
66
+ export async function runDoctorRuntimeCommand(
67
+ _args: string[],
68
+ deps: AgentCommandDeps = {}
69
+ ) {
70
+ const env = deps.env ?? process.env;
71
+ const output = deps.output ?? process.stdout;
72
+ const authToken = resolveAuthToken(env);
73
+ const serverUrl = resolveServerUrl(env);
74
+ const localProbe = await (deps.localRuntimeProbe ?? defaultLocalRuntimeProbe)(env);
75
+ const hasLocalAgentConfig = detectLocalAgentConfig(env) && localProbe.ok && localProbe.agentFound;
76
+ const hasLocalProvider = detectLocalProvider(env);
77
+ const missingLocalCapabilities = [
78
+ ...(localProbe.ok ? [] : ["leveldb"]),
79
+ ...(hasLocalAgentConfig ? [] : ["agent-config"]),
80
+ ...(hasLocalProvider ? [] : ["provider"]),
81
+ ];
82
+ const decision = resolveAgentRuntimeDecision({
83
+ requestedMode: "auto",
84
+ syncRequested: false,
85
+ host: "cli",
86
+ hasLocalAgentConfig,
87
+ hasLocalProvider,
88
+ hasLocalPersistence: true,
89
+ missingLocalCapabilities,
90
+ requiresServer: false,
91
+ serverFallbackAvailable: Boolean(authToken || env.NOLO_SERVER || env.BASE_URL),
92
+ });
93
+
94
+ output.write(`Runtime: ${decision.mode}\n`);
95
+ output.write(`Reason: ${decision.reason}\n`);
96
+ output.write(`LevelDB: ${localProbe.ok ? "ok" : "failed"}\n`);
97
+ output.write(`DB path: ${localProbe.dbPath}\n`);
98
+ if (!localProbe.ok && localProbe.error) {
99
+ output.write(`DB error: ${localProbe.error}\n`);
100
+ }
101
+ if (localProbe.agentKey) {
102
+ output.write(`Agent config: ${localProbe.agentFound ? "found" : "missing"} (${localProbe.agentKey})\n`);
103
+ } else {
104
+ output.write("Agent config: missing (no local agent key)\n");
105
+ }
106
+ output.write(`Provider: ${hasLocalProvider ? "available" : "missing"} (${detectProviderLabel(env)})\n`);
107
+ output.write("Persistence: LevelDB local store\n");
108
+ output.write(`Sync: ${authToken ? "available" : "unavailable"}${authToken ? "" : " (not authenticated)"}\n`);
109
+ output.write(`Server fallback: ${decision.runnable && decision.mode === "server" ? serverUrl : authToken || env.NOLO_SERVER || env.BASE_URL ? serverUrl : "unavailable"}\n`);
110
+ if (decision.missingLocalCapabilities.length > 0) {
111
+ output.write("Missing local capabilities:\n");
112
+ for (const capability of decision.missingLocalCapabilities) {
113
+ output.write(`- ${capability}\n`);
114
+ }
115
+ }
116
+
117
+ return decision.runnable ? 0 : 1;
118
+ }
@@ -0,0 +1,71 @@
1
+ import { createScriptCommand, type CommandEntry } from "./cliCommandTypes";
2
+
3
+ function getDocScriptCommands(): CommandEntry[] {
4
+ return [
5
+ createScriptCommand(["doc", "create"], "createDoc.ts", "Create a normal doc"),
6
+ createScriptCommand(["doc", "read"], "readDoc.ts", "Read a normal doc"),
7
+ createScriptCommand(["doc", "update"], "updateDoc.ts", "Update a normal doc"),
8
+ createScriptCommand(["doc", "delete"], "deleteDoc.ts", "Delete a normal doc"),
9
+ createScriptCommand(["skill-doc", "create"], "createSkillDoc.ts", "Create a skill doc"),
10
+ createScriptCommand(["skill-doc", "read"], "readSkillDoc.ts", "Read a skill doc"),
11
+ createScriptCommand(["skill-doc", "update"], "updateSkillDoc.ts", "Update a skill doc"),
12
+ createScriptCommand(["skill-doc", "delete"], "deleteSkillDoc.ts", "Delete a skill doc"),
13
+ createScriptCommand(["skill", "create-doc"], "createSkillDoc.ts", "Create a skill-backed doc"),
14
+ ];
15
+ }
16
+
17
+ function getSpaceScriptCommands(): CommandEntry[] {
18
+ return [
19
+ createScriptCommand(["dialog", "read"], "readDialog.ts", "Read a dialog"),
20
+ createScriptCommand(["dialog", "status"], "dialogStatus.ts", "Read compact dialog status"),
21
+ createScriptCommand(["space", "list"], "listSpaces.ts", "List joined spaces"),
22
+ createScriptCommand(["space", "read"], "readSpace.ts", "Read a space"),
23
+ createScriptCommand(["space", "delete"], "deleteSpaces.ts", "Delete spaces by safe filters"),
24
+ createScriptCommand(["space", "category"], "upsertSpaceCategory.ts", "Create or update a space category"),
25
+ createScriptCommand(["space", "content-category"], "setSpaceContentCategory.ts", "Move content into a space category"),
26
+ ];
27
+ }
28
+
29
+ function getTableScriptCommands(): CommandEntry[] {
30
+ return [
31
+ createScriptCommand(["table", "add-column"], "tableData.ts", "Add one table column", ["--action", "add-column"]),
32
+ createScriptCommand(["table", "add-row"], "tableData.ts", "Add one table row", ["--action", "add-row"]),
33
+ createScriptCommand(["table", "add-rows"], "tableData.ts", "Add table rows", ["--action", "add-rows"]),
34
+ createScriptCommand(["table", "update-row"], "tableData.ts", "Update one table row", ["--action", "update-row"]),
35
+ createScriptCommand(["table", "update-rows"], "tableData.ts", "Update table rows", ["--action", "update-rows"]),
36
+ createScriptCommand(["table", "delete-row"], "tableData.ts", "Delete one table row", ["--action", "delete-row"]),
37
+ createScriptCommand(["table", "delete-rows"], "tableData.ts", "Delete table rows", ["--action", "delete-rows"]),
38
+ createScriptCommand(["table", "data"], "tableData.ts", "Low-level table row script bridge"),
39
+ createScriptCommand(["table", "meta"], "upsertTableMeta.ts", "Create or update table metadata"),
40
+ ];
41
+ }
42
+
43
+ function getAgentScriptCommands(): CommandEntry[] {
44
+ return [
45
+ createScriptCommand(["agent", "unpublish"], "unpublishAgent.ts", "Remove an agent's public record"),
46
+ createScriptCommand(["agent", "dialogs"], "queryAgentDialogs.ts", "Inspect recent dialogs for an agent"),
47
+ createScriptCommand(["agent", "doctor"], "doctorAgentWorkspace.ts", "Audit agent workspace health"),
48
+ createScriptCommand(["agent", "create-custom"], "createCustomCodingAgent.ts", "Create a custom coding agent"),
49
+ createScriptCommand(["agent", "create-space"], "createSpaceAgents.ts", "Create or attach shared-space agents"),
50
+ createScriptCommand(["agent", "setup-demo"], "setupDemoAgent.ts", "Bootstrap demo publisher agents"),
51
+ createScriptCommand(["agent", "supervise"], "runAutonomousAgent.ts", "Run an agent in autonomous cycles"),
52
+ ];
53
+ }
54
+
55
+ function getRuntimeScriptCommands(): CommandEntry[] {
56
+ return [
57
+ createScriptCommand(["llama"], "llamaServerSupervisor.ts", "Manage the llama.cpp runtime"),
58
+ createScriptCommand(["model-runtime"], "localModelRuntimeSupervisor.ts", "Manage local model runtime processes"),
59
+ createScriptCommand(["dev"], "devControl.ts", "Manage the local dev environment"),
60
+ ];
61
+ }
62
+
63
+ export function getScriptCommandEntries(): CommandEntry[] {
64
+ return [
65
+ ...getDocScriptCommands(),
66
+ ...getSpaceScriptCommands(),
67
+ ...getTableScriptCommands(),
68
+ ...getAgentScriptCommands(),
69
+ ...getRuntimeScriptCommands(),
70
+ ];
71
+ }
@@ -0,0 +1,71 @@
1
+ import { runLoginCommand, runLogoutCommand, runWhoamiCommand } from "./authCommands";
2
+ import {
3
+ createEntrypointEnvCommand,
4
+ createEnvCommand,
5
+ } from "./cliCommandFactories";
6
+ import { createInternalCommand, type CommandEntry } from "./cliCommandTypes";
7
+ import { withDaemonShortcutArgs } from "./cliCommandDispatch";
8
+ import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
9
+ import {
10
+ runMachineConnectCommand,
11
+ runMachineStatusCommand,
12
+ } from "./machineCommands";
13
+ import { runDoctorRuntimeCommand } from "./runtimeDoctorCommands";
14
+ import {
15
+ buildCliDoctorText,
16
+ buildCliVersionText,
17
+ runSelfUpdate,
18
+ } from "./updateCommands";
19
+
20
+ export function getSystemInternalCommandEntries(renderHelpText: () => string): CommandEntry[] {
21
+ return [
22
+ createEnvCommand(["doctor", "runtime"], "Diagnose local-first agent runtime selection", runDoctorRuntimeCommand),
23
+ createInternalCommand(["login"], "Log in to Nolo", (args) => Promise.resolve(runLoginCommand(args))),
24
+ createInternalCommand(["logout"], "Log out of Nolo", async () => runLogoutCommand()),
25
+ createInternalCommand(["whoami"], "Show the current login state", async () => runWhoamiCommand()),
26
+ createEntrypointEnvCommand(["connect"], "Send machine heartbeats or hold a connector websocket", runMachineConnectCommand),
27
+ createInternalCommand(["daemon"], "Run the connector daemon shortcut", (args, ctx) => {
28
+ const normalized = withDaemonShortcutArgs(args, ctx.env);
29
+ return runMachineConnectCommand(["--ws"], {
30
+ env: normalized.envOverride ?? ctx.env,
31
+ cliEntrypointPath: ctx.entrypointPath,
32
+ });
33
+ }),
34
+ createInternalCommand(["machine", "status"], "List machines registered to the current profile", (args, ctx) =>
35
+ runMachineStatusCommand(args, { env: ctx.env })
36
+ ),
37
+ createInternalCommand(["doctor"], "Show CLI doctor information", async (_args, ctx) => {
38
+ console.log(
39
+ buildCliDoctorText({
40
+ packageName: ctx.packageInfo.name,
41
+ version: ctx.packageInfo.version,
42
+ entrypoint: ctx.entrypointPath,
43
+ serverUrl: ctx.env.NOLO_SERVER || ctx.env.BASE_URL || DEFAULT_NOLO_SERVER_URL,
44
+ profileName: ctx.env.NOLO_PROFILE || "local",
45
+ })
46
+ );
47
+ return 0;
48
+ }),
49
+ createInternalCommand(["update"], "Update the CLI", async () => runSelfUpdate()),
50
+ createInternalCommand(["version"], "Show CLI version", async (_args, ctx) => {
51
+ console.log(buildCliVersionText(ctx.packageInfo));
52
+ return 0;
53
+ }),
54
+ createInternalCommand(["--version"], "Show CLI version", async (_args, ctx) => {
55
+ console.log(buildCliVersionText(ctx.packageInfo));
56
+ return 0;
57
+ }),
58
+ createInternalCommand(["-v"], "Show CLI version", async (_args, ctx) => {
59
+ console.log(buildCliVersionText(ctx.packageInfo));
60
+ return 0;
61
+ }),
62
+ createInternalCommand(["--help"], "Show help", async () => {
63
+ console.log(renderHelpText());
64
+ return 0;
65
+ }),
66
+ createInternalCommand(["-h"], "Show help", async () => {
67
+ console.log(renderHelpText());
68
+ return 0;
69
+ }),
70
+ ];
71
+ }
@@ -0,0 +1,11 @@
1
+ import { createEnvCommand } from "./cliCommandFactories";
2
+ import type { CommandEntry } from "./cliCommandTypes";
3
+ import { runTableQueryCommand } from "./tableCommands";
4
+ import { runTaskRunCommand } from "./taskRunCommand";
5
+
6
+ export function getWorkflowInternalCommandEntries(): CommandEntry[] {
7
+ return [
8
+ createEnvCommand(["table", "query"], "Query table rows", runTableQueryCommand),
9
+ createEnvCommand(["task-run"], "Update durable task-run state through the runner bridge", runTaskRunCommand),
10
+ ];
11
+ }