nolo-cli 0.1.44 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,12 +100,14 @@ Current public source status:
100
100
 
101
101
  This repository is the public OSS entry point for the CLI project. The package
102
102
  is actively maintained and has frequent releases on npm. As of June 2026,
103
- `nolo-cli` is prepared at version `0.1.44`; npm reports about 3.5k downloads
104
- for the last-month window ending 2026-06-02.
103
+ `nolo-cli` source is prepared for version `0.1.45`; npm reports about 3.5k
104
+ downloads for the last-month window ending 2026-06-02. Version `0.1.44` was
105
+ published from the public `nolotus/nolo-cli` GitHub Actions npm workflow, and
106
+ `0.1.45` should be published from the same public workflow after review.
105
107
 
106
108
  The current implementation is developed in a broader private monorepo because
107
109
  it shares product infrastructure with Nolo. Public source mirroring is being
108
- prepared in a staged way so that reusable CLI, local runtime, provider adapter,
110
+ managed in a staged way so that reusable CLI, local runtime, provider adapter,
109
111
  desktop local-mode, docs, tests, and maintainer workflow code can be opened
110
112
  without exposing private product records, credentials, production operations,
111
113
  billing systems, or user-data paths.
package/agentAliases.ts CHANGED
@@ -1,95 +1,25 @@
1
- export const CURRENT_OPERATOR_USER_ID = "0e95801d90";
2
1
  export const NOLO_DEFAULT_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
3
2
  export const NOLO_DEFAULT_AGENT_KEY = `agent-pub-${NOLO_DEFAULT_AGENT_ID}`;
4
- export const NOLO_PROJECT_MANAGER_AGENT_ID = "01NOLOPROJMGR00000000MSVGG";
5
- export const NOLO_PROJECT_MANAGER_AGENT_KEY =
6
- `agent-${CURRENT_OPERATOR_USER_ID}-${NOLO_PROJECT_MANAGER_AGENT_ID}`;
7
- export const NOLO_FRONTEND_AGENT_ID = "01FRONTENDAG0000000115N4E1";
8
- export const NOLO_FRONTEND_AGENT_KEY =
9
- `agent-${CURRENT_OPERATOR_USER_ID}-${NOLO_FRONTEND_AGENT_ID}`;
10
- export const WIN_CODEX_AGENT_ID = "WINCODEX00000000FQ0LK0";
11
- export const WIN_CODEX_AGENT_KEY =
12
- `agent-0e95801d90-${WIN_CODEX_AGENT_ID}`;
13
- export const WIN_QWEN_AGENT_ID = "01CUSTOMCODEA00000001JEAG3";
14
- export const WIN_QWEN_AGENT_KEY =
15
- `agent-0e95801d90-${WIN_QWEN_AGENT_ID}`;
16
- export const MIMO_MONTH_AGENT_ID = "01MIMO25MONTH0000000NEW001";
17
- export const MIMO_MONTH_AGENT_KEY =
18
- `agent-0e95801d90-${MIMO_MONTH_AGENT_ID}`;
19
- export const QODER_AGENT_ID = "01QODERCLIAGENT00000000NEW";
20
- export const QODER_AGENT_KEY =
21
- `agent-0e95801d90-${QODER_AGENT_ID}`;
22
3
  export const LOCAL_CODEX_AGENT_ID = "01LOCALCODEXCLI000000NEW";
23
4
  export const LOCAL_CODEX_AGENT_KEY =
24
- `agent-${CURRENT_OPERATOR_USER_ID}-${LOCAL_CODEX_AGENT_ID}`;
5
+ `agent-local-${LOCAL_CODEX_AGENT_ID}`;
25
6
  export const LOCAL_QODER_AGENT_ID = "01LOCALQODERCLI000000NEW";
26
7
  export const LOCAL_QODER_AGENT_KEY =
27
- `agent-${CURRENT_OPERATOR_USER_ID}-${LOCAL_QODER_AGENT_ID}`;
8
+ `agent-local-${LOCAL_QODER_AGENT_ID}`;
28
9
 
