nolo-cli 0.1.32 → 0.1.33

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 (230) hide show
  1. package/README.md +0 -6
  2. package/agent-runtime/agentRecordConfig.ts +2 -0
  3. package/agent-runtime/hostAdapter.ts +1 -0
  4. package/agent-runtime/index.ts +0 -21
  5. package/agent-runtime/localToolPolicy.ts +6 -13
  6. package/agent-runtime/localWorkspaceTools.ts +30 -333
  7. package/agent-runtime/platformChatProvider.ts +1 -4
  8. package/agent-runtime/runtimeToolPolicy.ts +47 -32
  9. package/agentAliases.ts +14 -83
  10. package/agentCommandSupport.ts +3 -6
  11. package/agentInternalCommandEntries.ts +2 -13
  12. package/agentListCommands.ts +19 -28
  13. package/agentMachineCommands.ts +40 -336
  14. package/agentPullCommand.ts +36 -56
  15. package/agentRecordCommands.ts +3 -5
  16. package/agentRecordHelpers.ts +22 -189
  17. package/agentRunCommand.ts +438 -107
  18. package/agentRuntimeLocal.ts +0 -10
  19. package/ai/agent/cliExecutor.ts +19 -323
  20. package/authCommands.ts +2 -36
  21. package/cli/agentAliases.ts +14 -83
  22. package/cli/agentCommandSupport.ts +3 -6
  23. package/cli/agentInternalCommandEntries.ts +2 -13
  24. package/cli/agentListCommands.ts +19 -28
  25. package/cli/agentMachineCommands.ts +40 -336
  26. package/cli/agentPullCommand.ts +36 -56
  27. package/cli/agentRecordCommands.ts +3 -5
  28. package/cli/agentRecordHelpers.ts +22 -189
  29. package/cli/agentRunCommand.ts +438 -107
  30. package/cli/agentRuntimeLocal.ts +0 -10
  31. package/cli/authCommands.ts +2 -36
  32. package/cli/cliEnvHelpers.ts +21 -162
  33. package/cli/client/agentRun.ts +853 -57
  34. package/cli/client/localRuntimeAdapter.ts +22 -159
  35. package/cli/client/streamingOutput.ts +3 -1
  36. package/cli/client/taskRunPrompt.ts +24 -7
  37. package/cli/commandRegistry.ts +2 -3
  38. package/cli/connectorRunArtifact.ts +10 -34
  39. package/cli/connectorWebSocketTarget.ts +6 -10
  40. package/cli/internalCommandEntries.ts +0 -8
  41. package/cli/localRuntimeDb.ts +2 -39
  42. package/cli/machineCommands.ts +189 -190
  43. package/cli/machineDaemonCommands.ts +165 -9
  44. package/cli/machineHeartbeatCommands.ts +2 -1
  45. package/cli/machineStatusCommands.ts +13 -5
  46. package/cli/machineWatchCommands.ts +1 -1
  47. package/cli/machineWsRunDispatch.ts +14 -48
  48. package/cli/machineWsSession.ts +0 -2
  49. package/cli/offlineMarxistsAgentCommand.ts +314 -0
  50. package/cli/runtimeDoctorCommands.ts +69 -18
  51. package/cli/scriptCommandEntries.ts +45 -5
  52. package/cli/tableCommands.ts +133 -33
  53. package/cli/taskRunCommand.ts +211 -16
  54. package/cli/tui/session.ts +372 -43
  55. package/cli/workflowInternalCommandEntries.ts +0 -20
  56. package/cliEnvHelpers.ts +21 -162
  57. package/client/agentConfigResolver.test.ts +2 -0
  58. package/client/agentRun.test.ts +12 -208
  59. package/client/agentRun.ts +853 -57
  60. package/client/localRuntimeAdapter.test.ts +9 -168
  61. package/client/localRuntimeAdapter.ts +22 -159
  62. package/client/localToolPolicy.test.ts +4 -28
  63. package/client/streamingOutput.ts +3 -1
  64. package/client/taskRunPrompt.ts +24 -7
  65. package/commandRegistry.ts +2 -3
  66. package/connectorRunArtifact.ts +10 -34
  67. package/connectorWebSocketTarget.ts +6 -10
  68. package/database/server/db.ts +34 -15
  69. package/internalCommandEntries.ts +0 -8
  70. package/localRuntimeDb.ts +2 -39
  71. package/machineCommands.ts +189 -190
  72. package/machineDaemonCommands.ts +165 -9
  73. package/machineHeartbeatCommands.ts +2 -1
  74. package/machineStatusCommands.ts +13 -5
  75. package/machineWatchCommands.ts +1 -1
  76. package/machineWsRunDispatch.ts +14 -48
  77. package/machineWsSession.ts +0 -2
  78. package/offlineMarxistsAgentCommand.ts +314 -0
  79. package/package.json +49 -193
  80. package/runtimeDoctorCommands.ts +69 -18
  81. package/scriptCommandEntries.ts +45 -5
  82. package/tableCommands.ts +133 -33
  83. package/taskRunCommand.ts +211 -16
  84. package/tui/session.ts +372 -43
  85. package/workflowInternalCommandEntries.ts +0 -20
  86. package/agent-runtime/dialogStatus.ts +0 -139
  87. package/agent-runtime/hostRuntimeConfig.ts +0 -86
  88. package/agentCreateCustomCommands.ts +0 -278
  89. package/agentDialogsCommands.ts +0 -227
  90. package/agentDoctorCommands.ts +0 -203
  91. package/agentRunLocalWorkspace.ts +0 -182
  92. package/agentRunParsing.ts +0 -298
  93. package/agentSuperviseCommands.ts +0 -8
  94. package/agentUnpublishCommands.ts +0 -113
  95. package/ai/agent/agentRunDialogLineage.ts +0 -56
  96. package/ai/policy/types.ts +0 -64
  97. package/ai/skills/skillSummaryMarker.ts +0 -26
  98. package/app/types.ts +0 -358
  99. package/build.ts +0 -16
  100. package/buildPublish.ts +0 -138
  101. package/buildPublishArtifactAssembly.ts +0 -724
  102. package/buildPublishImportAnalysis.ts +0 -242
  103. package/chat/messages/fetchMessages.ts +0 -84
  104. package/chat/messages/types.ts +0 -185
  105. package/cli/agentCreateCustomCommands.ts +0 -278
  106. package/cli/agentDialogsCommands.ts +0 -227
  107. package/cli/agentDoctorCommands.ts +0 -203
  108. package/cli/agentRunLocalWorkspace.ts +0 -182
  109. package/cli/agentRunParsing.ts +0 -298
  110. package/cli/agentSuperviseCommands.ts +0 -8
  111. package/cli/agentUnpublishCommands.ts +0 -113
  112. package/cli/buildPublish.ts +0 -138
  113. package/cli/buildPublishArtifactAssembly.ts +0 -724
  114. package/cli/buildPublishImportAnalysis.ts +0 -242
  115. package/cli/cliIoTypes.ts +0 -3
  116. package/cli/cliRuntimeDoctorReport.ts +0 -193
  117. package/cli/client/agentRunInput.ts +0 -10
  118. package/cli/client/agentRunLocalExecution.ts +0 -124
  119. package/cli/client/agentRunLocalExecutor.ts +0 -179
  120. package/cli/client/agentRunRuntimeSelection.ts +0 -145
  121. package/cli/client/agentRunServerTransport.ts +0 -397
  122. package/cli/client/agentRunTaskRunRecorder.ts +0 -176
  123. package/cli/customCodingAgentFlow.ts +0 -208
  124. package/cli/customCodingAgentRecord.ts +0 -179
  125. package/cli/dialogDataAccess.ts +0 -186
  126. package/cli/dialogInternalCommandEntries.ts +0 -11
  127. package/cli/dialogReadCommands.ts +0 -180
  128. package/cli/dialogReadHelpers.ts +0 -226
  129. package/cli/dialogStatusCommands.ts +0 -123
  130. package/cli/docInternalCommandEntries.ts +0 -20
  131. package/cli/hybridReadHelpers.ts +0 -109
  132. package/cli/machineCommandPrompts.ts +0 -79
  133. package/cli/machineCommandResolution.ts +0 -43
  134. package/cli/noloReadBridgePrompt.ts +0 -16
  135. package/cli/pageCreateCommands.ts +0 -219
  136. package/cli/pageDeleteCommands.ts +0 -167
  137. package/cli/pageReadCommands.ts +0 -151
  138. package/cli/pageUpdateCommands.ts +0 -249
  139. package/cli/pageWriteHelpers.ts +0 -209
  140. package/cli/publishPackage.ts +0 -36
  141. package/cli/runtimeInternalCommandEntries.ts +0 -15
  142. package/cli/runtimePassthroughCommands.ts +0 -22
  143. package/cli/scriptPassthroughHelpers.ts +0 -28
  144. package/cli/secretScanHelpers.ts +0 -31
  145. package/cli/spaceAttachCommands.ts +0 -134
  146. package/cli/spaceCategoryCommands.ts +0 -263
  147. package/cli/spaceCommands.ts +0 -295
  148. package/cli/spaceDeleteCommands.ts +0 -312
  149. package/cli/spaceInternalCommandEntries.ts +0 -21
  150. package/cli/spaceMutationHelpers.ts +0 -135
  151. package/cli/spaceReadCommands.ts +0 -323
  152. package/cli/tableDataOutput.ts +0 -41
  153. package/cli/tableDataSafety.ts +0 -26
  154. package/cli/tableMetaCommands.ts +0 -307
  155. package/cli/tableMutationCommands.ts +0 -266
  156. package/cli/tableQueryParsing.ts +0 -90
  157. package/cli/tableQueryRequest.ts +0 -53
  158. package/cli/taskRunActions.ts +0 -122
  159. package/cli/taskRunParsing.ts +0 -86
  160. package/cli/taskRunPromptShared.ts +0 -55
  161. package/cli/tui/tuiCatalog.ts +0 -22
  162. package/cli/tui/tuiCommandInterpreter.ts +0 -253
  163. package/cli/tui/tuiPresentation.ts +0 -146
  164. package/cliIoTypes.ts +0 -3
  165. package/cliRuntimeDoctorReport.ts +0 -193
  166. package/client/agentRunInput.ts +0 -10
  167. package/client/agentRunLocalExecution.test.ts +0 -123
  168. package/client/agentRunLocalExecution.ts +0 -124
  169. package/client/agentRunLocalExecutor.ts +0 -179
  170. package/client/agentRunRuntimeSelection.test.ts +0 -159
  171. package/client/agentRunRuntimeSelection.ts +0 -145
  172. package/client/agentRunServerTransport.ts +0 -397
  173. package/client/agentRunTaskRunRecorder.ts +0 -176
  174. package/connector-experimental/daemonLauncher.ts +0 -223
  175. package/create/space/spaceKeys.ts +0 -65
  176. package/create/types.ts +0 -21
  177. package/customCodingAgentFlow.ts +0 -208
  178. package/customCodingAgentRecord.ts +0 -179
  179. package/database/keys.ts +0 -765
  180. package/database/server/authorityStoreTypes.ts +0 -31
  181. package/database/server/legacyServerDb.ts +0 -58
  182. package/database/server/levelAuthorityStore.ts +0 -75
  183. package/database/server/memoryAuthorityStore.ts +0 -133
  184. package/database/server/serverStoreFactory.ts +0 -124
  185. package/database/tombstones.ts +0 -52
  186. package/database/utils/ulid.ts +0 -1
  187. package/dialogDataAccess.ts +0 -186
  188. package/dialogInternalCommandEntries.ts +0 -11
  189. package/dialogReadCommands.ts +0 -180
  190. package/dialogReadHelpers.ts +0 -226
  191. package/dialogStatusCommands.ts +0 -123
  192. package/docInternalCommandEntries.ts +0 -20
  193. package/hybridReadHelpers.ts +0 -109
  194. package/machineCommandPrompts.ts +0 -79
  195. package/machineCommandResolution.ts +0 -43
  196. package/noloReadBridgePrompt.ts +0 -16
  197. package/pageCreateCommands.ts +0 -219
  198. package/pageDeleteCommands.ts +0 -167
  199. package/pageReadCommands.ts +0 -151
  200. package/pageUpdateCommands.ts +0 -249
  201. package/pageWriteHelpers.ts +0 -209
  202. package/publishPackage.ts +0 -36
  203. package/runtimeInternalCommandEntries.ts +0 -15
  204. package/runtimePassthroughCommands.ts +0 -22
  205. package/scriptPassthroughHelpers.ts +0 -28
  206. package/secretScanHelpers.ts +0 -31
  207. package/share/helpers.ts +0 -302
  208. package/share/keys.ts +0 -243
  209. package/share/types.ts +0 -100
  210. package/spaceAttachCommands.ts +0 -134
  211. package/spaceCategoryCommands.ts +0 -263
  212. package/spaceCommands.ts +0 -295
  213. package/spaceDeleteCommands.ts +0 -312
  214. package/spaceInternalCommandEntries.ts +0 -21
  215. package/spaceMutationHelpers.ts +0 -135
  216. package/spaceReadCommands.ts +0 -323
  217. package/tableDataOutput.ts +0 -41
  218. package/tableDataSafety.ts +0 -26
  219. package/tableMetaCommands.ts +0 -307
  220. package/tableMutationCommands.ts +0 -266
  221. package/tableQueryParsing.ts +0 -90
  222. package/tableQueryRequest.ts +0 -53
  223. package/taskRunActions.ts +0 -122
  224. package/taskRunParsing.ts +0 -86
  225. package/taskRunPromptShared.ts +0 -55
  226. package/tui/tuiCatalog.ts +0 -22
  227. package/tui/tuiCommandInterpreter.test.ts +0 -169
  228. package/tui/tuiCommandInterpreter.ts +0 -253
  229. package/tui/tuiPresentation.test.ts +0 -154
  230. package/tui/tuiPresentation.ts +0 -146
