nolo-cli 0.1.32 → 0.1.34

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 (234) hide show
  1. package/README.md +4 -10
  2. package/agent-runtime/agentRecordConfig.ts +10 -0
  3. package/agent-runtime/hostAdapter.ts +10 -1
  4. package/agent-runtime/index.ts +9 -20
  5. package/agent-runtime/localLoop.ts +49 -2
  6. package/agent-runtime/localToolPolicy.ts +76 -34
  7. package/agent-runtime/localWorkspaceTools.ts +30 -333
  8. package/agent-runtime/openAiCompatibleProvider.ts +7 -3
  9. package/agent-runtime/openAiCompatibleProviderConfig.ts +15 -26
  10. package/agent-runtime/platformChatProvider.ts +47 -73
  11. package/agent-runtime/providerResolution.ts +244 -0
  12. package/agent-runtime/runtimeToolPolicy.ts +77 -29
  13. package/agent-runtime/shellCommandPolicy.ts +120 -0
  14. package/agentAliases.ts +14 -83
  15. package/agentCommandSupport.ts +3 -6
  16. package/agentInternalCommandEntries.ts +4 -13
  17. package/agentListCommands.ts +18 -27
  18. package/agentMachineCommands.ts +40 -336
  19. package/agentPullCommand.ts +30 -55
  20. package/agentRecordCommands.ts +84 -1
  21. package/agentRecordHelpers.ts +102 -186
  22. package/agentRunCommand.ts +438 -107
  23. package/agentRuntimeLocal.ts +2 -9
  24. package/ai/agent/cliExecutor.ts +19 -323
  25. package/ai/tools/toolNameAliases.ts +70 -0
  26. package/authCommands.ts +2 -36
  27. package/cli/agentAliases.ts +14 -83
  28. package/cli/agentCommandSupport.ts +3 -6
  29. package/cli/agentInternalCommandEntries.ts +4 -13
  30. package/cli/agentListCommands.ts +18 -27
  31. package/cli/agentMachineCommands.ts +40 -336
  32. package/cli/agentPullCommand.ts +30 -55
  33. package/cli/agentRecordCommands.ts +84 -1
  34. package/cli/agentRecordHelpers.ts +102 -186
  35. package/cli/agentRunCommand.ts +438 -107
  36. package/cli/agentRuntimeLocal.ts +2 -9
  37. package/cli/authCommands.ts +2 -36
  38. package/cli/cliCommandDispatch.ts +2 -2
  39. package/cli/cliEnvHelpers.ts +34 -107
  40. package/cli/client/agentRun.ts +854 -57
  41. package/cli/client/localRuntimeAdapter.ts +59 -159
  42. package/cli/client/streamingOutput.ts +3 -1
  43. package/cli/client/taskRunPrompt.ts +24 -7
  44. package/cli/commandRegistry.ts +4 -5
  45. package/cli/connectorRunArtifact.ts +10 -34
  46. package/cli/connectorWebSocketTarget.ts +6 -10
  47. package/cli/internalCommandEntries.ts +0 -8
  48. package/cli/localRuntimeDb.ts +2 -39
  49. package/cli/machineCommands.ts +189 -190
  50. package/cli/machineDaemonCommands.ts +165 -9
  51. package/cli/machineHeartbeatCommands.ts +2 -1
  52. package/cli/machineStatusCommands.ts +13 -5
  53. package/cli/machineWatchCommands.ts +1 -1
  54. package/cli/machineWsRunDispatch.ts +30 -44
  55. package/cli/machineWsSession.ts +0 -2
  56. package/cli/offlineMarxistsAgentCommand.ts +314 -0
  57. package/cli/runtimeDoctorCommands.ts +72 -15
  58. package/cli/scriptCommandEntries.ts +45 -5
  59. package/cli/tableCommands.ts +133 -33
  60. package/cli/taskRunCommand.ts +211 -16
  61. package/cli/tui/session.ts +372 -43
  62. package/cli/workflowInternalCommandEntries.ts +0 -20
  63. package/cliCommandDispatch.ts +2 -2
  64. package/cliEnvHelpers.ts +34 -107
  65. package/client/agentConfigResolver.test.ts +2 -0
  66. package/client/agentRun.test.ts +12 -208
  67. package/client/agentRun.ts +854 -57
  68. package/client/localProviderResolver.test.ts +26 -2
  69. package/client/localRuntimeAdapter.test.ts +296 -171
  70. package/client/localRuntimeAdapter.ts +59 -159
  71. package/client/localToolPolicy.test.ts +4 -28
  72. package/client/streamingOutput.ts +3 -1
  73. package/client/taskRunPrompt.ts +24 -7
  74. package/commandRegistry.ts +4 -5
  75. package/connectorRunArtifact.ts +10 -34
  76. package/connectorWebSocketTarget.ts +6 -10
  77. package/database/server/serverStoreFactory.ts +2 -8
  78. package/internalCommandEntries.ts +0 -8
  79. package/localRuntimeDb.ts +2 -39
  80. package/machineCommands.ts +189 -190
  81. package/machineDaemonCommands.ts +165 -9
  82. package/machineHeartbeatCommands.ts +2 -1
  83. package/machineStatusCommands.ts +13 -5
  84. package/machineWatchCommands.ts +1 -1
  85. package/machineWsRunDispatch.ts +30 -44
  86. package/machineWsSession.ts +0 -2
  87. package/offlineMarxistsAgentCommand.ts +314 -0
  88. package/package.json +52 -188
  89. package/runtimeDoctorCommands.ts +72 -15
  90. package/scriptCommandEntries.ts +45 -5
  91. package/tableCommands.ts +133 -33
  92. package/taskRunCommand.ts +211 -16
  93. package/tui/session.ts +372 -43
  94. package/workflowInternalCommandEntries.ts +0 -20
  95. package/agent-runtime/dialogStatus.ts +0 -139
  96. package/agent-runtime/hostRuntimeConfig.ts +0 -86
  97. package/agentCreateCustomCommands.ts +0 -278
  98. package/agentDialogsCommands.ts +0 -227
  99. package/agentDoctorCommands.ts +0 -203
  100. package/agentRunLocalWorkspace.ts +0 -182
  101. package/agentRunParsing.ts +0 -298
  102. package/agentSuperviseCommands.ts +0 -8
  103. package/agentUnpublishCommands.ts +0 -113
  104. package/ai/agent/agentRunDialogLineage.ts +0 -56
  105. package/ai/policy/types.ts +0 -64
  106. package/ai/skills/skillSummaryMarker.ts +0 -26
  107. package/app/types.ts +0 -358
  108. package/build.ts +0 -16
  109. package/buildPublish.ts +0 -138
  110. package/buildPublishArtifactAssembly.ts +0 -724
  111. package/buildPublishImportAnalysis.ts +0 -242
  112. package/chat/messages/fetchMessages.ts +0 -84
  113. package/chat/messages/types.ts +0 -185
  114. package/cli/agentCreateCustomCommands.ts +0 -278
  115. package/cli/agentDialogsCommands.ts +0 -227
  116. package/cli/agentDoctorCommands.ts +0 -203
  117. package/cli/agentRunLocalWorkspace.ts +0 -182
  118. package/cli/agentRunParsing.ts +0 -298
  119. package/cli/agentSuperviseCommands.ts +0 -8
  120. package/cli/agentUnpublishCommands.ts +0 -113
  121. package/cli/buildPublish.ts +0 -138
  122. package/cli/buildPublishArtifactAssembly.ts +0 -724
  123. package/cli/buildPublishImportAnalysis.ts +0 -242
  124. package/cli/cliIoTypes.ts +0 -3
  125. package/cli/cliRuntimeDoctorReport.ts +0 -193
  126. package/cli/client/agentRunInput.ts +0 -10
  127. package/cli/client/agentRunLocalExecution.ts +0 -124
  128. package/cli/client/agentRunLocalExecutor.ts +0 -179
  129. package/cli/client/agentRunRuntimeSelection.ts +0 -145
  130. package/cli/client/agentRunServerTransport.ts +0 -397
  131. package/cli/client/agentRunTaskRunRecorder.ts +0 -176
  132. package/cli/customCodingAgentFlow.ts +0 -208
  133. package/cli/customCodingAgentRecord.ts +0 -179
  134. package/cli/dialogDataAccess.ts +0 -186
  135. package/cli/dialogInternalCommandEntries.ts +0 -11
  136. package/cli/dialogReadCommands.ts +0 -180
  137. package/cli/dialogReadHelpers.ts +0 -226
  138. package/cli/dialogStatusCommands.ts +0 -123
  139. package/cli/docInternalCommandEntries.ts +0 -20
  140. package/cli/hybridReadHelpers.ts +0 -109
  141. package/cli/machineCommandPrompts.ts +0 -79
  142. package/cli/machineCommandResolution.ts +0 -43
  143. package/cli/noloReadBridgePrompt.ts +0 -16
  144. package/cli/pageCreateCommands.ts +0 -219
  145. package/cli/pageDeleteCommands.ts +0 -167
  146. package/cli/pageReadCommands.ts +0 -151
  147. package/cli/pageUpdateCommands.ts +0 -249
  148. package/cli/pageWriteHelpers.ts +0 -209
  149. package/cli/publishPackage.ts +0 -36
  150. package/cli/runtimeInternalCommandEntries.ts +0 -15
  151. package/cli/runtimePassthroughCommands.ts +0 -22
  152. package/cli/scriptPassthroughHelpers.ts +0 -28
  153. package/cli/secretScanHelpers.ts +0 -31
  154. package/cli/spaceAttachCommands.ts +0 -134
  155. package/cli/spaceCategoryCommands.ts +0 -263
  156. package/cli/spaceCommands.ts +0 -295
  157. package/cli/spaceDeleteCommands.ts +0 -312
  158. package/cli/spaceInternalCommandEntries.ts +0 -21
  159. package/cli/spaceMutationHelpers.ts +0 -135
  160. package/cli/spaceReadCommands.ts +0 -323
  161. package/cli/tableDataOutput.ts +0 -41
  162. package/cli/tableDataSafety.ts +0 -26
  163. package/cli/tableMetaCommands.ts +0 -307
  164. package/cli/tableMutationCommands.ts +0 -266
  165. package/cli/tableQueryParsing.ts +0 -90
  166. package/cli/tableQueryRequest.ts +0 -53
  167. package/cli/taskRunActions.ts +0 -122
  168. package/cli/taskRunParsing.ts +0 -86
  169. package/cli/taskRunPromptShared.ts +0 -55
  170. package/cli/tui/tuiCatalog.ts +0 -22
  171. package/cli/tui/tuiCommandInterpreter.ts +0 -253
  172. package/cli/tui/tuiPresentation.ts +0 -146
  173. package/cliIoTypes.ts +0 -3
  174. package/cliRuntimeDoctorReport.ts +0 -193
  175. package/client/agentRunInput.ts +0 -10
  176. package/client/agentRunLocalExecution.test.ts +0 -123
  177. package/client/agentRunLocalExecution.ts +0 -124
  178. package/client/agentRunLocalExecutor.ts +0 -179
  179. package/client/agentRunRuntimeSelection.test.ts +0 -159
  180. package/client/agentRunRuntimeSelection.ts +0 -145
  181. package/client/agentRunServerTransport.ts +0 -397
  182. package/client/agentRunTaskRunRecorder.ts +0 -176
  183. package/connector-experimental/daemonLauncher.ts +0 -223
  184. package/create/space/spaceKeys.ts +0 -65
  185. package/create/types.ts +0 -21
  186. package/customCodingAgentFlow.ts +0 -208
  187. package/customCodingAgentRecord.ts +0 -179
  188. package/database/keys.ts +0 -765
  189. package/database/tombstones.ts +0 -52
  190. package/database/utils/ulid.ts +0 -1
  191. package/dialogDataAccess.ts +0 -186
  192. package/dialogInternalCommandEntries.ts +0 -11
  193. package/dialogReadCommands.ts +0 -180
  194. package/dialogReadHelpers.ts +0 -226
  195. package/dialogStatusCommands.ts +0 -123
  196. package/docInternalCommandEntries.ts +0 -20
  197. package/hybridReadHelpers.ts +0 -109
  198. package/machineCommandPrompts.ts +0 -79
  199. package/machineCommandResolution.ts +0 -43
  200. package/noloReadBridgePrompt.ts +0 -16
  201. package/pageCreateCommands.ts +0 -219
  202. package/pageDeleteCommands.ts +0 -167
  203. package/pageReadCommands.ts +0 -151
  204. package/pageUpdateCommands.ts +0 -249
  205. package/pageWriteHelpers.ts +0 -209
  206. package/publishPackage.ts +0 -36
  207. package/runtimeInternalCommandEntries.ts +0 -15
  208. package/runtimePassthroughCommands.ts +0 -22
  209. package/scriptPassthroughHelpers.ts +0 -28
  210. package/secretScanHelpers.ts +0 -31
  211. package/share/helpers.ts +0 -302
  212. package/share/keys.ts +0 -243
  213. package/share/types.ts +0 -100
  214. package/spaceAttachCommands.ts +0 -134
  215. package/spaceCategoryCommands.ts +0 -263
  216. package/spaceCommands.ts +0 -295
  217. package/spaceDeleteCommands.ts +0 -312
  218. package/spaceInternalCommandEntries.ts +0 -21
  219. package/spaceMutationHelpers.ts +0 -135
  220. package/spaceReadCommands.ts +0 -323
  221. package/tableDataOutput.ts +0 -41
  222. package/tableDataSafety.ts +0 -26
  223. package/tableMetaCommands.ts +0 -307
  224. package/tableMutationCommands.ts +0 -266
  225. package/tableQueryParsing.ts +0 -90
  226. package/tableQueryRequest.ts +0 -53
  227. package/taskRunActions.ts +0 -122
  228. package/taskRunParsing.ts +0 -86
  229. package/taskRunPromptShared.ts +0 -55
  230. package/tui/tuiCatalog.ts +0 -22
  231. package/tui/tuiCommandInterpreter.test.ts +0 -169
  232. package/tui/tuiCommandInterpreter.ts +0 -253
  233. package/tui/tuiPresentation.test.ts +0 -154
  234. package/tui/tuiPresentation.ts +0 -146
