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 +5 -3
- package/agentAliases.ts +4 -74
- package/agentMachineCommands.ts +33 -10
- package/agentNameResolver.ts +164 -0
- package/agentRecordCommands.ts +1 -3
- package/agentRecordHelpers.ts +23 -2
- package/agentRunCommand.ts +26 -14
- package/cli/agentAliases.ts +4 -74
- package/cli/agentMachineCommands.ts +33 -10
- package/cli/agentNameResolver.ts +164 -0
- package/cli/agentRecordCommands.ts +1 -3
- package/cli/agentRecordHelpers.ts +23 -2
- package/cli/agentRunCommand.ts +26 -14
- package/cli/client/agentRun.ts +1 -61
- package/cli/client/localRuntimeAdapter.ts +2 -8
- package/cli/commandRegistry.ts +3 -3
- package/cli/machineCommands.ts +6 -1
- package/cli/offlineMarxistsAgentCommand.ts +4 -5
- package/client/agentRun.test.ts +13 -11
- package/client/agentRun.ts +1 -61
- package/client/localRuntimeAdapter.test.ts +22 -17
- package/client/localRuntimeAdapter.ts +2 -8
- package/commandRegistry.ts +3 -3
- package/machineCommands.ts +6 -1
- package/offlineMarxistsAgentCommand.ts +4 -5
- package/package.json +4 -2
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
|
|
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
|
-
|
|
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-${
|
|
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-${
|
|
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
|
-
//
|
|
35
|
-
|
|
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 {
|
package/agentMachineCommands.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MachineHeartbeat } from "./connector-experimental/protocol";
|
|
2
2
|
import { resolveConnectorWebSocketTarget } from "./connectorWebSocketTarget";
|
|
3
|
-
import {
|
|
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
|
|
177
|
-
if (!
|
|
178
|
-
output.write("Usage: nolo agent bind-current <
|
|
179
|
-
return
|
|
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 ${
|
|
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
|
+
}
|
package/agentRecordCommands.ts
CHANGED
|
@@ -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: ${
|
|
44
|
+
throw new Error(`agent not found: ${agentInput}`);
|
|
47
45
|
}
|
|
48
46
|
output.write(JSON.stringify({
|
|
49
47
|
...normalizeAgentRecordForOutput(result.agentKey, authToken, result.record),
|
package/agentRecordHelpers.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/agentRunCommand.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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:
|
|
508
|
+
agentName: runnerAgentName,
|
|
497
509
|
agentKey: parsed.agentKey,
|
|
498
510
|
serverUrl: resolveServerUrl(env),
|
|
499
511
|
message: prependWorkflowReferencePrompt(
|
package/cli/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-${
|
|
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-${
|
|
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
|
-
//
|
|
35
|
-
|
|
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 {
|