29
10
  const AGENT_ALIAS_TO_KEY: Record<string, string> = {
30
11
  nolo: NOLO_DEFAULT_AGENT_KEY,
31
12
  default: NOLO_DEFAULT_AGENT_KEY,
32
13
  "default-nolo": NOLO_DEFAULT_AGENT_KEY,
33
14
 
34
- // Win Codex (高智力终审与架构把关)
35
- "win-codex": WIN_CODEX_AGENT_KEY,
36
- "wincodex": WIN_CODEX_AGENT_KEY,
37
- "win codex": WIN_CODEX_AGENT_KEY,
38
- "nolo-reviewer": WIN_CODEX_AGENT_KEY,
39
- reviewer: WIN_CODEX_AGENT_KEY,
40
- "nolo-code-review": WIN_CODEX_AGENT_KEY,
41
- "code-review": WIN_CODEX_AGENT_KEY,
42
- review: WIN_CODEX_AGENT_KEY,
43
- "代码审查": WIN_CODEX_AGENT_KEY,
44
- "nolo 代码审查": WIN_CODEX_AGENT_KEY,
45
-
46
- // 包月 Mimo (全栈业务代码与常规杂活物理实体)
47
- "包月mimo": MIMO_MONTH_AGENT_KEY,
48
- "包月mimo2.5": MIMO_MONTH_AGENT_KEY,
49
- "mimo-month": MIMO_MONTH_AGENT_KEY,
50
- "nolo-fullstack": MIMO_MONTH_AGENT_KEY,
51
- fullstack: MIMO_MONTH_AGENT_KEY,
52
- "full-stack": MIMO_MONTH_AGENT_KEY,
53
- "全栈": MIMO_MONTH_AGENT_KEY,
54
- "nolo 全栈工程师": MIMO_MONTH_AGENT_KEY,
55
-
56
- // Qoder CLI (本机 Qoder CLI 实现 agent)
57
- qoder: QODER_AGENT_KEY,
58
- "qoder-agent": QODER_AGENT_KEY,
59
- "qoder cli": QODER_AGENT_KEY,
60
- "qoder-cli": QODER_AGENT_KEY,
61
-
62
- // Explicit local CLI agents. These are ordinary private agent records that
63
- // run through the current computer local runtime when unbound.
15
+ // Explicit local CLI agents. These are ordinary user-scoped agent records
16
+ // that run through the current computer local runtime when unbound.
64
17
  "local-codex": LOCAL_CODEX_AGENT_KEY,
65
18
  "codex-local": LOCAL_CODEX_AGENT_KEY,
66
19
  "local codex": LOCAL_CODEX_AGENT_KEY,
67
20
  "local-qoder": LOCAL_QODER_AGENT_KEY,
68
21
  "qoder-local": LOCAL_QODER_AGENT_KEY,
69
22
  "local qoder": LOCAL_QODER_AGENT_KEY,
70
-
71
- // 产品前端实现
72
- "frontend": NOLO_FRONTEND_AGENT_KEY,
73
- "frontend-agent": NOLO_FRONTEND_AGENT_KEY,
74
- "frontend-implementer": NOLO_FRONTEND_AGENT_KEY,
75
- "nolo-frontend": NOLO_FRONTEND_AGENT_KEY,
76
- "nolo frontend": NOLO_FRONTEND_AGENT_KEY,
77
- "前端agent": NOLO_FRONTEND_AGENT_KEY,
78
- "前端 agent": NOLO_FRONTEND_AGENT_KEY,
79
- "前端": NOLO_FRONTEND_AGENT_KEY,
80
-
81
- // Win Qwen (本地轻量无Token消耗辅助)
82
- "win-qwen": WIN_QWEN_AGENT_KEY,
83
- "win qwen": WIN_QWEN_AGENT_KEY,
84
- "winqwen": WIN_QWEN_AGENT_KEY,
85
-
86
- // 其他辅助/特定角色
87
- "nolo-project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
88
- "project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
89
- "nolo-pm": NOLO_PROJECT_MANAGER_AGENT_KEY,
90
- pm: NOLO_PROJECT_MANAGER_AGENT_KEY,
91
- "项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
92
- "nolo 项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
93
23
  };
94
24
 