@@ -1,13 +1,7 @@
1
1
  import { runAgentListCommand } from "./agentListCommands";
2
- import { runAgentCreateCustomCommand } from "./agentCreateCustomCommands";
3
- import { runAgentDialogsCommand } from "./agentDialogsCommands";
4
- import { runAgentDoctorCommand } from "./agentDoctorCommands";
5
- import { runAgentUnpublishCommand } from "./agentUnpublishCommands";
6
- import { runAgentSuperviseCommand } from "./agentSuperviseCommands";
7
2
  import {
8
3
  runAgentBindCurrentCommand,
9
4
  runAgentRuntimeDoctorCommand,
10
- runAgentSmokeBoundCommand,
11
5
  runAgentSmokeCurrentCommand,
12
6
  } from "./agentMachineCommands";
13
7
  import { runAgentPullCommand } from "./agentPullCommand";
@@ -17,27 +11,22 @@ import {
17
11
  } from "./agentRecordCommands";
18
12
  import { runAgentRunCommand } from "./agentRunCommand";
19
13
  import {
20
- createArgsCommand,
21
14
  createEnvCommand,
22
15
  createEnvScriptDirCommand,
23
16
  } from "./cliCommandFactories";
24
17
  import type { CommandEntry } from "./cliCommandTypes";