@@ -1,19 +1,13 @@
1
1
  import type { MachineHeartbeat } from "./connector-experimental/protocol";
2
2
  import { resolveConnectorWebSocketTarget } from "./connectorWebSocketTarget";
3
- import {
4
- buildCurrentUserPlatformAgentCandidateKeys,
5
- resolveCliAgentKeyInput,
6
- } from "./agentAliases";
3
+ import { resolveCliAgentKeyInput } from "./agentAliases";
7
4
  import {
8
5
  type AgentCommandDeps,
9
- getReadableCliDb,
10
6
  type LocalCliExecutor,
11
7
  type SmokeWebSocketOptions,
12
8
  } from "./agentCommandSupport";
13
- import { buildConnectorCliPrompt } from "./machineCommandPrompts";
14
9
  import {
15
10
  readAgentRecord,
16
- resolveAgentRecordFromHybridStore,
17
11
  writeAgentRecord,
18
12
  } from "./agentRecordHelpers";
19
13
  import {
@@ -34,26 +28,15 @@ async function defaultExecuteCli(
34
28
  }
35
29
 
36
30
  async function forwardConnectorRunMessage(
37
- _machine: MachineHeartbeat,
31
+ machine: MachineHeartbeat,
38
32
  message: string,
39
33
  pushMessage: (response: string) => void,
40
34
  executeCli: LocalCliExecutor,
41
35
  env: EnvLike,
42
- fetchImpl: typeof fetch,
43
- dispatchDeps?: AgentCommandDeps["connectorRunDispatchDeps"],
36
+ fetchImpl: typeof fetch
44
37
  ) {
45
- const { handleConnectorRunMessage } = await import("./machineWsRunDispatch");
46
- return handleConnectorRunMessage(
47
- message,
48
- pushMessage,
49
- executeCli,
50
- env,
51
- fetchImpl,
52
- {
53
- buildConnectorCliPrompt,
54
- ...(dispatchDeps ?? {}),
55
- },
56
- );
38
+ const { handleConnectorRunMessage } = await import("./machineCommands");
39
+ return handleConnectorRunMessage(machine, message, pushMessage, executeCli, env, fetchImpl);
57
40
  }
58
41
 
59
42
  async function detectLaunchableMachineInfo() {
@@ -86,16 +69,9 @@ function shouldRuntimeDoctorPass(args: {
86
69
  runtimeClass: string;
87
70
  requiredCapability: string;
88
71
  hasCapability: boolean;
89
- bindingRequired: boolean;
90
- isBoundToCurrent: boolean;
91
- isBindingOwnerCompatible: boolean;
92
72
  }) {
93
73
  if (args.runtimeClass !== "cli-machine") return args.runtimeClass !== "unknown";
94
- return Boolean(
95
- args.requiredCapability &&
96
- args.hasCapability &&
97
- (!args.bindingRequired || (args.isBoundToCurrent && args.isBindingOwnerCompatible))
98
- );
74
+ return Boolean(args.requiredCapability && args.hasCapability);
99
75
  }
100
76
 
101
77
  function assertSmokeCompatible(agent: any, machine: MachineHeartbeat) {
@@ -116,46 +92,6 @@ function assertSmokeCompatible(agent: any, machine: MachineHeartbeat) {
116
92
  }
117
93
  }
118
94
 
119
- function runtimeBindingFacts(agent: any, machine: MachineHeartbeat, currentUserId?: string) {
120
- const runtimeBinding = agent?.runtimeBinding && typeof agent.runtimeBinding === "object"
121
- ? agent.runtimeBinding
122
- : {};
123
- const boundMachineId = typeof runtimeBinding.machineId === "string"
124
- ? runtimeBinding.machineId.trim()
125
- : "";
126
- const boundOwnerUserId = typeof runtimeBinding.ownerUserId === "string"
127
- ? runtimeBinding.ownerUserId.trim()
128
- : "";
129
- const bindingRequired = agent?.apiSource === "cli";
130
- return {
131
- bindingRequired,
132
- boundMachineId,
133
- boundOwnerUserId,
134
- isBoundToCurrent: Boolean(boundMachineId) && boundMachineId === machine.machineId,
135
- isBindingOwnerCompatible: !boundOwnerUserId || !currentUserId || boundOwnerUserId === currentUserId,
136
- };
137
- }
138
-
139
- function assertSmokeBindingState(args: {
140
- agent: any;
141
- machine: MachineHeartbeat;
142
- currentUserId: string;
143
- allowRebind: boolean;
144
- }) {
145
- const facts = runtimeBindingFacts(args.agent, args.machine, args.currentUserId);
146
- if (args.allowRebind || !facts.bindingRequired) return;
147
- if (facts.boundMachineId && !facts.isBoundToCurrent) {
148
- throw new Error(
149
- `Agent ${args.agent?.name ?? args.agent?.dbKey ?? "unknown"} is bound to ${facts.boundMachineId}, not current machine ${args.machine.machineId}. Run \`nolo agent bind-current ${args.agent?.dbKey ?? "AGENT_KEY"}\` first or pass --bind to rebind explicitly.`
150
- );
151
- }
152
- if (facts.boundOwnerUserId && !facts.isBindingOwnerCompatible) {
153
- throw new Error(
154
- `Agent ${args.agent?.name ?? args.agent?.dbKey ?? "unknown"} is bound for owner ${facts.boundOwnerUserId}, not current user ${args.currentUserId}. \`nolo agent smoke-current\` only validates a connector launched under your current auth token, so it cannot simulate another owner's binding. If you intend to use the existing shared binding, run \`nolo agent run ${args.agent?.dbKey ?? "AGENT_KEY"}\` and make sure owner ${facts.boundOwnerUserId} has a live connector online. If you intend to take ownership of the binding on this account, run \`nolo agent bind-current ${args.agent?.dbKey ?? "AGENT_KEY"}\` first or pass --bind explicitly.`
155
- );
156
- }
157
- }
158
-
159
95
  const CONNECTOR_WS_KEEPALIVE_MS = 25_000;
160
96
 
161
97
  function buildConnectorKeepaliveMessage() {
@@ -230,75 +166,19 @@ async function heartbeatCurrentMachine(args: {
230
166
  }
231
167
  }
232
168
 
233
- async function resolveMachineAgent(args: {
234
- agentInput: string;
235
- cliArgs: string[];
236
- deps: AgentCommandDeps;
237
- env: EnvLike;
238
- fetchImpl: typeof fetch;
239
- fallbackFetchImpl?: typeof fetch;
240
- output: typeof process.stdout | { write: (chunk: string) => void };
241
- userId: string;
242
- }) {
243
- const fallbackKey = resolveCliAgentKeyInput(args.agentInput);
244
- const candidateKeys = buildCurrentUserPlatformAgentCandidateKeys(args.agentInput, args.userId);
245
- if (candidateKeys.length === 0 || candidateKeys[0] === fallbackKey) {
246
- return { agentKey: fallbackKey };
247
- }
248
-
249
- try {
250
- const db = args.deps.db ?? await getReadableCliDb(args.output as any);
251
- const resolved = await resolveAgentRecordFromHybridStore({
252
- agentInput: args.agentInput,
253
- cliArgs: args.cliArgs,
254
- env: args.env,
255
- db,
256
- fetchImpl: args.fetchImpl,
257
- fallbackFetchImpl: args.fallbackFetchImpl,
258
- });
259
- return resolved
260
- ? {
261
- agentKey: resolved.agentKey,
262
- record: resolved.record,
263
- source: resolved.source,
264
- }
265
- : { agentKey: fallbackKey };
266
- } catch {
267
- return { agentKey: fallbackKey };
268
- }
269
- }
270
-
271
- async function readResolvedAgentRecord(args: {
272
- authToken: string;
273
- fallbackFetchImpl?: typeof fetch;
274
- fetchImpl: typeof fetch;
275
- resolvedAgent: Awaited<ReturnType<typeof resolveMachineAgent>>;
276
- serverUrl: string;
277
- }) {
278
- return args.resolvedAgent.source === "remote-cache"
279
- ? args.resolvedAgent.record
280
- : await readAgentRecord({
281
- agentKey: args.resolvedAgent.agentKey,
282
- authToken: args.authToken,
283
- fallbackFetchImpl: args.fallbackFetchImpl,
284
- fetchImpl: args.fetchImpl,
285
- serverUrl: args.serverUrl,
286
- });
287
- }
288
-
289
169
  export async function runAgentBindCurrentCommand(
290
170
  args: string[],
291
171
  deps: AgentCommandDeps = {}
292
172
  ) {
293
173
  const env = deps.env ?? process.env;
294
174
  const output = deps.output ?? process.stdout;
295
- const agentInput = args[0]?.trim();
296
- if (!agentInput || agentInput === "--help" || agentInput === "-h") {
175
+ const agentKey = args[0]?.trim();
176
+ if (!agentKey || agentKey === "--help" || agentKey === "-h") {
297
177
  output.write("Usage: nolo agent bind-current <agentKey>\n");
298
- return agentInput ? 0 : 1;
178
+ return agentKey ? 0 : 1;
299
179
  }
300
180
 
301
- const authToken = resolveAuthToken(args, env);
181
+ const authToken = resolveAuthToken(env);
302
182
  if (!authToken) {
303
183
  output.write("[nolo] agent bind-current requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
304
184
  return 1;
@@ -310,28 +190,14 @@ export async function runAgentBindCurrentCommand(
310
190
  return 1;
311
191
  }
312
192
 
313
- const serverUrl = resolveServerUrl(args, env);
193
+ const serverUrl = resolveServerUrl(env);
314
194
  const fetchImpl = deps.fetchImpl ?? fetch;
315
195
  const fallbackFetchImpl = deps.fallbackFetchImpl;
316
- const resolvedAgent = await resolveMachineAgent({
317
- agentInput,
318
- cliArgs: args,
319
- deps,
320
- env,
321
- fetchImpl,
322
- fallbackFetchImpl,
323
- output,
324
- userId,
325
- });
326
- const agentKey = resolvedAgent.agentKey;
327
196
  const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
328
197
 
329
198
  try {
330
199
  await heartbeatCurrentMachine({ authToken, fetchImpl, machine, serverUrl });
331
- const existing =
332
- resolvedAgent.source === "remote-cache"
333
- ? resolvedAgent.record
334
- : await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
200
+ const existing = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
335
201
  const updated = {
336
202
  ...existing,
337
203
  runtimeBinding: {
@@ -372,13 +238,13 @@ export async function runAgentSmokeCurrentCommand(
372
238
  const env = deps.env ?? process.env;
373
239
  const output = deps.output ?? process.stdout;
374
240
  const agentInput = args[0]?.trim();
375
- const allowRebind = args.includes("--bind");
376
241
  if (!agentInput || agentInput === "--help" || agentInput === "-h") {
377
- output.write("Usage: nolo agent smoke-current <agent> [--msg \"hello\"] [--bind]\n");
242
+ output.write("Usage: nolo agent smoke-current <agent> --msg \"hello\"\n");
378
243
  return agentInput ? 0 : 1;
379
244
  }
245
+ const agentKey = resolveCliAgentKeyInput(agentInput);
380
246
 
381
- const authToken = resolveAuthToken(args, env);
247
+ const authToken = resolveAuthToken(env);
382
248
  if (!authToken) {
383
249
  output.write("[nolo] agent smoke-current requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
384
250
  return 1;
@@ -390,60 +256,35 @@ export async function runAgentSmokeCurrentCommand(
390
256
  }
391
257
 
392
258
  const userInput = readOption(args, "--msg") ?? "Smoke test from nolo connector.";
393
- const serverUrl = resolveServerUrl(args, env);
259
+ const serverUrl = resolveServerUrl(env);
394
260
  const fetchImpl = deps.fetchImpl ?? fetch;
395
261
  const fallbackFetchImpl = deps.fallbackFetchImpl;
396
- const resolvedAgent = await resolveMachineAgent({
397
- agentInput,
398
- cliArgs: args,
399
- deps,
400
- env,
401
- fetchImpl,
402
- fallbackFetchImpl,
403
- output,
404
- userId,
405
- });
406
- const agentKey = resolvedAgent.agentKey;
407
262
  const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
408
263
  const sentMessages: string[] = [];
409
264
 
410
265
  try {
411
266
  await heartbeatCurrentMachine({ authToken, fetchImpl, machine, serverUrl });
412
- const existing = await readResolvedAgentRecord({
267
+ const existing = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
268
+ assertSmokeCompatible(existing, machine);
269
+ await writeAgentRecord({
270
+ agentKey,
413
271
  authToken,
414
272
  fallbackFetchImpl,
415
273
  fetchImpl,
416
- resolvedAgent,
417
274
  serverUrl,
418
- });
419
- assertSmokeCompatible(existing, machine);
420
- assertSmokeBindingState({
421
- agent: existing,
422
- machine,
423
- currentUserId: userId,
424
- allowRebind,
425
- });
426
- if (allowRebind) {
427
- await writeAgentRecord({
428
- agentKey,
429
- authToken,
430
- fallbackFetchImpl,
431
- fetchImpl,
432
- serverUrl,
433
- userId,
434
- record: {
435
- ...existing,
436
- runtimeBinding: {
437
- ...(existing?.runtimeBinding && typeof existing.runtimeBinding === "object"
438
- ? existing.runtimeBinding
439
- : {}),
440
- machineId: machine.machineId,
441
- ownerUserId: userId,
442
- },
443
- updatedAt: Date.now(),
275
+ userId,
276
+ record: {
277
+ ...existing,
278
+ runtimeBinding: {
279
+ ...(existing?.runtimeBinding && typeof existing.runtimeBinding === "object"
280
+ ? existing.runtimeBinding
281
+ : {}),
282
+ machineId: machine.machineId,
283
+ ownerUserId: userId,
444
284
  },
445
- });
446
- }
285
+ updatedAt: Date.now(),
286
+ },
287
+ });
447
288
 
448
289
  let runResponse: any = null;
449
290
  const wsTarget = await resolveConnectorWebSocketTarget({
@@ -470,8 +311,7 @@ export async function runAgentSmokeCurrentCommand(
470
311
  AUTH_TOKEN: authToken,
471
312
  NOLO_MACHINE_API_KEY: authToken,
472
313
  },
473
- fetchImpl,
474
- deps.connectorRunDispatchDeps,
314
+ fetchImpl
475
315
  ),
476
316
  onOpen: async () => {
477
317
  const res = await fetchImpl(`${serverUrl}/api/agent/run`, {
@@ -506,106 +346,6 @@ export async function runAgentSmokeCurrentCommand(
506
346
  }
507
347
  }
508
348
 
509
- export async function runAgentSmokeBoundCommand(
510
- args: string[],
511
- deps: AgentCommandDeps = {}
512
- ) {
513
- const env = deps.env ?? process.env;
514
- const output = deps.output ?? process.stdout;
515
- const agentInput = args[0]?.trim();
516
- if (!agentInput || agentInput === "--help" || agentInput === "-h") {
517
- output.write("Usage: nolo agent smoke-bound <agent> [--msg \"hello\"]\n");
518
- return agentInput ? 0 : 1;
519
- }
520
-
521
- const authToken = resolveAuthToken(args, env);
522
- if (!authToken) {
523
- output.write("[nolo] agent smoke-bound requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
524
- return 1;
525
- }
526
- const userId = parseUserIdFromAuthToken(authToken);
527
- if (!userId) {
528
- output.write("[nolo] agent smoke-bound could not read userId from AUTH_TOKEN.\n");
529
- return 1;
530
- }
531
-
532
- const userInput = readOption(args, "--msg") ?? "Smoke test existing bound runtime from nolo.";
533
- const serverUrl = resolveServerUrl(args, env);
534
- const fetchImpl = deps.fetchImpl ?? fetch;
535
- const fallbackFetchImpl = deps.fallbackFetchImpl;
536
- const resolvedAgent = await resolveMachineAgent({
537
- agentInput,
538
- cliArgs: args,
539
- deps,
540
- env,
541
- fetchImpl,
542
- fallbackFetchImpl,
543
- output,
544
- userId,
545
- });
546
- const agentKey = resolvedAgent.agentKey;
547
-
548
- try {
549
- const existing = await readResolvedAgentRecord({
550
- authToken,
551
- fallbackFetchImpl,
552
- fetchImpl,
553
- resolvedAgent,
554
- serverUrl,
555
- });
556
- const facts = runtimeBindingFacts(existing, { machineId: "", name: "", platform: "", arch: "", capabilities: [] }, userId);
557
- if (!facts.boundMachineId) {
558
- throw new Error(
559
- `Agent ${existing?.name ?? existing?.dbKey ?? agentKey} is not currently bound to a machine. Run \`nolo agent bind-current ${agentKey}\` first.`
560
- );
561
- }
562
- if (!facts.boundOwnerUserId) {
563
- throw new Error(
564
- `Agent ${existing?.name ?? existing?.dbKey ?? agentKey} is missing runtimeBinding.ownerUserId for machine ${facts.boundMachineId}.`
565
- );
566
- }
567
-
568
- const res = await fetchImpl(`${serverUrl}/api/agent/run`, {
569
- method: "POST",
570
- headers: {
571
- Authorization: `Bearer ${authToken}`,
572
- "Content-Type": "application/json",
573
- },
574
- body: JSON.stringify({
575
- agentKey,
576
- userInput,
577
- stream: false,
578
- }),
579
- });
580
- const body = await res.json().catch(() => ({}));
581
- if (!res.ok) {
582
- const errorMessage = typeof body?.error === "string" ? body.error : `HTTP ${res.status}`;
583
- const detailMessage = typeof body?.message === "string" ? body.message : "";
584
- const reason = typeof body?.reason === "string" ? body.reason : "";
585
- throw new Error(
586
- [
587
- `agent run failed: ${errorMessage}`,
588
- detailMessage,
589
- reason ? `reason=${reason}` : "",
590
- ].filter(Boolean).join("\n")
591
- );
592
- }
593
-
594
- output.write(
595
- `Bound run OK: ${body?.dialogId ?? "no-dialog"}\nAgent key: ${agentKey}\nBound owner: ${facts.boundOwnerUserId}\nBound machine: ${facts.boundMachineId}\n`
596
- );
597
- if (body?.content) output.write(`${body.content}\n`);
598
- return 0;
599
- } catch (error) {
600
- output.write(
601
- `[nolo] agent smoke-bound failed: ${
602
- error instanceof Error ? error.message : String(error)
603
- }\n`
604
- );
605
- return 1;
606
- }
607
- }
608
-
609
349
  export async function runAgentRuntimeDoctorCommand(
610
350
  args: string[],
611
351
  deps: AgentCommandDeps = {}
@@ -617,45 +357,27 @@ export async function runAgentRuntimeDoctorCommand(
617
357
  output.write("Usage: nolo agent runtime-doctor <agentKey>\n");
618
358
  return agentInput ? 0 : 1;
619
359
  }
360
+ const agentKey = resolveCliAgentKeyInput(agentInput);
620
361
 
621
- const authToken = resolveAuthToken(args, env);
362
+ const authToken = resolveAuthToken(env);
622
363
  if (!authToken) {
623
364
  output.write("[nolo] agent runtime-doctor requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
624
365
  return 1;
625
366
  }
626
- const userId = parseUserIdFromAuthToken(authToken);
627
- if (!userId) {
628
- output.write("[nolo] agent runtime-doctor could not read userId from AUTH_TOKEN.\n");
629
- return 1;
630
- }
631
367
 
632
- const serverUrl = resolveServerUrl(args, env);
368
+ const serverUrl = resolveServerUrl(env);
633
369
  const fetchImpl = deps.fetchImpl ?? fetch;
634
370
  const fallbackFetchImpl = deps.fallbackFetchImpl;
635
- const resolvedAgent = await resolveMachineAgent({
636
- agentInput,
637
- cliArgs: args,
638
- deps,
639
- env,
640
- fetchImpl,
641
- fallbackFetchImpl,
642
- output,
643
- userId,
644
- });
645
- const agentKey = resolvedAgent.agentKey;
646
371
  const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
647
372
 
648
373
  try {
649
- const agent =
650
- resolvedAgent.source === "remote-cache"
651
- ? resolvedAgent.record
652
- : await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
374
+ const agent = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
653
375
  const requiredCapability = requiredCapabilityForAgent(agent);
654
376
  const runtimeClass = classifyAgentRuntime(agent);
377
+ const isBoundToCurrent = agent?.runtimeBinding?.machineId === machine.machineId;
655
378
  const hasCapability = requiredCapability
656
379
  ? machine.capabilities.includes(requiredCapability)
657
380
  : false;
658
- const bindingFacts = runtimeBindingFacts(agent, machine, userId);
659
381
  output.write(`Agent runtime doctor: ${agent?.name ?? agentKey}\n`);
660
382
  if (agentInput !== agentKey) output.write(`Agent input: ${agentInput}\n`);
661
383
  output.write(`Agent key: ${agentKey}\n`);
@@ -665,28 +387,10 @@ export async function runAgentRuntimeDoctorCommand(
665
387
  output.write(`Required capability: ${requiredCapability || "none"}\n`);
666
388
  output.write(`Current machine: ${machine.name} (${machine.machineId})\n`);
667
389
  output.write(`Current machine capabilities: ${machine.capabilities.length ? machine.capabilities.join(", ") : "none"}\n`);
668
- output.write(`Recorded bound machine: ${bindingFacts.boundMachineId || "none"}\n`);
669
- output.write(`Recorded binding owner: ${bindingFacts.boundOwnerUserId || "unknown"}\n`);
670
- output.write(`Current machine binding: ${runtimeClass === "cli-machine" ? (bindingFacts.isBoundToCurrent ? "yes" : "no") : "not required"}\n`);
671
- output.write(
672
- `Binding owner matches current user: ${
673
- runtimeClass === "cli-machine"
674
- ? (bindingFacts.boundOwnerUserId
675
- ? (bindingFacts.isBindingOwnerCompatible ? "yes" : "no")
676
- : "unknown")
677
- : "not required"
678
- }\n`
679
- );
390
+ output.write(`Current machine binding: ${runtimeClass === "cli-machine" ? (isBoundToCurrent ? "yes" : "no") : "not required"}\n`);
680
391
  output.write(`Current machine has required capability: ${requiredCapability ? (hasCapability ? "yes" : "no") : "not required"}\n`);
681
392
 
682
- return shouldRuntimeDoctorPass({
683
- runtimeClass,
684
- requiredCapability,
685
- hasCapability,
686
- bindingRequired: bindingFacts.bindingRequired,
687
- isBoundToCurrent: bindingFacts.isBoundToCurrent,
688
- isBindingOwnerCompatible: bindingFacts.isBindingOwnerCompatible,
689
- }) ? 0 : 1;
393
+ return shouldRuntimeDoctorPass({ runtimeClass, requiredCapability, hasCapability }) ? 0 : 1;
690
394
  } catch (error) {
691
395
  output.write(
692
396
  `[nolo] agent runtime-doctor failed: ${
@@ -1,20 +1,14 @@
1
1
  import { getDefaultCliLocalRuntimeDb } from "./localRuntimeDb";
2
2
  import type { CliLocalRuntimeDb } from "./client/localRuntimeAdapter";
3
- import type { OutputLike } from "./cliIoTypes";
4
- import {
5
- buildCurrentUserPlatformAgentCandidateKeys,
6
- resolveCliAgentKeyInput,
7
- } from "./agentAliases";
8
- import { resolveAgentRecordFromHybridStore } from "./agentRecordHelpers";
9
- import {
10
- parseUserIdFromAuthToken,
11
- readCliOptionValue,
12
- resolveAuthToken,
13
- resolveServerUrl,
14
- } from "./cliEnvHelpers";
3
+ import { resolveCliAgentKeyInput } from "./agentAliases";
4
+ import { resolveAuthToken, resolveServerUrl } from "./cliEnvHelpers";
15
5
 
16
6
  type EnvLike = Record<string, string | undefined>;
17
7
 
8
+ type OutputLike = {
9
+ write(chunk: string): unknown;
10
+ };
11
+
18
12
  type AgentPullCommandDeps = {
19
13
  env?: EnvLike;
20
14
  output?: OutputLike;
@@ -27,7 +21,8 @@ export type ParsedAgentPullArgs = {
27
21
  };
28
22
 
29
23
  function readFlagValue(args: string[], flag: string) {
30
- return readCliOptionValue(args, flag) || undefined;
24
+ const index = args.indexOf(flag);
25
+ return index >= 0 ? args[index + 1] : undefined;
31
26
  }
32
27
 
33
28
  function positionalArgs(args: string[]) {
@@ -111,57 +106,37 @@ export async function runAgentPullCommand(args: string[], deps: AgentPullCommand
111
106
  }
112
107
 
113
108
  const serverUrl = resolveServerUrl(args, env);
114
- const authToken = resolveAuthToken(args, env);
115
- const currentUserId = parseUserIdFromAuthToken(authToken);
109
+ const authToken =
110
+ readFlagValue(args, "--token") ||
111
+ readFlagValue(args, "--machine-key") ||
112
+ resolveAuthToken(env, ["BENCHMARK_AUTH_TOKEN"]);
116
113
  const fetchImpl = deps.fetchImpl ?? fetch;
117
114
  const db = deps.db ?? await getDefaultCliLocalRuntimeDb();
118
- const agentInput = readFlagValue(args, "--agent") ?? positionalArgs(args)[0] ?? parsed.agentKey;
119
- let agentKey = parsed.agentKey;
120
- let resolvedRecord: any;
121
-
122
- const candidateKeys = buildCurrentUserPlatformAgentCandidateKeys(agentInput, currentUserId);
123
- if (candidateKeys.length > 0 && candidateKeys[0] !== parsed.agentKey) {
124
- const resolved = await resolveAgentRecordFromHybridStore({
125
- agentInput,
126
- cliArgs: args,
127
- env,
128
- db,
115
+
116
+ let record: any;
117
+ try {
118
+ record = await readRemoteAgent({
119
+ agentKey: parsed.agentKey,
120
+ serverUrl,
121
+ authToken,
129
122
  fetchImpl,
130
123
  });
131
- if (resolved?.agentKey) {
132
- agentKey = resolved.agentKey;
133
- if (resolved.source === "remote-cache") {
134
- resolvedRecord = resolved.record;
135
- }
136
- }
137
- }
138
-
139
- let record: any = resolvedRecord;
140
- if (!record) {
141
- try {
142
- record = await readRemoteAgent({
143
- agentKey,
144
- serverUrl,
145
- authToken,
146
- fetchImpl,
147
- });
148
- } catch (error) {
149
- output.write(
150
- `[nolo] Failed to pull ${agentKey} from ${serverUrl}: ${
151
- error instanceof Error ? error.message : String(error)
152
- }\n`
153
- );
154
- return 1;
155
- }
124
+ } catch (error) {
125
+ output.write(
126
+ `[nolo] Failed to pull ${parsed.agentKey} from ${serverUrl}: ${
127
+ error instanceof Error ? error.message : String(error)
128
+ }\n`
129
+ );
130
+ return 1;
156
131
  }
157
132
 
158
133
  if (!record || typeof record !== "object") {
159
- output.write(`[nolo] Server returned an empty agent record for ${agentKey}.\n`);
134
+ output.write(`[nolo] Server returned an empty agent record for ${parsed.agentKey}.\n`);
160
135
  return 1;
161
136
  }
162
137
 
163
- const localRecord = normalizeAgentRecord(agentKey, record);
164
- await db.put(agentKey, localRecord);
165
- output.write(`[nolo] cached ${agentKey} in local LevelDB\n`);
138
+ const localRecord = normalizeAgentRecord(parsed.agentKey, record);
139
+ await db.put(parsed.agentKey, localRecord);
140
+ output.write(`[nolo] cached ${parsed.agentKey} in local LevelDB\n`);
166
141
  return 0;
167
142
  }