nolo-cli 0.1.38 → 0.1.39
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/agent-runtime/agentRecordConfig.ts +0 -14
- package/agent-runtime/hostAdapter.ts +0 -1
- package/agent-runtime/index.ts +24 -18
- package/agent-runtime/localDialogRead.ts +23 -0
- package/agent-runtime/localToolPolicy.ts +2 -7
- package/agent-runtime/localWorkspaceTools.ts +9 -0
- package/agent-runtime/noloWorkspaceTools.ts +371 -0
- package/agent-runtime/runtimeToolPolicy.ts +1 -3
- package/agent-runtime/types.ts +1 -1
- package/agentAliases.ts +6 -0
- package/agentRunCommand.ts +57 -65
- package/agentRuntimeLocal.ts +0 -7
- package/chat/messages/fetchMessages.ts +84 -0
- package/chat/messages/types.ts +185 -0
- package/cli/agentAliases.ts +6 -0
- package/cli/agentRunCommand.ts +57 -65
- package/cli/agentRuntimeLocal.ts +0 -7
- package/cli/cliEnvHelpers.ts +11 -7
- package/cli/client/agentRun.ts +55 -7
- package/cli/client/localRuntimeAdapter.ts +87 -59
- package/cli/connectorRunArtifact.ts +0 -1
- package/cli/dialogCommands.ts +572 -0
- package/cli/dialogInternalCommandEntries.ts +4 -0
- package/cli/machineWsRunDispatch.ts +29 -37
- package/cli/scriptCommandEntries.ts +0 -2
- package/cli/tui/readlineWorkspace.ts +69 -0
- package/cli/tui/session.ts +176 -1
- package/cliEnvHelpers.ts +11 -7
- package/client/agentConfigResolver.test.ts +2 -3
- package/client/agentRun.test.ts +185 -1
- package/client/agentRun.ts +55 -7
- package/client/localRuntimeAdapter.test.ts +90 -25
- package/client/localRuntimeAdapter.ts +87 -59
- package/connectorRunArtifact.ts +0 -1
- package/database/server/db.ts +99 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/memoryAuthorityStore.ts +133 -0
- package/database/server/serverStoreFactory.ts +118 -0
- package/dialogCommands.ts +572 -0
- package/dialogInternalCommandEntries.ts +4 -0
- package/machineWsRunDispatch.ts +29 -37
- package/package.json +10 -9
- package/scriptCommandEntries.ts +0 -2
- package/tui/readlineWorkspace.ts +69 -0
- package/tui/session.ts +176 -1
- package/agent-runtime/taskWorkspace.ts +0 -193
- package/agent-runtime/workspaceSession.ts +0 -76
- package/cli/client/taskWorktree.ts +0 -8
- package/cli/client/workspaceSession.ts +0 -11
- package/client/taskWorktree.ts +0 -8
- package/client/workspaceSession.test.ts +0 -57
- package/client/workspaceSession.ts +0 -11
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
resolveConnectorRunCwd as defaultResolveConnectorRunCwd,
|
|
11
11
|
} from "./connectorRunArtifact";
|
|
12
12
|
import { createCliLocalRuntimeAdapter } from "./client/localRuntimeAdapter";
|
|
13
|
-
import { prepareTaskWorktree } from "./client/taskWorktree";
|
|
14
13
|
import {
|
|
15
14
|
materializeLargeConnectorPrompt as defaultMaterializeLargeConnectorPrompt,
|
|
16
15
|
readRuntimePromptPageMeta as defaultReadRuntimePromptPageMeta,
|
|
@@ -65,7 +64,6 @@ type MachineWsRunDispatchDeps = {
|
|
|
65
64
|
collectConnectorRunArtifact?: typeof defaultCollectConnectorRunArtifact;
|
|
66
65
|
materializeLargeConnectorPrompt?: typeof defaultMaterializeLargeConnectorPrompt;
|
|
67
66
|
readRuntimePromptPageMeta?: typeof defaultReadRuntimePromptPageMeta;
|
|
68
|
-
prepareTaskWorktree?: typeof prepareTaskWorktree;
|
|
69
67
|
buildConnectorCliPrompt?: (
|
|
70
68
|
agentConfig: any,
|
|
71
69
|
userInput: string,
|
|
@@ -97,6 +95,18 @@ function logConnectorRun(message: string, fields: Record<string, unknown> = {})
|
|
|
97
95
|
console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
|
|
98
96
|
}
|
|
99
97
|
|
|
98
|
+
function summarizeRuntimePolicyForLogs(policy?: ConnectorRuntimePolicy) {
|
|
99
|
+
return {
|
|
100
|
+
hasPolicy: Boolean(policy),
|
|
101
|
+
runtimeTools: policy?.runtimeTools ?? [],
|
|
102
|
+
agentTools: policy?.agentTools ?? [],
|
|
103
|
+
workspaceMode: policy?.workspace?.mode ?? null,
|
|
104
|
+
workspaceCwd: policy?.workspace?.cwd ?? null,
|
|
105
|
+
shellEnabled: policy?.shell?.enabled ?? null,
|
|
106
|
+
shellMode: policy?.shell?.mode ?? null,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
function summarizeEndpoint(value: unknown) {
|
|
101
111
|
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
102
112
|
try {
|
|
@@ -139,14 +149,9 @@ function runtimePolicyFromConnectorPayload(parsed: any): ConnectorRuntimePolicy
|
|
|
139
149
|
return policy ? policy as ConnectorRuntimePolicy : undefined;
|
|
140
150
|
}
|
|
141
151
|
|
|
142
|
-
function requestsTaskWorktree(policy?: ConnectorRuntimePolicy) {
|
|
143
|
-
return policy?.workspace?.mode === "task-worktree" || policy?.workspace?.mode === "lease";
|
|
144
|
-
}
|
|
145
|
-
|
|
146
152
|
function requestsLocalWorkspaceRuntime(policy?: ConnectorRuntimePolicy) {
|
|
147
153
|
return Boolean(
|
|
148
|
-
Array.isArray(policy?.runtimeTools) && policy.runtimeTools.length > 0
|
|
149
|
-
requestsTaskWorktree(policy),
|
|
154
|
+
Array.isArray(policy?.runtimeTools) && policy.runtimeTools.length > 0,
|
|
150
155
|
);
|
|
151
156
|
}
|
|
152
157
|
|
|
@@ -183,13 +188,13 @@ function runtimeWorkspacePermissionPolicy(
|
|
|
183
188
|
runtimeEnv: EnvLike,
|
|
184
189
|
cwd: string
|
|
185
190
|
): PermissionPolicy | null {
|
|
186
|
-
if (!
|
|
191
|
+
if (!requestsLocalWorkspaceRuntime(runtimePolicy)) return null;
|
|
187
192
|
return {
|
|
188
193
|
mode: "full_access",
|
|
189
194
|
allowFilesystemRead: true,
|
|
190
195
|
allowFilesystemWrite: true,
|
|
191
196
|
allowShell:
|
|
192
|
-
|
|
197
|
+
runtimeEnv.NOLO_LOCAL_SHELL_MODE === "worktree" &&
|
|
193
198
|
Array.isArray(runtimePolicy?.runtimeTools) &&
|
|
194
199
|
runtimePolicy.runtimeTools.includes("execShell"),
|
|
195
200
|
writableRoots: [cwd],
|
|
@@ -201,7 +206,7 @@ function scopePermissionPolicyToRuntimeWorkspace(
|
|
|
201
206
|
runtimePolicy: ConnectorRuntimePolicy | undefined,
|
|
202
207
|
cwd: string
|
|
203
208
|
): PermissionPolicy {
|
|
204
|
-
if (!
|
|
209
|
+
if (!requestsLocalWorkspaceRuntime(runtimePolicy)) return policy;
|
|
205
210
|
return {
|
|
206
211
|
...policy,
|
|
207
212
|
writableRoots: policy.allowFilesystemWrite ? [cwd] : [],
|
|
@@ -313,7 +318,6 @@ async function defaultRunConnectorLocalRuntimeAgent(args: {
|
|
|
313
318
|
policy?.runtimeTools,
|
|
314
319
|
),
|
|
315
320
|
...(policy ? { runtimeToolPolicy: policy } : {}),
|
|
316
|
-
...(requestsTaskWorktree(policy) ? { localWorkspaceMode: "task-worktree" } : {}),
|
|
317
321
|
};
|
|
318
322
|
const store = new Map<string, any>([
|
|
319
323
|
[agentKey, agentRecord],
|
|
@@ -324,19 +328,17 @@ async function defaultRunConnectorLocalRuntimeAgent(args: {
|
|
|
324
328
|
withForwardedUserAuthToken(args.runtimeEnv, args.parsed),
|
|
325
329
|
policy
|
|
326
330
|
);
|
|
327
|
-
|
|
331
|
+
logConnectorRun("agent.run.local-runtime.env", {
|
|
332
|
+
requestId: args.parsed.requestId,
|
|
333
|
+
agentKey,
|
|
334
|
+
cwd: args.cwd,
|
|
335
|
+
runtimeEnvShellModeBeforePolicy: args.runtimeEnv.NOLO_LOCAL_SHELL_MODE ?? null,
|
|
336
|
+
runtimeEnvShellModeAfterPolicy: env.NOLO_LOCAL_SHELL_MODE ?? null,
|
|
337
|
+
...summarizeRuntimePolicyForLogs(policy),
|
|
338
|
+
});
|
|
328
339
|
const adapter = createCliLocalRuntimeAdapter({
|
|
329
340
|
env,
|
|
330
341
|
cwd: args.cwd,
|
|
331
|
-
useCwdAsTaskWorkspaceBase: wantsTaskWorktree,
|
|
332
|
-
...(wantsTaskWorktree
|
|
333
|
-
? {
|
|
334
|
-
prepareTaskWorktree: (input) => prepareTaskWorktree({
|
|
335
|
-
...input,
|
|
336
|
-
cwd: args.cwd,
|
|
337
|
-
}),
|
|
338
|
-
}
|
|
339
|
-
: {}),
|
|
340
342
|
fetchImpl: args.fetchImpl ?? fetch,
|
|
341
343
|
store: {
|
|
342
344
|
read: async (key) => store.get(key) ?? null,
|
|
@@ -447,7 +449,6 @@ export async function handleConnectorRunMessage(
|
|
|
447
449
|
deps.materializeLargeConnectorPrompt ?? defaultMaterializeLargeConnectorPrompt;
|
|
448
450
|
const readRuntimePromptPageMeta =
|
|
449
451
|
deps.readRuntimePromptPageMeta ?? defaultReadRuntimePromptPageMeta;
|
|
450
|
-
const prepareTaskWorktreeImpl = deps.prepareTaskWorktree ?? prepareTaskWorktree;
|
|
451
452
|
const runConnectorLocalRuntimeAgent =
|
|
452
453
|
deps.runConnectorLocalRuntimeAgent ?? defaultRunConnectorLocalRuntimeAgent;
|
|
453
454
|
try {
|
|
@@ -465,15 +466,6 @@ export async function handleConnectorRunMessage(
|
|
|
465
466
|
if (!buildConnectorCliPrompt) {
|
|
466
467
|
throw new Error("buildConnectorCliPrompt is required for CLI connector runs.");
|
|
467
468
|
}
|
|
468
|
-
if (requestsTaskWorktree(runtimePolicy)) {
|
|
469
|
-
const prepared = await prepareTaskWorktreeImpl({
|
|
470
|
-
agentKey: String(parsed.payload?.agentKey ?? "agent"),
|
|
471
|
-
cwd,
|
|
472
|
-
env: runtimeEnv,
|
|
473
|
-
});
|
|
474
|
-
cwd = prepared.path;
|
|
475
|
-
artifactCwd = prepared.path;
|
|
476
|
-
}
|
|
477
469
|
const runtimePermissionPolicy = !hasExplicitMachinePermissions(agentConfig)
|
|
478
470
|
? runtimeWorkspacePermissionPolicy(runtimePolicy, runtimeEnv, cwd)
|
|
479
471
|
: null;
|
|
@@ -507,7 +499,7 @@ export async function handleConnectorRunMessage(
|
|
|
507
499
|
...(runtimePromptPage?.dbKey ? { promptPageKey: runtimePromptPage.dbKey } : {}),
|
|
508
500
|
...(runtimePromptPage?.promptHash ? { promptPageHash: runtimePromptPage.promptHash } : {}),
|
|
509
501
|
workspaceRoot: artifactCwd,
|
|
510
|
-
workspaceKind:
|
|
502
|
+
workspaceKind: "current",
|
|
511
503
|
};
|
|
512
504
|
logConnectorRun("agent.run.cli.start", {
|
|
513
505
|
requestId: parsed.requestId,
|
|
@@ -580,9 +572,9 @@ export async function handleConnectorRunMessage(
|
|
|
580
572
|
artifacts,
|
|
581
573
|
runtimePolicy,
|
|
582
574
|
workspaceRoot: artifactCwd,
|
|
583
|
-
workspaceKind:
|
|
575
|
+
workspaceKind: "current",
|
|
584
576
|
});
|
|
585
|
-
if (artifactProgress
|
|
577
|
+
if (artifactProgress) sendProgress(artifactProgress);
|
|
586
578
|
send(JSON.stringify({
|
|
587
579
|
type: "agent.run.result",
|
|
588
580
|
requestId: parsed.requestId,
|
|
@@ -628,9 +620,9 @@ export async function handleConnectorRunMessage(
|
|
|
628
620
|
artifacts,
|
|
629
621
|
runtimePolicy,
|
|
630
622
|
workspaceRoot: artifactCwd,
|
|
631
|
-
workspaceKind:
|
|
623
|
+
workspaceKind: "current",
|
|
632
624
|
});
|
|
633
|
-
if (artifactProgress
|
|
625
|
+
if (artifactProgress) sendProgress(artifactProgress);
|
|
634
626
|
send(JSON.stringify({
|
|
635
627
|
type: "agent.run.result",
|
|
636
628
|
requestId: parsed.requestId,
|
|
@@ -16,8 +16,6 @@ function getDocScriptCommands(): CommandEntry[] {
|
|
|
16
16
|
|
|
17
17
|
function getSpaceScriptCommands(): CommandEntry[] {
|
|
18
18
|
return [
|
|
19
|
-
createScriptCommand(["dialog", "read"], "readDialog.ts", "Read a dialog"),
|
|
20
|
-
createScriptCommand(["dialog", "status"], "dialogStatus.ts", "Read compact dialog status"),
|
|
21
19
|
createScriptCommand(["space", "list"], "listSpaces.ts", "List joined spaces"),
|
|
22
20
|
createScriptCommand(["space", "read"], "readSpace.ts", "Read a space"),
|
|
23
21
|
createScriptCommand(["space", "delete"], "deleteSpaces.ts", "Delete spaces by safe filters"),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { stdin as defaultInput, stdout as defaultOutput } from "node:process";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
import { runAgentTurn, type RunAgentTurnResult } from "../client/agentRun";
|
|
4
5
|
import { compactDialog, type CompactDialogResult } from "../client/compactDialog";
|
|
5
6
|
import { runSelfUpdate } from "../updateCommands";
|
|
@@ -21,7 +22,9 @@ type WorkspaceOptions = {
|
|
|
21
22
|
env?: NodeJS.ProcessEnv;
|
|
22
23
|
input?: NodeJS.ReadableStream;
|
|
23
24
|
output?: NodeJS.WritableStream;
|
|
25
|
+
cliEntrypointPath?: string;
|
|
24
26
|
agentRunner?: typeof runAgentTurn;
|
|
27
|
+
cliCommandRunner?: CliCommandRunner;
|
|
25
28
|
compactRunner?: (options: {
|
|
26
29
|
serverUrl: string;
|
|
27
30
|
authToken: string;
|
|
@@ -30,6 +33,16 @@ type WorkspaceOptions = {
|
|
|
30
33
|
selfUpdater?: SelfUpdater;
|
|
31
34
|
};
|
|
32
35
|
|
|
36
|
+
type CliCommandRunner = (
|
|
37
|
+
args: string[],
|
|
38
|
+
context: {
|
|
39
|
+
env: NodeJS.ProcessEnv;
|
|
40
|
+
output: NodeJS.WritableStream;
|
|
41
|
+
scriptDir: string;
|
|
42
|
+
cliEntrypointPath: string;
|
|
43
|
+
}
|
|
44
|
+
) => Promise<number>;
|
|
45
|
+
|
|
33
46
|
async function runAgentChat(
|
|
34
47
|
scriptDir: string,
|
|
35
48
|
state: TuiState,
|
|
@@ -52,10 +65,48 @@ async function runAgentChat(
|
|
|
52
65
|
return result;
|
|
53
66
|
}
|
|
54
67
|
|
|
68
|
+
async function pipeReadableToOutput(
|
|
69
|
+
readable: ReadableStream<Uint8Array> | null,
|
|
70
|
+
output: NodeJS.WritableStream
|
|
71
|
+
) {
|
|
72
|
+
if (!readable) return;
|
|
73
|
+
const text = await new Response(readable).text();
|
|
74
|
+
if (text) output.write(text);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function resolveDefaultCliEntrypoint(scriptDir: string) {
|
|
78
|
+
if (process.argv[1]) return process.argv[1];
|
|
79
|
+
return join(scriptDir, "..", "packages", "cli", "index.ts");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function runCliCommandInChildProcess(
|
|
83
|
+
args: string[],
|
|
84
|
+
context: {
|
|
85
|
+
env: NodeJS.ProcessEnv;
|
|
86
|
+
output: NodeJS.WritableStream;
|
|
87
|
+
cliEntrypointPath: string;
|
|
88
|
+
}
|
|
89
|
+
) {
|
|
90
|
+
const proc = Bun.spawn({
|
|
91
|
+
cmd: [process.execPath, context.cliEntrypointPath, ...args],
|
|
92
|
+
stdout: "pipe",
|
|
93
|
+
stderr: "pipe",
|
|
94
|
+
env: context.env,
|
|
95
|
+
});
|
|
96
|
+
await Promise.all([
|
|
97
|
+
pipeReadableToOutput(proc.stdout, context.output),
|
|
98
|
+
pipeReadableToOutput(proc.stderr, context.output),
|
|
99
|
+
]);
|
|
100
|
+
return proc.exited;
|
|
101
|
+
}
|
|
102
|
+
|
|
55
103
|
export async function startTuiWorkspace(options: WorkspaceOptions) {
|
|
56
104
|
let state = createInitialTuiState(options.env ?? process.env);
|
|
57
105
|
const input = options.input ?? defaultInput;
|
|
58
106
|
const output = options.output ?? defaultOutput;
|
|
107
|
+
const cliEntrypointPath =
|
|
108
|
+
options.cliEntrypointPath ?? resolveDefaultCliEntrypoint(options.scriptDir);
|
|
109
|
+
const cliCommandRunner = options.cliCommandRunner ?? runCliCommandInChildProcess;
|
|
59
110
|
const selfUpdater: SelfUpdater =
|
|
60
111
|
options.selfUpdater ?? ((target) => runSelfUpdate({ output: target }));
|
|
61
112
|
const rl = createInterface({ input, output });
|
|
@@ -113,6 +164,24 @@ export async function startTuiWorkspace(options: WorkspaceOptions) {
|
|
|
113
164
|
}
|
|
114
165
|
}
|
|
115
166
|
|
|
167
|
+
if (result.action?.type === "cli-command") {
|
|
168
|
+
try {
|
|
169
|
+
const exitCode = await cliCommandRunner(result.action.args, {
|
|
170
|
+
env: options.env ?? process.env,
|
|
171
|
+
output,
|
|
172
|
+
scriptDir: options.scriptDir,
|
|
173
|
+
cliEntrypointPath,
|
|
174
|
+
});
|
|
175
|
+
if (exitCode !== 0) {
|
|
176
|
+
output.write(`[nolo] CLI command exited with code ${exitCode}.\n`);
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
output.write(
|
|
180
|
+
`[nolo] CLI command failed: ${error instanceof Error ? error.message : String(error)}\n`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
116
185
|
if (result.action?.type === "chat") {
|
|
117
186
|
const runResult = await runAgentChat(
|
|
118
187
|
options.scriptDir,
|
package/cli/tui/session.ts
CHANGED
|
@@ -63,6 +63,10 @@ export type TuiAction =
|
|
|
63
63
|
| {
|
|
64
64
|
type: "self-update";
|
|
65
65
|
}
|
|
66
|
+
| {
|
|
67
|
+
type: "cli-command";
|
|
68
|
+
args: string[];
|
|
69
|
+
}
|
|
66
70
|
| {
|
|
67
71
|
type: "exit";
|
|
68
72
|
};
|
|
@@ -152,7 +156,7 @@ export function renderTuiHelp() {
|
|
|
152
156
|
" /version Show version/update hint",
|
|
153
157
|
" /exit Leave the workspace",
|
|
154
158
|
"",
|
|
155
|
-
"You can also type normally. nolo
|
|
159
|
+
"You can also type normally. nolo routes simple read/status requests to CLI commands and sends the rest to the current agent.",
|
|
156
160
|
].join("\n");
|
|
157
161
|
}
|
|
158
162
|
|
|
@@ -203,6 +207,165 @@ function resolveSwitchTarget(rawTarget: string) {
|
|
|
203
207
|
return null;
|
|
204
208
|
}
|
|
205
209
|
|
|
210
|
+
const DIALOG_ID_PATTERN = /[0-9A-HJKMNP-TV-Z]{26}/i;
|
|
211
|
+
const DIALOG_KEY_PATTERN = /dialog-[^\s"'<>]+-[0-9A-HJKMNP-TV-Z]{26}/i;
|
|
212
|
+
const DIALOG_URL_PATTERN = /https?:\/\/[^\s"'<>]+\/(?:space\/[^/\s"'<>]+\/)?dialog-[^\s"'<>]+-[0-9A-HJKMNP-TV-Z]{26}\/?/i;
|
|
213
|
+
const GENERIC_TOKEN_PATTERN = /(?:agent-pub-|agent-|space-|page-|meta-)?[A-Za-z0-9][A-Za-z0-9._:/-]{2,}/;
|
|
214
|
+
|
|
215
|
+
function parseChineseSmallNumber(value: string) {
|
|
216
|
+
const normalized = value.trim();
|
|
217
|
+
const direct: Record<string, number> = {
|
|
218
|
+
一: 1,
|
|
219
|
+
二: 2,
|
|
220
|
+
两: 2,
|
|
221
|
+
三: 3,
|
|
222
|
+
四: 4,
|
|
223
|
+
五: 5,
|
|
224
|
+
六: 6,
|
|
225
|
+
七: 7,
|
|
226
|
+
八: 8,
|
|
227
|
+
九: 9,
|
|
228
|
+
十: 10,
|
|
229
|
+
};
|
|
230
|
+
if (direct[normalized]) return direct[normalized];
|
|
231
|
+
const teen = normalized.match(/^十([一二两三四五六七八九])$/);
|
|
232
|
+
if (teen) return 10 + direct[teen[1]];
|
|
233
|
+
const tens = normalized.match(/^([一二两三四五六七八九])十([一二两三四五六七八九])?$/);
|
|
234
|
+
if (tens) return direct[tens[1]] * 10 + (tens[2] ? direct[tens[2]] : 0);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function resolveDialogListLimit(input: string) {
|
|
239
|
+
const digitMatch =
|
|
240
|
+
input.match(/(?:最近|前|latest|last)?\s*(\d{1,3})\s*(?:个|条|篇)?\s*(?:对话|dialogs?)/i) ??
|
|
241
|
+
input.match(/(?:对话|dialogs?).*?(?:最近|前|latest|last)?\s*(\d{1,3})\s*(?:个|条|篇)?/i);
|
|
242
|
+
if (digitMatch) {
|
|
243
|
+
const parsed = Number(digitMatch[1]);
|
|
244
|
+
if (Number.isInteger(parsed) && parsed > 0) return String(parsed);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const chineseMatch =
|
|
248
|
+
input.match(/(?:最近|前)?\s*([一二两三四五六七八九十]{1,3})\s*(?:个|条|篇)?\s*对话/) ??
|
|
249
|
+
input.match(/对话.*?(?:最近|前)?\s*([一二两三四五六七八九十]{1,3})\s*(?:个|条|篇)?/);
|
|
250
|
+
if (chineseMatch) {
|
|
251
|
+
const parsed = parseChineseSmallNumber(chineseMatch[1]);
|
|
252
|
+
if (parsed && parsed > 0) return String(parsed);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function extractDialogRef(input: string) {
|
|
259
|
+
return (
|
|
260
|
+
input.match(DIALOG_URL_PATTERN)?.[0] ??
|
|
261
|
+
input.match(DIALOG_KEY_PATTERN)?.[0] ??
|
|
262
|
+
input.match(DIALOG_ID_PATTERN)?.[0] ??
|
|
263
|
+
null
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function extractTokenAfterKeywords(input: string, keywords: string[]) {
|
|
268
|
+
for (const keyword of keywords) {
|
|
269
|
+
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
270
|
+
const match = input.match(new RegExp(`${escaped}\\s+(${GENERIC_TOKEN_PATTERN.source})`, "i"));
|
|
271
|
+
if (match?.[1]) return match[1];
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function formatCliCommand(args: string[]) {
|
|
277
|
+
return `nolo ${args.join(" ")}`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function wantsList(input: string) {
|
|
281
|
+
return /(列出|列表|有哪些|所有|我的|list|show all)/i.test(input);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function wantsRead(input: string) {
|
|
285
|
+
return /(读|读取|查看|打开|查询|read|show|open)/i.test(input);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function wantsAnalysisOrComposition(input: string) {
|
|
289
|
+
return /(总结|汇总|分析|归纳|对比|解释|整理|summary|summari[sz]e|analy[sz]e|compare)/i.test(input);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function resolveSystemCliCommand(input: string) {
|
|
293
|
+
if (/(我.*登录.*谁|当前.*登录|whoami|who am i)/i.test(input)) {
|
|
294
|
+
return ["whoami"];
|
|
295
|
+
}
|
|
296
|
+
if (/(版本|version)/i.test(input) && /(?:nolo|cli|版本|version)/i.test(input)) {
|
|
297
|
+
return ["version"];
|
|
298
|
+
}
|
|
299
|
+
if (/(诊断|doctor|健康检查|health)/i.test(input) && /(?:nolo|cli|诊断|doctor)/i.test(input)) {
|
|
300
|
+
return ["doctor"];
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function resolveResourceCliCommand(input: string) {
|
|
306
|
+
if (/(agent|代理|智能体)/i.test(input)) {
|
|
307
|
+
if (wantsList(input)) return ["agent", "list"];
|
|
308
|
+
if (wantsRead(input)) {
|
|
309
|
+
const agentRef = extractTokenAfterKeywords(input, ["agent", "代理", "智能体"]);
|
|
310
|
+
if (agentRef) return ["agent", "read", agentRef];
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (/(space|空间)/i.test(input)) {
|
|
315
|
+
if (wantsList(input)) return ["space", "list"];
|
|
316
|
+
if (wantsRead(input)) {
|
|
317
|
+
const spaceRef = extractTokenAfterKeywords(input, ["space", "空间"]);
|
|
318
|
+
if (spaceRef) return ["space", "read", spaceRef];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (/(skill-doc|skill doc|技能文档|技能)/i.test(input)) {
|
|
323
|
+
if (wantsRead(input)) {
|
|
324
|
+
const docRef = extractTokenAfterKeywords(input, ["skill-doc", "skill doc", "技能文档", "技能"]);
|
|
325
|
+
if (docRef) return ["skill-doc", "read", docRef];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (/(doc|文档|page)/i.test(input)) {
|
|
330
|
+
if (wantsRead(input)) {
|
|
331
|
+
const docRef = extractTokenAfterKeywords(input, ["doc", "文档", "page"]);
|
|
332
|
+
if (docRef) return ["doc", "read", docRef];
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (/(table|表格|表|任务表)/i.test(input) && /(查询|查看|读取|query|read|show)/i.test(input)) {
|
|
337
|
+
const tableRef = extractTokenAfterKeywords(input, ["table", "表格", "表", "任务表"]);
|
|
338
|
+
if (tableRef) return ["table", "query", "--table", tableRef];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function resolveNaturalLanguageCliCommand(input: string): string[] | null {
|
|
345
|
+
const normalized = input.trim();
|
|
346
|
+
if (!normalized) return null;
|
|
347
|
+
|
|
348
|
+
const systemCommand = resolveSystemCliCommand(normalized);
|
|
349
|
+
if (systemCommand) return systemCommand;
|
|
350
|
+
|
|
351
|
+
if (/(对话|dialog)/i.test(normalized)) {
|
|
352
|
+
const dialogRef = extractDialogRef(normalized);
|
|
353
|
+
if (dialogRef && wantsRead(normalized)) {
|
|
354
|
+
return ["dialog", "read", dialogRef];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const shouldList = /(查|查询|查看|列出|找|最近|list|show|latest|recent)/i.test(normalized);
|
|
358
|
+
if (shouldList && !dialogRef && !wantsAnalysisOrComposition(normalized)) {
|
|
359
|
+
const args = ["dialog", "list"];
|
|
360
|
+
const limit = resolveDialogListLimit(normalized);
|
|
361
|
+
if (limit) args.push("--limit", limit);
|
|
362
|
+
return args;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return resolveResourceCliCommand(normalized);
|
|
367
|
+
}
|
|
368
|
+
|
|
206
369
|
export function handleTuiInput(input: string, state: TuiState): TuiInputResult {
|
|
207
370
|
const trimmed = input.trim();
|
|
208
371
|
if (!trimmed) {
|
|
@@ -210,6 +373,18 @@ export function handleTuiInput(input: string, state: TuiState): TuiInputResult {
|
|
|
210
373
|
}
|
|
211
374
|
|
|
212
375
|
if (!trimmed.startsWith("/")) {
|
|
376
|
+
const cliArgs = resolveNaturalLanguageCliCommand(trimmed);
|
|
377
|
+
if (cliArgs) {
|
|
378
|
+
return {
|
|
379
|
+
nextState: state,
|
|
380
|
+
output: `[nolo] ${formatCliCommand(cliArgs)}`,
|
|
381
|
+
action: {
|
|
382
|
+
type: "cli-command",
|
|
383
|
+
args: cliArgs,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
213
388
|
return {
|
|
214
389
|
nextState: state,
|
|
215
390
|
output: "",
|
package/cliEnvHelpers.ts
CHANGED
|
@@ -96,14 +96,18 @@ export function resolveAuthToken(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function parseUserIdFromAuthToken(token: string) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
const parts = token.trim().split(".").filter(Boolean);
|
|
100
|
+
const payloadCandidates = parts.length >= 3 ? [parts[1], parts[0]] : [parts[0]];
|
|
101
|
+
for (const payload of payloadCandidates) {
|
|
102
|
+
if (!payload) continue;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
105
|
+
if (typeof parsed?.userId === "string") return parsed.userId;
|
|
106
|
+
} catch {
|
|
107
|
+
// Try the next token shape.
|
|
108
|
+
}
|
|
106
109
|
}
|
|
110
|
+
return "";
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
export function readOption(args: string[], flag: string) {
|
|
@@ -16,7 +16,7 @@ describe("agent config resolver", () => {
|
|
|
16
16
|
customProviderUrl: "https://provider.example/v1",
|
|
17
17
|
toolNames: ["legacyTool", "readFile"],
|
|
18
18
|
tools: ["readFile", "applyEdit"],
|
|
19
|
-
runtimeBinding: { machineId: "machine-1"
|
|
19
|
+
runtimeBinding: { machineId: "machine-1" },
|
|
20
20
|
delegation: { target: "local" },
|
|
21
21
|
temperature: 0.2,
|
|
22
22
|
top_p: 0.9,
|
|
@@ -34,8 +34,7 @@ describe("agent config resolver", () => {
|
|
|
34
34
|
cliProvider: "codex",
|
|
35
35
|
customProviderUrl: "https://provider.example/v1",
|
|
36
36
|
toolNames: ["legacyTool", "readFile", "applyEdit"],
|
|
37
|
-
runtimeBinding: { machineId: "machine-1"
|
|
38
|
-
localWorkspaceMode: "task-worktree",
|
|
37
|
+
runtimeBinding: { machineId: "machine-1" },
|
|
39
38
|
delegation: { target: "local" },
|
|
40
39
|
temperature: 0.2,
|
|
41
40
|
top_p: 0.9,
|