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
@@ -34,6 +34,7 @@ import {
34
34
  type HybridRecordStore,
35
35
  } from "./hybridRecordStore";
36
36
  import { executeLocalToolWithPolicy } from "./localToolPolicy";
37
+ import { taskRunFunctionSchema } from "../ai/tools/agent/taskRunTool";
37
38
  import { prepareTaskWorktree } from "./taskWorktree";
38
39
  import {
39
40
  activateWorkspaceSession,
@@ -144,10 +145,13 @@ async function fetchWithTransientRetry(
144
145
 
145
146
  function buildOpenAiTools(args: { toolNames?: string[]; env: EnvLike }) {
146
147
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
147
- return buildLocalWorkspaceOpenAiTools({
148
- toolNames: toolset.toolNames,
149
- exposeShellTools: toolset.exposeShellTools,
150
- });
148
+ return [
149
+ ...buildLocalWorkspaceOpenAiTools({
150
+ toolNames: toolset.toolNames,
151
+ exposeShellTools: toolset.exposeShellTools,
152
+ }),
153
+ ...buildServerPlatformOpenAiTools({ toolNames: args.toolNames }),
154
+ ];
151
155
  }
152
156
 
153
157
  function buildLocalWorkspaceToolsetForEnv(args: { toolNames?: string[]; env: EnvLike }) {
@@ -159,18 +163,86 @@ function buildLocalWorkspaceToolsetForEnv(args: { toolNames?: string[]; env: Env
159
163
  }
160
164
 
161
165
  function buildLocalPolicyToolNames(args: { toolNames?: string[]; env: EnvLike }) {
162
- return buildLocalWorkspacePolicyToolNames({
163
- declaredToolNames: args.toolNames,
164
- exposeShellTools: shellModeEnabled(args.env),
165
- });
166
+ return [
167
+ ...buildLocalWorkspacePolicyToolNames({
168
+ declaredToolNames: args.toolNames,
169
+ exposeShellTools: shellModeEnabled(args.env),
170
+ }),
171
+ ...((args.toolNames ?? []).includes("taskRun") ? ["taskRun"] : []),
172
+ ];
173
+ }
174
+
175
+ function buildServerPlatformOpenAiTools(args: { toolNames?: string[] }) {
176
+ const toolNames = new Set(args.toolNames ?? []);
177
+ return [
178
+ ...(toolNames.has("taskRun")
179
+ ? [{
180
+ type: "function",
181
+ function: taskRunFunctionSchema,
182
+ }]
183
+ : []),
184
+ ];
185
+ }
186
+
187
+ function parseToolArguments(raw: string) {
188
+ try {
189
+ const parsed = JSON.parse(raw || "{}");
190
+ return parsed && typeof parsed === "object" ? parsed : {};
191
+ } catch {
192
+ return {};
193
+ }
194
+ }
195
+
196
+ function resolveRuntimeServerUrl(env: EnvLike) {
197
+ return (env.NOLO_SERVER_URL || env.NOLO_SERVER || env.BASE_URL || "").replace(/\/+$/, "");
198
+ }
199
+
200
+ function resolveRuntimeAuthToken(env: EnvLike) {
201
+ return env.AUTH_TOKEN || env.AUTH || env.NOLO_MACHINE_API_KEY || "";
202
+ }
203
+
204
+ function buildServerPlatformToolExecutors(args: {
205
+ env: EnvLike;
206
+ fetchImpl: typeof fetch;
207
+ }) {
208
+ return {
209
+ taskRun: async (call: any) => {
210
+ const serverUrl = resolveRuntimeServerUrl(args.env);
211
+ const authToken = resolveRuntimeAuthToken(args.env);
212
+ if (!serverUrl) throw new Error("taskRun requires NOLO_SERVER_URL, NOLO_SERVER, or BASE_URL.");
213
+ if (!authToken) throw new Error("taskRun requires AUTH_TOKEN or NOLO_MACHINE_API_KEY.");
214
+ const response = await args.fetchImpl(`${serverUrl}/api/task-run`, {
215
+ method: "POST",
216
+ headers: {
217
+ "Content-Type": "application/json",
218
+ Authorization: `Bearer ${authToken}`,
219
+ },
220
+ body: JSON.stringify(parseToolArguments(call.arguments)),
221
+ });
222
+ const text = await response.text().catch(() => "");
223
+ if (!response.ok) {
224
+ throw new Error(`taskRun bridge failed: HTTP ${response.status} ${text.slice(0, 500)}`);
225
+ }
226
+ return {
227
+ content: text,
228
+ metadata: { serverPlatformTool: true },
229
+ };
230
+ },
231
+ };
166
232
  }
167
233
 
168
234
  function buildLocalToolExecutors(args: {
169
235
  workspaceRoot: string;
236
+ env: EnvLike;
237
+ fetchImpl: typeof fetch;
170
238
  localToolExecutors?: CliLocalRuntimeAdapterDeps["localToolExecutors"];
171
239
  }) {
172
240
  return {
173
241
  ...createLocalWorkspaceToolExecutors({ workspaceRoot: args.workspaceRoot }),
242
+ ...buildServerPlatformToolExecutors({
243
+ env: args.env,
244
+ fetchImpl: args.fetchImpl,
245
+ }),
174
246
  ...(args.localToolExecutors ?? {}),
175
247
  };
176
248
  }
@@ -262,6 +334,8 @@ export function createCliLocalRuntimeAdapter(
262
334
  );
263
335
  let localToolExecutors = buildLocalToolExecutors({
264
336
  workspaceRoot: workspaceSession.workspaceRoot,
337
+ env: deps.env,
338
+ fetchImpl,
265
339
  localToolExecutors: deps.localToolExecutors,
266
340
  });
267
341
 
@@ -279,6 +353,8 @@ export function createCliLocalRuntimeAdapter(
279
353
  if (workspaceSession.workspaceRoot === previousWorkspaceRoot) return;
280
354
  localToolExecutors = buildLocalToolExecutors({
281
355
  workspaceRoot: workspaceSession.workspaceRoot,
356
+ env: deps.env,
357
+ fetchImpl,
282
358
  localToolExecutors: deps.localToolExecutors,
283
359
  });
284
360
  deps.output?.write(formatWorkspaceSessionActivation(workspaceSession));
@@ -4,7 +4,6 @@ import { join } from "node:path";
4
4
  import { tmpdir } from "node:os";
5
5
 
6
6
  import { runLocalAgentTurn } from "../agent-runtime/localLoop";
7
- import { buildLocalWorkspaceToolset } from "../agent-runtime/localWorkspaceTools";
8
7
  import { createCliLocalRuntimeAdapter } from "./localRuntimeAdapter";
9
8
 
10
9
  describe("CLI local runtime dry run", () => {
@@ -54,7 +53,11 @@ describe("CLI local runtime dry run", () => {
54
53
  completeCount += 1;
55
54
  if (completeCount === 1) {
56
55
  expect(body.tools.map((tool: any) => tool.function.name)).toEqual([
57
- ...buildLocalWorkspaceToolset({}).toolNames,
56
+ "writeWorkspaceFile",
57
+ "listFiles",
58
+ "readFile",
59
+ "editFile",
60
+ "searchFiles",
58
61
  ]);
59
62
  return Response.json({
60
63
  model: "qwen-coder",
@@ -16,6 +16,7 @@ export function prependTaskRunPrompt(message: string, context?: TaskRunPromptCon
16
16
  ...(context.taskRunId ? [`- taskRunId: ${context.taskRunId}`] : []),
17
17
  ...(context.workItemId ? [`- workItemId: ${context.workItemId}`] : []),
18
18
  ...(artifactIds.length ? [`- artifactIds: ${artifactIds.join(",")}`] : []),
19
+ "- Handoff rule: start from the durable task 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.",
19
20
  "- When you create, update, hand off, block, or review work, update the task context with `bun run task:run -- ...`.",
20
21
  "- If this turn already includes a server TaskRun preflight snapshot, use that snapshot directly for read-only reporting or planning.",
21
22
  "- Keep long reasoning and logs in the dialog; record only resumable references in task row meta.",
@@ -1,66 +1,16 @@
1
- export type CommandEntry = {
2
- path: string[];
3
- script: string;
4
- description: string;
5
- fixedArgs?: string[];
6
- };
1
+ export type { CliPackageInfo, CliRuntimeContext, CommandEntry } from "./cliCommandTypes";
2
+ export {
3
+ createCliRuntimeContext,
4
+ looksLikeDaemonShortcut,
5
+ runResolvedCommand,
6
+ } from "./cliCommandDispatch";
7
+ import type { CommandEntry } from "./cliCommandTypes";
8
+ import { getInternalCommandEntries } from "./internalCommandEntries";
9
+ import { getScriptCommandEntries } from "./scriptCommandEntries";
7
10
 
8
11
  export const COMMANDS: CommandEntry[] = [
9
- { path: ["chat"], script: "", description: "Chat with an agent" },
10
-
11
- { path: ["doc", "create"], script: "createDoc.ts", description: "Create a normal doc" },
12
- { path: ["doc", "read"], script: "readDoc.ts", description: "Read a normal doc" },
13
- { path: ["doc", "update"], script: "updateDoc.ts", description: "Update a normal doc" },
14
- { path: ["doc", "delete"], script: "deleteDoc.ts", description: "Delete a normal doc" },
15
-
16
- { path: ["skill-doc", "create"], script: "createSkillDoc.ts", description: "Create a skill doc" },
17
- { path: ["skill-doc", "read"], script: "readSkillDoc.ts", description: "Read a skill doc" },
18
- { path: ["skill-doc", "update"], script: "updateSkillDoc.ts", description: "Update a skill doc" },
19
- { path: ["skill-doc", "delete"], script: "deleteSkillDoc.ts", description: "Delete a skill doc" },
20
- { path: ["skill", "create-doc"], script: "createSkillDoc.ts", description: "Create a skill-backed doc" },
21
-
22
- { path: ["dialog", "read"], script: "readDialog.ts", description: "Read a dialog" },
23
- { path: ["dialog", "status"], script: "dialogStatus.ts", description: "Read compact dialog status" },
24
- { path: ["space", "list"], script: "listSpaces.ts", description: "List joined spaces" },
25
- { path: ["space", "read"], script: "readSpace.ts", description: "Read a space" },
26
- { path: ["space", "delete"], script: "deleteSpaces.ts", description: "Delete spaces by safe filters" },
27
- { path: ["space", "category"], script: "upsertSpaceCategory.ts", description: "Create or update a space category" },
28
- { path: ["space", "content-category"], script: "setSpaceContentCategory.ts", description: "Move content into a space category" },
29
-
30
- { path: ["table", "query"], script: "tableData.ts", fixedArgs: ["--action", "query"], description: "Query table rows" },
31
- { path: ["table", "add-column"], script: "tableData.ts", fixedArgs: ["--action", "add-column"], description: "Add one table column" },
32
- { path: ["table", "add-row"], script: "tableData.ts", fixedArgs: ["--action", "add-row"], description: "Add one table row" },
33
- { path: ["table", "add-rows"], script: "tableData.ts", fixedArgs: ["--action", "add-rows"], description: "Add table rows" },
34
- { path: ["table", "update-row"], script: "tableData.ts", fixedArgs: ["--action", "update-row"], description: "Update one table row" },
35
- { path: ["table", "update-rows"], script: "tableData.ts", fixedArgs: ["--action", "update-rows"], description: "Update table rows" },
36
- { path: ["table", "delete-row"], script: "tableData.ts", fixedArgs: ["--action", "delete-row"], description: "Delete one table row" },
37
- { path: ["table", "delete-rows"], script: "tableData.ts", fixedArgs: ["--action", "delete-rows"], description: "Delete table rows" },
38
- { path: ["table", "data"], script: "tableData.ts", description: "Low-level table row script bridge" },
39
- { path: ["table", "meta"], script: "upsertTableMeta.ts", description: "Create or update table metadata" },
40
- { path: ["task-run"], script: "", description: "Update durable task-run state through the runner bridge" },
41
-
42
- { path: ["agent", "list"], script: "listMyAgents.ts", description: "List owned agents" },
43
- { path: ["agent", "pull"], script: "", description: "Cache an agent for local runs" },
44
- { path: ["agent", "read"], script: "", description: "Read a single agent" },
45
- { path: ["agent", "run"], script: "", description: "Run an agent" },
46
- { path: ["agent", "update"], script: "updateAgent.ts", description: "Update agent fields" },
47
- { path: ["agent", "bind-current"], script: "", description: "Bind an agent to this machine" },
48
- { path: ["agent", "smoke-current"], script: "", description: "Smoke test a bound agent through this machine" },
49
- { path: ["agent", "runtime-doctor"], script: "", description: "Diagnose current machine runtime compatibility" },
50
- { path: ["agent", "unpublish"], script: "unpublishAgent.ts", description: "Remove an agent's public record" },
51
- { path: ["agent", "chat"], script: "", description: "Chat with an agent" },
52
- { path: ["agent", "dialogs"], script: "queryAgentDialogs.ts", description: "Inspect recent dialogs for an agent" },
53
- { path: ["agent", "doctor"], script: "doctorAgentWorkspace.ts", description: "Audit agent workspace health" },
54
- { path: ["agent", "create-custom"], script: "createCustomCodingAgent.ts", description: "Create a custom coding agent" },
55
- { path: ["agent", "create-space"], script: "createSpaceAgents.ts", description: "Create or attach shared-space agents" },
56
- { path: ["agent", "setup-demo"], script: "setupDemoAgent.ts", description: "Bootstrap demo publisher agents" },
57
- { path: ["agent", "supervise"], script: "runAutonomousAgent.ts", description: "Run an agent in autonomous cycles" },
58
-
59
- { path: ["doctor", "runtime"], script: "", description: "Diagnose local-first agent runtime selection" },
60
-
61
- { path: ["llama"], script: "llamaServerSupervisor.ts", description: "Manage the llama.cpp runtime" },
62
- { path: ["model-runtime"], script: "localModelRuntimeSupervisor.ts", description: "Manage local model runtime processes" },
63
- { path: ["dev"], script: "devControl.ts", description: "Manage the local dev environment" },
12
+ ...getInternalCommandEntries(renderHelpText),
13
+ ...getScriptCommandEntries(),
64
14
  ];
65
15
 
66
16
  export const GROUP_ORDER = [
@@ -29,6 +29,7 @@ const COMMAND_CAPABILITIES: Array<[probes: CommandProbe[], capability: string]>
29
29
  ],
30
30
  [[{ command: "gemini", args: ["--version"] }], "gemini-cli"],
31
31
  [[{ command: "kimi", args: ["--version"] }], "kimi-cli"],
32
+ [[{ command: "agy", args: ["--help"] }], "agy-cli"],
32
33
  ];
33
34
 
34
35
  function defaultCommandExists(command: string) {
@@ -9,7 +9,7 @@ const normalizeOrigin = (value: string | URL | null | undefined): string => {
9
9
  }
10
10
  };
11
11
 
12
- const getDbKeyForOperation = (args: {
12
+ const getDbKeyForOperation = (args: {
13
13
  operation: string;
14
14
  pathname: string;
15
15
  req: { body?: Record<string, any> };
@@ -26,10 +26,32 @@ const getDbKeyForOperation = (args: {
26
26
  return typeof req.body?.customKey === "string" ? req.body.customKey : null;
27
27
  default:
28
28
  return null;
29
- }
30
- };
31
-
32
- export async function maybeProxyDatabaseRequestToCore(args: {
29
+ }
30
+ };
31
+
32
+ export function resolveCoreDataProxyTarget(args: {
33
+ dbKey: string;
34
+ requestUrl?: string | URL | null;
35
+ coreBaseUrl?: string | null;
36
+ }): string | null {
37
+ const coreBaseUrl = normalizeOrigin(args.coreBaseUrl);
38
+ if (!coreBaseUrl) {
39
+ return null;
40
+ }
41
+
42
+ const requestOrigin = normalizeOrigin(args.requestUrl);
43
+ if (requestOrigin && requestOrigin === coreBaseUrl) {
44
+ return null;
45
+ }
46
+
47
+ if (classifyCoreDataKey(args.dbKey).owner !== "core") {
48
+ return null;
49
+ }
50
+
51
+ return coreBaseUrl;
52
+ }
53
+
54
+ export async function maybeProxyDatabaseRequestToCore(args: {
33
55
  req: {
34
56
  method?: string;
35
57
  url?: string | URL;
@@ -40,27 +62,22 @@ export async function maybeProxyDatabaseRequestToCore(args: {
40
62
  search: string;
41
63
  coreBaseUrl?: string | null;
42
64
  fetchImpl?: typeof fetch;
43
- }): Promise<Response | null> {
44
- const { req, pathname, search, fetchImpl = fetch } = args;
45
- const coreBaseUrl = normalizeOrigin(args.coreBaseUrl);
46
- if (!coreBaseUrl) {
47
- return null;
48
- }
49
-
50
- const requestOrigin = normalizeOrigin(req.url);
51
- if (requestOrigin && requestOrigin === coreBaseUrl) {
52
- return null;
53
- }
54
-
55
- const operation = pathname.slice("/api/v1/db/".length).split("/")[0] ?? "";
56
- const dbKey = getDbKeyForOperation({ operation, pathname, req });
57
- if (!dbKey) {
58
- return null;
59
- }
60
-
61
- if (classifyCoreDataKey(dbKey).owner !== "core") {
62
- return null;
63
- }
65
+ }): Promise<Response | null> {
66
+ const { req, pathname, search, fetchImpl = fetch } = args;
67
+ const operation = pathname.slice("/api/v1/db/".length).split("/")[0] ?? "";
68
+ const dbKey = getDbKeyForOperation({ operation, pathname, req });
69
+ if (!dbKey) {
70
+ return null;
71
+ }
72
+
73
+ const coreBaseUrl = resolveCoreDataProxyTarget({
74
+ dbKey,
75
+ requestUrl: req.url,
76
+ coreBaseUrl: args.coreBaseUrl,
77
+ });
78
+ if (!coreBaseUrl) {
79
+ return null;
80
+ }
64
81
 
65
82
  const headers = new Headers(req.headers ?? {});
66
83
  const init: RequestInit = {
package/index.ts CHANGED
@@ -1,54 +1,37 @@
1
1
  #!/usr/bin/env bun
2
-
3
- import { dirname, join } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- import { renderHelpText, resolveCommand } from "./commandRegistry";
7
- import { runLoginCommand, runLogoutCommand, runWhoamiCommand } from "./authCommands";
2
+
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
8
6
  import {
9
- runAgentBindCurrentCommand,
10
- runAgentReadCommand,
11
- runAgentRuntimeDoctorCommand,
12
- runAgentSmokeCurrentCommand,
13
- runDoctorRuntimeCommand,
14
- } from "./agentRuntimeCommands";
15
- import { runAgentRunCommand } from "./agentRunCommand";
16
- import { runAgentPullCommand } from "./agentPullCommand";
7
+ createCliRuntimeContext,
8
+ looksLikeDaemonShortcut,
9
+ renderHelpText,
10
+ resolveCommand,
11
+ runResolvedCommand,
12
+ } from "./commandRegistry";
17
13
  import { buildEnvFromProfile, loadProfileConfig } from "./client/profileConfig";
18
14
  import { resolveTuiLaunchMode } from "./runtimeModeArgs";
19
- import {
20
- runMachineConnectCommand,
21
- runMachineStatusCommand,
22
- } from "./machineCommands";
23
- import { runTaskRunCommand } from "./taskRunCommand";
24
- import { runTableQueryCommand } from "./tableCommands";
25
- import { startTuiWorkspace } from "./tui/readlineWorkspace";
26
- import {
27
- buildCliDoctorText,
28
- buildCliVersionText,
29
- readPackageInfo,
30
- runSelfUpdate,
31
- } from "./updateCommands";
32
- import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
33
-
34
- const CLI_DIR = dirname(fileURLToPath(import.meta.url));
35
- const ROOT_DIR = join(CLI_DIR, "..", "..");
36
- const SCRIPT_DIR = join(ROOT_DIR, "scripts");
37
- const packageInfo = readPackageInfo();
38
-
39
- async function runScript(script: string, forwardedArgs: string[], env: NodeJS.ProcessEnv) {
40
- const scriptPath = join(SCRIPT_DIR, script);
41
- const proc = Bun.spawn({
42
- cmd: [process.execPath, scriptPath, ...forwardedArgs],
43
- stdin: "inherit",
44
- stdout: "inherit",
45
- stderr: "inherit",
46
- env,
47
- });
48
- const exitCode = await proc.exited;
49
- process.exit(exitCode);
50
- }
51
-
15
+ import { startTuiWorkspace } from "./tui/readlineWorkspace";
16
+ import { readPackageInfo } from "./updateCommands";
17
+
18
+ const CLI_DIR = dirname(fileURLToPath(import.meta.url));
19
+ const ROOT_DIR = join(CLI_DIR, "..", "..");
20
+ const SCRIPT_DIR = join(ROOT_DIR, "scripts");
21
+ const packageInfo = readPackageInfo();
22
+
23
+ async function runScript(script: string, forwardedArgs: string[], env: NodeJS.ProcessEnv) {
24
+ const scriptPath = join(SCRIPT_DIR, script);
25
+ const proc = Bun.spawn({
26
+ cmd: [process.execPath, scriptPath, ...forwardedArgs],
27
+ stdin: "inherit",
28
+ stdout: "inherit",
29
+ stderr: "inherit",
30
+ env,
31
+ });
32
+ return proc.exited;
33
+ }
34
+
52
35
  const args = process.argv.slice(2);
53
36
  const profileEnv = buildEnvFromProfile(loadProfileConfig());
54
37
  const explicitServerUrl = process.env.NOLO_SERVER || process.env.NOLO_SERVER_URL || process.env.BASE_URL;
@@ -58,35 +41,23 @@ const runtimeEnv = {
58
41
  ...(explicitServerUrl ? { NOLO_SERVER: explicitServerUrl, BASE_URL: explicitServerUrl } : {}),
59
42
  NOLO_CLI_VERSION: packageInfo.version,
60
43
  };
61
-
62
- function readOption(args: string[], flag: string) {
63
- const index = args.indexOf(flag);
64
- return index >= 0 ? args[index + 1] : undefined;
65
- }
66
-
67
- function buildDaemonEnv(args: string[]) {
68
- const serverUrl = readOption(args, "--server-url") || readOption(args, "--server");
69
- const apiKey = readOption(args, "--api-key") || readOption(args, "--token");
70
- return {
71
- ...runtimeEnv,
72
- ...(serverUrl ? { NOLO_SERVER: serverUrl } : {}),
73
- ...(apiKey ? { AUTH_TOKEN: apiKey } : {}),
74
- };
75
- }
76
-
77
- function looksLikeDaemonShortcut(args: string[]) {
78
- return args.includes("--server-url") || args.includes("--api-key");
79
- }
80
-
81
- if (args.length === 0) {
82
- if (process.stdin.isTTY) {
83
- await startTuiWorkspace({ scriptDir: SCRIPT_DIR, env: runtimeEnv });
84
- } else {
85
- console.log(renderHelpText());
86
- }
87
- process.exit(0);
88
- }
89
-
44
+
45
+ const runtimeContext = createCliRuntimeContext({
46
+ env: runtimeEnv,
47
+ scriptDir: SCRIPT_DIR,
48
+ entrypointPath: fileURLToPath(import.meta.url),
49
+ packageInfo,
50
+ });
51
+
52
+ if (args.length === 0) {
53
+ if (process.stdin.isTTY) {
54
+ await startTuiWorkspace({ scriptDir: SCRIPT_DIR, env: runtimeEnv });
55
+ } else {
56
+ console.log(renderHelpText());
57
+ }
58
+ process.exit(0);
59
+ }
60
+
90
61
  const tuiLaunchMode = resolveTuiLaunchMode(args);
91
62
  if (tuiLaunchMode.shouldStartTui) {
92
63
  await startTuiWorkspace({
@@ -95,125 +66,17 @@ if (tuiLaunchMode.shouldStartTui) {
95
66
  });
96
67
  process.exit(0);
97
68
  }
98
-
99
- if (args[0] === "login") {
100
- process.exit(await runLoginCommand(args.slice(1)));
101
- }
102
-
103
- if (args[0] === "whoami") {
104
- process.exit(runWhoamiCommand());
105
- }
106
-
107
- if (args[0] === "logout") {
108
- process.exit(runLogoutCommand());
109
- }
110
-
111
- if (args[0] === "connect") {
112
- process.exit(await runMachineConnectCommand(args.slice(1), {
113
- env: runtimeEnv,
114
- cliEntrypointPath: fileURLToPath(import.meta.url),
115
- }));
116
- }
117
-
118
- if (args[0] === "task-run") {
119
- process.exit(await runTaskRunCommand(args.slice(1), { env: runtimeEnv }));
120
- }
121
-
122
- if (args[0] === "table" && args[1] === "query") {
123
- process.exit(await runTableQueryCommand(args.slice(2), { env: runtimeEnv }));
124
- }
125
-
126
- if (args[0] === "daemon" || looksLikeDaemonShortcut(args)) {
127
- const daemonArgs = args[0] === "daemon" ? args.slice(1) : args;
128
- process.exit(await runMachineConnectCommand(["--ws"], {
129
- env: buildDaemonEnv(daemonArgs),
130
- cliEntrypointPath: fileURLToPath(import.meta.url),
131
- }));
132
- }
133
-
134
- if (args[0] === "machine" && args[1] === "status") {
135
- process.exit(await runMachineStatusCommand(args.slice(2), { env: runtimeEnv }));
136
- }
137
-
138
- if (args[0] === "agent" && args[1] === "bind-current") {
139
- process.exit(await runAgentBindCurrentCommand(args.slice(2), { env: runtimeEnv }));
140
- }
141
-
142
- if (args[0] === "agent" && args[1] === "smoke-current") {
143
- process.exit(await runAgentSmokeCurrentCommand(args.slice(2), { env: runtimeEnv }));
144
- }
145
-
146
- if (args[0] === "agent" && args[1] === "run") {
147
- process.exit(await runAgentRunCommand(args.slice(2), {
148
- env: runtimeEnv,
149
- scriptDir: SCRIPT_DIR,
150
- }));
151
- }
152
-
153
- if (args[0] === "agent" && args[1] === "chat") {
154
- process.exit(await runAgentRunCommand(args.slice(2), {
155
- env: runtimeEnv,
156
- scriptDir: SCRIPT_DIR,
157
- }));
158
- }
159
-
160
- if (args[0] === "chat") {
161
- process.exit(await runAgentRunCommand(args.slice(1), {
162
- env: runtimeEnv,
163
- scriptDir: SCRIPT_DIR,
164
- }));
165
- }
166
-
167
- if (args[0] === "agent" && args[1] === "pull") {
168
- process.exit(await runAgentPullCommand(args.slice(2), { env: runtimeEnv }));
169
- }
170
-
171
- if (args[0] === "agent" && args[1] === "read") {
172
- process.exit(await runAgentReadCommand(args.slice(2), { env: runtimeEnv }));
173
- }
174
-
175
- if (args[0] === "agent" && args[1] === "runtime-doctor") {
176
- process.exit(await runAgentRuntimeDoctorCommand(args.slice(2), { env: runtimeEnv }));
177
- }
178
-
179
- if (args[0] === "doctor" && args[1] === "runtime") {
180
- process.exit(await runDoctorRuntimeCommand(args.slice(2), { env: runtimeEnv }));
181
- }
182
69
 
183
- if (args[0] === "--version" || args[0] === "-v" || args[0] === "version") {
184
- console.log(buildCliVersionText(packageInfo));
185
- process.exit(0);
186
- }
187
-
188
- if (args[0] === "doctor") {
189
- console.log(
190
- buildCliDoctorText({
191
- packageName: packageInfo.name,
192
- version: packageInfo.version,
193
- entrypoint: fileURLToPath(import.meta.url),
194
- serverUrl: runtimeEnv.NOLO_SERVER || runtimeEnv.BASE_URL || DEFAULT_NOLO_SERVER_URL,
195
- profileName: runtimeEnv.NOLO_PROFILE || "local",
196
- })
197
- );
198
- process.exit(0);
199
- }
200
-
201
- if (args[0] === "update") {
202
- process.exit(await runSelfUpdate());
203
- }
204
-
205
- if (args[0] === "--help" || args[0] === "-h") {
206
- console.log(renderHelpText());
207
- process.exit(0);
208
- }
209
-
210
- const command = resolveCommand(args);
70
+ const commandArgs = looksLikeDaemonShortcut(args) ? ["daemon", ...args] : args;
71
+ const command = resolveCommand(commandArgs);
211
72
  if (!command) {
212
73
  console.error(`Unknown command: ${args.join(" ")}`);
213
74
  console.error("");
214
75
  console.log(renderHelpText());
215
- process.exit(1);
76
+ process.exit(1);
216
77
  }
217
78
 
218
- const forwardedArgs = [...(command.fixedArgs ?? []), ...args.slice(command.path.length)];
219
- await runScript(command.script, forwardedArgs, runtimeEnv);
79
+ const exitCode = await runResolvedCommand(command, commandArgs, runtimeContext, {
80
+ runScript,
81
+ });
82
+ process.exit(exitCode);
@@ -0,0 +1,12 @@
1
+ import { getAgentInternalCommandEntries } from "./agentInternalCommandEntries";
2
+ import type { CommandEntry } from "./cliCommandTypes";
3
+ import { getSystemInternalCommandEntries } from "./systemInternalCommandEntries";
4
+ import { getWorkflowInternalCommandEntries } from "./workflowInternalCommandEntries";
5
+
6
+ export function getInternalCommandEntries(renderHelpText: () => string): CommandEntry[] {
7
+ return [
8
+ ...getAgentInternalCommandEntries(),
9
+ ...getWorkflowInternalCommandEntries(),
10
+ ...getSystemInternalCommandEntries(renderHelpText),
11
+ ];
12
+ }