95
25
  function parseAgentKeyFromInput(raw: string): string {
@@ -1,6 +1,6 @@
1
1
  import type { MachineHeartbeat } from "./connector-experimental/protocol";
2
2
  import { resolveConnectorWebSocketTarget } from "./connectorWebSocketTarget";
3
- import { resolveCliAgentKeyInput } from "./agentAliases";
3
+ import { resolveAgentInput } from "./agentNameResolver";
4
4
  import {
5
5
  type AgentCommandDeps,
6
6
  type LocalCliExecutor,
@@ -173,10 +173,10 @@ export async function runAgentBindCurrentCommand(
173
173
  ) {
174
174
  const env = deps.env ?? process.env;
175
175
  const output = deps.output ?? process.stdout;
176
- const agentKey = args[0]?.trim();
177
- if (!agentKey || agentKey === "--help" || agentKey === "-h") {
178
- output.write("Usage: nolo agent bind-current <agentKey>\n");
179
- return agentKey ? 0 : 1;
176
+ const agentInput = args[0]?.trim();
177
+ if (!agentInput || agentInput === "--help" || agentInput === "-h") {
178
+ output.write("Usage: nolo agent bind-current <agent>\n");
179
+ return agentInput ? 0 : 1;
180
180
  }
181
181
 
182
182
  const authToken = resolveAuthToken(env);
@@ -197,6 +197,15 @@ export async function runAgentBindCurrentCommand(
197
197
  const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
198
198
 
199
199
  try {
200
+ const { agentKey } = await resolveAgentInput({
201
+ agentInput,
202
+ authToken,
203
+ db: deps.db,
204
+ env,
205
+ fallbackFetchImpl,
206
+ fetchImpl,
207
+ output,
208
+ });
200
209
  await heartbeatCurrentMachine({ authToken, fetchImpl, machine, serverUrl });
201
210
  const existing = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
202
211
  const updated = {
@@ -228,7 +237,7 @@ export async function runAgentBindCurrentCommand(
228
237
  return 1;
229
238
  }
230
239
 
231
- output.write(`Bound agent ${agentKey} to this machine: ${machine.name} (${machine.machineId})\n`);
240
+ output.write(`Bound agent ${agentInput} to this machine: ${machine.name} (${machine.machineId})\n`);
232
241
  return 0;
233
242
  }
234
243
 
@@ -243,8 +252,6 @@ export async function runAgentSmokeCurrentCommand(
243
252
  output.write("Usage: nolo agent smoke-current <agent> --msg \"hello\"\n");
244
253
  return agentInput ? 0 : 1;
245
254
  }
246
- const agentKey = resolveCliAgentKeyInput(agentInput);
247
-
248
255
  const authToken = resolveAuthToken(env);
249
256
  if (!authToken) {
250
257
  output.write("[nolo] agent smoke-current requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
@@ -264,6 +271,15 @@ export async function runAgentSmokeCurrentCommand(
264
271
  const sentMessages: string[] = [];
265
272
 
266
273
  try {
274
+ const { agentKey } = await resolveAgentInput({
275
+ agentInput,
276
+ authToken,
277
+ db: deps.db,
278
+ env,
279
+ fallbackFetchImpl,
280
+ fetchImpl,
281
+ output,
282
+ });
267
283
  await heartbeatCurrentMachine({ authToken, fetchImpl, machine, serverUrl });
268
284
  const existing = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
269
285
  assertSmokeCompatible(existing, machine);
@@ -358,8 +374,6 @@ export async function runAgentRuntimeDoctorCommand(
358
374
  output.write("Usage: nolo agent runtime-doctor <agentKey>\n");
359
375
  return agentInput ? 0 : 1;
360
376
  }
361
- const agentKey = resolveCliAgentKeyInput(agentInput);
362
-
363
377
  const authToken = resolveAuthToken(env);
364
378
  if (!authToken) {
365
379
  output.write("[nolo] agent runtime-doctor requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
@@ -372,6 +386,15 @@ export async function runAgentRuntimeDoctorCommand(
372
386
  const machine = await (deps.machineInfo ?? detectLaunchableMachineInfo)();
373
387
 
374
388
  try {
389
+ const { agentKey } = await resolveAgentInput({
390
+ agentInput,
391
+ authToken,
392
+ db: deps.db,
393
+ env,
394
+ fallbackFetchImpl,
395
+ fetchImpl,
396
+ output,
397
+ });
375
398
  const agent = await readAgentRecord({ agentKey, authToken, fallbackFetchImpl, fetchImpl, serverUrl });
376
399
  const requiredCapability = requiredCapabilityForAgent(agent);
377
400
  const runtimeClass = classifyAgentRuntime(agent);
@@ -0,0 +1,164 @@
1
+ import { getReadableCliDb, type OutputLike } from "./agentCommandSupport";
2
+ import {
3
+ listLocalCachedAgents,
4
+ listRemoteAgentsAcrossServers,
5
+ type ListedAgent,
6
+ } from "./agentListHelpers";
7
+ import { resolveCliAgentKeyInput } from "./agentAliases";
8
+ import {
9
+ parseUserIdFromAuthToken,
10
+ resolveAuthToken,
11
+ resolveServerCandidates,
12
+ resolveServerUrl,
13
+ type EnvLike,
14
+ } from "./cliEnvHelpers";
15
+ import type { CliKvDb } from "./client/hybridRecordStore";
16
+
17
+ export type ResolvedAgentInput = {
18
+ agentKey: string;
19
+ agentName: string;
20
+ source: "explicit" | "agent-list";
21
+ };
22
+
23
+ function normalizeAgentName(value: string) {
24
+ return value.trim().toLowerCase();
25
+ }
26
+
27
+ function isExplicitAgentKey(value: string) {
28
+ return /^(agent|cybot)-(pub-|[^-]+-).+/i.test(value);
29
+ }
30
+
31
+ function findAgentByName(input: string, agents: ListedAgent[]) {
32
+ const normalized = normalizeAgentName(input);
33
+ return agents.filter((agent) => normalizeAgentName(agent.name) === normalized);
34
+ }
35
+
36
+ function formatAmbiguousAgentName(input: string, matches: ListedAgent[]) {
37
+ return [
38
+ `ambiguous agent name: ${input}`,
39
+ ...matches.map((agent) => `- ${agent.name}: ${agent.privateKey}`),
40
+ ].join("\n");
41
+ }
42
+
43
+ function isAmbiguousAgentNameError(error: unknown) {
44
+ return error instanceof Error && error.message.startsWith("ambiguous agent name:");
45
+ }
46
+
47
+ async function listLocalAgentsForResolution(args: {
48
+ authToken: string;
49
+ db?: CliKvDb;
50
+ output: OutputLike;
51
+ }) {
52
+ const userId = parseUserIdFromAuthToken(args.authToken);
53
+ if (!userId) {
54
+ throw new Error("could not read userId from AUTH_TOKEN; run `nolo login` first.");
55
+ }
56
+ const db = args.db ?? await getReadableCliDb(args.output);
57
+ return listLocalCachedAgents({ db, userId });
58
+ }
59
+
60
+ async function listRemoteAgentsForResolution(args: {
61
+ authToken: string;
62
+ env: EnvLike;
63
+ fallbackFetchImpl?: typeof fetch;
64
+ fetchImpl: typeof fetch;
65
+ }) {
66
+ const userId = parseUserIdFromAuthToken(args.authToken);
67
+ if (!userId) {
68
+ throw new Error("could not read userId from AUTH_TOKEN; run `nolo login` first.");
69
+ }
70
+ const serverUrl = resolveServerUrl(args.env);
71
+ const serverUrls = resolveServerCandidates(args.env, serverUrl);
72
+ return (await listRemoteAgentsAcrossServers({
73
+ authToken: args.authToken,
74
+ fallbackFetchImpl: args.fallbackFetchImpl,
75
+ fetchImpl: args.fetchImpl,
76
+ includeLegacy: false,
77
+ serverUrls,
78
+ userId,
79
+ })).agents;
80
+ }
81
+
82
+ function resolveUniqueAgentName(input: string, agents: ListedAgent[]) {
83
+ const matches = findAgentByName(input, agents);
84
+ if (matches.length === 1) {
85
+ return {
86
+ agentKey: matches[0].privateKey,
87
+ agentName: matches[0].name,
88
+ source: "agent-list" as const,
89
+ };
90
+ }
91
+ if (matches.length > 1) {
92
+ throw new Error(formatAmbiguousAgentName(input, matches));
93
+ }
94
+ return null;
95
+ }
96
+
97
+ export async function resolveAgentInput(args: {
98
+ agentInput: string;
99
+ authToken?: string;
100
+ db?: CliKvDb;
101
+ env: EnvLike;
102
+ fallbackFetchImpl?: typeof fetch;
103
+ fetchImpl: typeof fetch;
104
+ output: OutputLike;
105
+ }): Promise<ResolvedAgentInput> {
106
+ const parsed = resolveCliAgentKeyInput(args.agentInput);
107
+ if (isExplicitAgentKey(parsed)) {
108
+ return {
109
+ agentKey: parsed,
110
+ agentName: args.agentInput,
111
+ source: "explicit",
112
+ };
113
+ }
114
+
115
+ const authToken = args.authToken ?? resolveAuthToken(args.env);
116
+ if (!authToken) {
117
+ throw new Error(
118
+ `agent not found: ${args.agentInput}. Run \`nolo login\` and \`nolo agent list\`, or pass an explicit agent key.`
119
+ );
120
+ }
121
+
122
+ if (args.db) {
123
+ try {
124
+ const localAgents = await listLocalAgentsForResolution({
125
+ authToken,
126
+ db: args.db,
127
+ output: args.output,
128
+ });
129
+ const localMatch = resolveUniqueAgentName(parsed, localAgents);
130
+ if (localMatch) return localMatch;
131
+ } catch (error) {
132
+ if (isAmbiguousAgentNameError(error)) throw error;
133
+ // Local cache is an optimization; remote agent list remains authoritative enough to resolve names.
134
+ }
135
+ }
136
+
137
+ try {
138
+ const remoteAgents = await listRemoteAgentsForResolution({
139
+ authToken,
140
+ env: args.env,
141
+ fallbackFetchImpl: args.fallbackFetchImpl,
142
+ fetchImpl: args.fetchImpl,
143
+ });
144
+ const remoteMatch = resolveUniqueAgentName(parsed, remoteAgents);
145
+ if (remoteMatch) return remoteMatch;
146
+ } catch (error) {
147
+ if (isAmbiguousAgentNameError(error)) throw error;
148
+ if (!args.db) {
149
+ try {
150
+ const localAgents = await listLocalAgentsForResolution({
151
+ authToken,
152
+ output: args.output,
153
+ });
154
+ const localMatch = resolveUniqueAgentName(parsed, localAgents);
155
+ if (localMatch) return localMatch;
156
+ } catch (localError) {
157
+ if (isAmbiguousAgentNameError(localError)) throw localError;
158
+ // Fall through to the user-facing not-found message below.
159
+ }
160
+ }
161
+ }
162
+
163
+ throw new Error(`agent not found by name: ${args.agentInput}. Run \`nolo agent list\` to see available agents.`);
164
+ }
@@ -1,4 +1,3 @@
1
- import { resolveCliAgentKeyInput } from "./agentAliases";
2
1
  import { getReadableCliDb, type AgentCommandDeps } from "./agentCommandSupport";
3
2
  import {
4
3
  buildUpdatedAgentRecord,
@@ -28,7 +27,6 @@ export async function runAgentReadCommand(
28
27
  return 1;
29
28
  }
30
29
 
31
- const agentKey = resolveCliAgentKeyInput(agentInput);
32
30
  const db = deps.db ?? await getReadableCliDb(output);
33
31
  const fetchImpl = deps.fetchImpl ?? fetch;
34
32
  const fallbackFetchImpl = deps.fallbackFetchImpl;
@@ -43,7 +41,7 @@ export async function runAgentReadCommand(
43
41
  fallbackFetchImpl,
44
42
  });
45
43
  if (!result) {
46
- throw new Error(`agent not found: ${agentKey}`);
44
+ throw new Error(`agent not found: ${agentInput}`);
47
45
  }
48
46
  output.write(JSON.stringify({
49
47
  ...normalizeAgentRecordForOutput(result.agentKey, authToken, result.record),
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { DataType } from "./create/types";
3
3
  import { createAgentKey } from "./database/keys";
4
4
  import { resolveCliAgentKeyInput } from "./agentAliases";
5
+ import { resolveAgentInput } from "./agentNameResolver";
5
6
  import type { CliKvDb } from "./client/hybridRecordStore";
6
7
  import { buildLocalAgentLookupKeys, shouldReadAgentKeyRemotely } from "./client/localAgentRecords";
7
8
  import {
@@ -25,6 +26,8 @@ const PROVIDER_COPY_FIELDS = [
25
26
  "outputPrice",
26
27
  ] as const;
27
28
 
29
+ const silentOutput = { write() {} };
30
+
28
31
  function readRepeatedOption(args: string[], flag: string) {
29
32
  const values: string[] = [];
30
33
  for (let index = 0; index < args.length; index += 1) {
@@ -283,10 +286,25 @@ export async function resolveAgentRecordFromHybridStore(args: {
283
286
  fetchImpl: typeof fetch;
284
287
  fallbackFetchImpl?: typeof fetch;
285
288
  }) {
286
- const agentKey = resolveCliAgentKeyInput(args.agentInput);
287
289
  const authToken = args.cliArgs
288
290
  ? resolveAuthToken(args.cliArgs, args.env)
289
291
  : resolveAuthToken(args.env);
292
+ const resolvedAgent = await resolveAgentInput({
293
+ agentInput: args.agentInput,
294
+ authToken,
295
+ db: args.db,
296
+ env: args.env,
297
+ fallbackFetchImpl: args.fallbackFetchImpl,
298
+ fetchImpl: args.fetchImpl,
299
+ output: silentOutput,
300
+ }).catch((error) => {
301
+ if (error instanceof Error && error.message.startsWith("ambiguous agent name:")) {
302
+ throw error;
303
+ }
304
+ return null;
305
+ });
306
+ if (!resolvedAgent) return null;
307
+ const agentKey = resolvedAgent.agentKey;
290
308
  const defaultServerUrl = args.cliArgs
291
309
  ? resolveServerUrl(args.cliArgs, args.env)
292
310
  : resolveServerUrl(args.env);
@@ -505,7 +523,10 @@ export async function buildUpdatedAgentRecord(args: {
505
523
  fetchImpl: args.fetchImpl,
506
524
  fallbackFetchImpl: args.fallbackFetchImpl,
507
525
  });
508
- const agentKey = cached?.agentKey ?? resolveCliAgentKeyInput(args.parsed.agentInput);
526
+ if (!cached) {
527
+ throw new Error(`agent not found: ${args.parsed.agentInput}`);
528
+ }
529
+ const agentKey = cached.agentKey;
509
530
  const explicitServerUrl = args.cliArgs
510
531
  ? readOption(args.cliArgs, "--server-url") || readOption(args.cliArgs, "--server")
511
532
  : undefined;
@@ -4,7 +4,9 @@ import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "./agent
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { extname, resolve } from "node:path";
6
6
  import { parseSkillDocProtocol, type WorkflowReferenceConfig } from "./ai/skills/skillDocProtocol";
7
- import { LOCAL_CODEX_AGENT_KEY, MIMO_MONTH_AGENT_KEY, isLocalCliAgentKey, resolveCliAgentKeyInput } from "./agentAliases";
7
+ import { LOCAL_CODEX_AGENT_KEY, isLocalCliAgentKey, resolveCliAgentKeyInput } from "./agentAliases";
8
+ import { resolveAgentInput } from "./agentNameResolver";
9
+ import type { CliKvDb } from "./client/hybridRecordStore";
8
10
 
9
11
  type EnvLike = Record<string, string | undefined>;
10
12
 
@@ -21,6 +23,9 @@ type AgentRunCommandDeps = {
21
23
  localRuntimeAdapterFactory?: (env: EnvLike, options?: { cwd?: string }) => AgentRuntimeHostAdapter;
22
24
  inspectLocalRunWorkspace?: typeof inspectLocalRunWorkspace;
23
25
  resolveWorkflowReference?: typeof resolveWorkflowReference;
26
+ db?: CliKvDb;
27
+ fetchImpl?: typeof fetch;
28
+ fallbackFetchImpl?: typeof fetch;
24
29
  };
25
30
 
26
31
  type LocalRunWorkspaceInspection = {
@@ -93,18 +98,9 @@ function runtimeModeFromArgs(args: string[]): AgentRuntimeRequestedMode | undefi
93
98
  }
94
99
 
95
100
  function isMonthlyMimoAgentRef(raw: string | undefined, resolved: string) {
96
- const normalized = raw?.trim().toLowerCase();
97
- return (
98
- resolved === MIMO_MONTH_AGENT_KEY ||
99
- normalized === "fullstack" ||
100
- normalized === "full-stack" ||
101
- normalized === "nolo-fullstack" ||
102
- normalized === "包月mimo" ||
103
- normalized === "包月mimo2.5" ||
104
- normalized === "mimo-month" ||
105
- normalized === "全栈" ||
106
- normalized === "nolo 全栈工程师"
107
- );
101
+ void raw;
102
+ void resolved;
103
+ return false;
108
104
  }
109
105
 
110
106
  function parsePositiveInteger(value: string | undefined) {
@@ -473,6 +469,22 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
473
469
  }
474
470
 
475
471
  const runner = deps.runner ?? runAgentTurn;
472
+ let runnerAgentName = parsed.agentKey;
473
+ try {
474
+ const resolvedAgent = await resolveAgentInput({
475
+ agentInput: parsed.agentKey,
476
+ env,
477
+ db: deps.db,
478
+ fetchImpl: deps.fetchImpl ?? fetch,
479
+ fallbackFetchImpl: deps.fallbackFetchImpl,
480
+ output,
481
+ });
482
+ parsed.agentKey = resolvedAgent.agentKey;
483
+ runnerAgentName = resolvedAgent.agentName;
484
+ } catch (error) {
485
+ output.write(`[nolo] ${error instanceof Error ? error.message : String(error)}\n`);
486
+ return 1;
487
+ }
476
488
  let workflowReference: ResolvedWorkflowReference | undefined;
477
489
  if (parsed.workflowRef) {
478
490
  try {
@@ -493,7 +505,7 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
493
505
  allowShell: parsed.allowShell,
494
506
  });
495
507
  const result: RunAgentTurnResult = await runner({
496
- agentName: parsed.agentKey,
508
+ agentName: runnerAgentName,
497
509
  agentKey: parsed.agentKey,
498
510
  serverUrl: resolveServerUrl(env),
499
511
  message: prependWorkflowReferencePrompt(
@@ -1,95 +1,25 @@
1
- export const CURRENT_OPERATOR_USER_ID = "0e95801d90";
2
1
  export const NOLO_DEFAULT_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
3
2
  export const NOLO_DEFAULT_AGENT_KEY = `agent-pub-${NOLO_DEFAULT_AGENT_ID}`;
4
- export const NOLO_PROJECT_MANAGER_AGENT_ID = "01NOLOPROJMGR00000000MSVGG";
5
- export const NOLO_PROJECT_MANAGER_AGENT_KEY =
6
- `agent-${CURRENT_OPERATOR_USER_ID}-${NOLO_PROJECT_MANAGER_AGENT_ID}`;
7
- export const NOLO_FRONTEND_AGENT_ID = "01FRONTENDAG0000000115N4E1";
8
- export const NOLO_FRONTEND_AGENT_KEY =
9
- `agent-${CURRENT_OPERATOR_USER_ID}-${NOLO_FRONTEND_AGENT_ID}`;
10
- export const WIN_CODEX_AGENT_ID = "WINCODEX00000000FQ0LK0";
11
- export const WIN_CODEX_AGENT_KEY =
12
- `agent-0e95801d90-${WIN_CODEX_AGENT_ID}`;
13
- export const WIN_QWEN_AGENT_ID = "01CUSTOMCODEA00000001JEAG3";
14
- export const WIN_QWEN_AGENT_KEY =
15
- `agent-0e95801d90-${WIN_QWEN_AGENT_ID}`;
16
- export const MIMO_MONTH_AGENT_ID = "01MIMO25MONTH0000000NEW001";
17
- export const MIMO_MONTH_AGENT_KEY =
18
- `agent-0e95801d90-${MIMO_MONTH_AGENT_ID}`;
19
- export const QODER_AGENT_ID = "01QODERCLIAGENT00000000NEW";
20
- export const QODER_AGENT_KEY =
21
- `agent-0e95801d90-${QODER_AGENT_ID}`;
22
3
  export const LOCAL_CODEX_AGENT_ID = "01LOCALCODEXCLI000000NEW";
23
4
  export const LOCAL_CODEX_AGENT_KEY =
24
- `agent-${CURRENT_OPERATOR_USER_ID}-${LOCAL_CODEX_AGENT_ID}`;
5
+ `agent-local-${LOCAL_CODEX_AGENT_ID}`;
25
6
  export const LOCAL_QODER_AGENT_ID = "01LOCALQODERCLI000000NEW";
26
7
  export const LOCAL_QODER_AGENT_KEY =
27
- `agent-${CURRENT_OPERATOR_USER_ID}-${LOCAL_QODER_AGENT_ID}`;
8
+ `agent-local-${LOCAL_QODER_AGENT_ID}`;
28
9
 
29
10
  const AGENT_ALIAS_TO_KEY: Record<string, string> = {
30
11
  nolo: NOLO_DEFAULT_AGENT_KEY,
31
12
  default: NOLO_DEFAULT_AGENT_KEY,
32
13
  "default-nolo": NOLO_DEFAULT_AGENT_KEY,
33
14
 
34
- // Win Codex (高智力终审与架构把关)
35
- "win-codex": WIN_CODEX_AGENT_KEY,
36
- "wincodex": WIN_CODEX_AGENT_KEY,
37
- "win codex": WIN_CODEX_AGENT_KEY,
38
- "nolo-reviewer": WIN_CODEX_AGENT_KEY,
39
- reviewer: WIN_CODEX_AGENT_KEY,
40
- "nolo-code-review": WIN_CODEX_AGENT_KEY,
41
- "code-review": WIN_CODEX_AGENT_KEY,
42
- review: WIN_CODEX_AGENT_KEY,
43
- "代码审查": WIN_CODEX_AGENT_KEY,
44
- "nolo 代码审查": WIN_CODEX_AGENT_KEY,
45
-
46
- // 包月 Mimo (全栈业务代码与常规杂活物理实体)
47
- "包月mimo": MIMO_MONTH_AGENT_KEY,
48
- "包月mimo2.5": MIMO_MONTH_AGENT_KEY,
49
- "mimo-month": MIMO_MONTH_AGENT_KEY,
50
- "nolo-fullstack": MIMO_MONTH_AGENT_KEY,
51
- fullstack: MIMO_MONTH_AGENT_KEY,
52
- "full-stack": MIMO_MONTH_AGENT_KEY,
53
- "全栈": MIMO_MONTH_AGENT_KEY,
54
- "nolo 全栈工程师": MIMO_MONTH_AGENT_KEY,
55
-
56
- // Qoder CLI (本机 Qoder CLI 实现 agent)
57
- qoder: QODER_AGENT_KEY,
58
- "qoder-agent": QODER_AGENT_KEY,
59
- "qoder cli": QODER_AGENT_KEY,
60
- "qoder-cli": QODER_AGENT_KEY,
61
-
62
- // Explicit local CLI agents. These are ordinary private agent records that
63
- // run through the current computer local runtime when unbound.
15
+ // Explicit local CLI agents. These are ordinary user-scoped agent records
16
+ // that run through the current computer local runtime when unbound.
64
17
  "local-codex": LOCAL_CODEX_AGENT_KEY,
65
18
  "codex-local": LOCAL_CODEX_AGENT_KEY,
66
19
  "local codex": LOCAL_CODEX_AGENT_KEY,
67
20
  "local-qoder": LOCAL_QODER_AGENT_KEY,
68
21
  "qoder-local": LOCAL_QODER_AGENT_KEY,
69
22
  "local qoder": LOCAL_QODER_AGENT_KEY,
70
-
71
- // 产品前端实现
72
- "frontend": NOLO_FRONTEND_AGENT_KEY,
73
- "frontend-agent": NOLO_FRONTEND_AGENT_KEY,
74
- "frontend-implementer": NOLO_FRONTEND_AGENT_KEY,
75
- "nolo-frontend": NOLO_FRONTEND_AGENT_KEY,
76
- "nolo frontend": NOLO_FRONTEND_AGENT_KEY,
77
- "前端agent": NOLO_FRONTEND_AGENT_KEY,
78
- "前端 agent": NOLO_FRONTEND_AGENT_KEY,
79
- "前端": NOLO_FRONTEND_AGENT_KEY,
80
-
81
- // Win Qwen (本地轻量无Token消耗辅助)
82
- "win-qwen": WIN_QWEN_AGENT_KEY,
83
- "win qwen": WIN_QWEN_AGENT_KEY,
84
- "winqwen": WIN_QWEN_AGENT_KEY,
85
-
86
- // 其他辅助/特定角色
87
- "nolo-project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
88
- "project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
89
- "nolo-pm": NOLO_PROJECT_MANAGER_AGENT_KEY,
90
- pm: NOLO_PROJECT_MANAGER_AGENT_KEY,
91
- "项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
92
- "nolo 项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
93
23
  };
94
24
 
95
25
  function parseAgentKeyFromInput(raw: string): string {