18
+ import { runSetupOfflineMarxistsAgentCommand } from "./offlineMarxistsAgentCommand";
25
19
 
26
20
  export function getAgentInternalCommandEntries(): CommandEntry[] {
27
21
  return [
28
22
  createEnvScriptDirCommand(["chat"], "Chat with an agent", runAgentRunCommand),
29
- createEnvCommand(["agent", "create-custom"], "Create a custom coding agent", runAgentCreateCustomCommand),
30
23
  createEnvCommand(["agent", "list"], "List owned agents", runAgentListCommand),
31
- createEnvCommand(["agent", "dialogs"], "Inspect recent dialogs for an agent", runAgentDialogsCommand),
32
- createEnvCommand(["agent", "doctor"], "Audit agent workspace health", runAgentDoctorCommand),
33
- createEnvCommand(["agent", "unpublish"], "Remove an agent's public record", runAgentUnpublishCommand),
34
24
  createEnvCommand(["agent", "pull"], "Cache an agent for local runs", runAgentPullCommand),
35
25
  createEnvCommand(["agent", "read"], "Read a single agent", runAgentReadCommand),
36
26
  createEnvScriptDirCommand(["agent", "run"], "Run an agent", runAgentRunCommand),
37
- createArgsCommand(["agent", "supervise"], "Run an agent in autonomous cycles", runAgentSuperviseCommand),
27
+ createEnvCommand(["agent", "setup-offline-marxists"], "Create or update the Marxists.org offline book agent", runSetupOfflineMarxistsAgentCommand),
38
28
  createEnvCommand(["agent", "update"], "Update agent fields", runAgentUpdateCommand),
39
29
  createEnvCommand(["agent", "bind-current"], "Bind an agent to this machine", runAgentBindCurrentCommand),
40
- createEnvCommand(["agent", "smoke-bound"], "Smoke test an agent through its existing runtime binding", runAgentSmokeBoundCommand),
41
30
  createEnvCommand(["agent", "smoke-current"], "Smoke test a bound agent through this machine", runAgentSmokeCurrentCommand),
42
31
  createEnvCommand(["agent", "runtime-doctor"], "Diagnose current machine runtime compatibility", runAgentRuntimeDoctorCommand),
43
32
  createEnvScriptDirCommand(["agent", "chat"], "Chat with an agent", runAgentRunCommand),
@@ -12,10 +12,8 @@ import {
12
12
  import {
13
13
  parseUserIdFromAuthToken,
14
14
  resolveAuthToken,
15
- resolveServerCandidates,
16
15
  resolveServerUrl,
17
16
  } from "./cliEnvHelpers";
18
- import { readFromServerCandidates, readThroughList } from "./hybridReadHelpers";
19
17
 
20
18
  export async function runAgentListCommand(
21
19
  args: string[],
@@ -25,7 +23,7 @@ export async function runAgentListCommand(
25
23
  const output = deps.output ?? process.stdout;
26
24
  const { wantJson, publicOnly, idsOnly, includeLegacy } = parseAgentListArgs(args);
27
25
 
28
- const authToken = resolveAuthToken(args, env);
26
+ const authToken = resolveAuthToken(env);
29
27
  if (!authToken) {
30
28
  output.write("[nolo] agent list requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
31
29
  return 1;
@@ -40,34 +38,27 @@ export async function runAgentListCommand(
40
38
  const db = deps.db ?? await getReadableCliDb(output);
41
39
  const fetchImpl = deps.fetchImpl ?? fetch;
42
40
  const fallbackFetchImpl = deps.fallbackFetchImpl;
43
- const serverUrl = resolveServerUrl(args, env);
44
- const serverCandidates = resolveServerCandidates(args, env, serverUrl);
45
41
 
46
42
  try {
47
- const loadRemoteAgents = async () => {
48
- const remote = await readFromServerCandidates({
49
- serverCandidates,
50
- readRemote: (candidateUrl) =>
51
- listRemoteAgents({
52
- authToken,
53
- fallbackFetchImpl,
54
- fetchImpl,
55
- includeLegacy,
56
- serverUrl: candidateUrl,
57
- userId,
58
- queryUserRecords,
59
- readDbRecord,
60
- }),
43
+ let agents: ListedAgent[];
44
+ let source: "local-cache" | "remote-cache";
45
+ try {
46
+ agents = await listLocalCachedAgents({ db, userId });
47
+ source = "local-cache";
48
+ } catch {
49
+ const serverUrl = resolveServerUrl(env);
50
+ agents = await listRemoteAgents({
51
+ authToken,
52
+ fallbackFetchImpl,
53
+ fetchImpl,
54
+ includeLegacy,
55
+ serverUrl,
56
+ userId,
57
+ queryUserRecords,
58
+ readDbRecord,
61
59
  });
62
- return remote.value;
63
- };
64
-
65
- const result = await readThroughList({
66
- readLocal: () => listLocalCachedAgents({ db, userId }),
67
- readRemote: loadRemoteAgents,
68
- });
69
- let agents = result.items;
70
- const source = result.source;
60
+ source = "remote-cache";
61
+ }
71
62
 
72
63
  if (!includeLegacy) {
73
64
  agents = agents.filter((agent) => !agent.privateKey.startsWith("cybot-"));
@@ -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: ${