nolo-cli 0.1.22 → 0.1.26
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 +25 -3
- package/agent-runtime/agentRecordConfig.ts +4 -0
- package/agent-runtime/hostAdapter.ts +2 -0
- package/agent-runtime/hybridRecordStore.ts +30 -9
- package/agent-runtime/index.ts +7 -0
- package/agent-runtime/localLoop.ts +14 -4
- package/agent-runtime/localToolPolicy.ts +22 -10
- package/agent-runtime/localWorkspaceTools.ts +140 -374
- package/agent-runtime/platformChatProvider.ts +3 -0
- package/agent-runtime/runtimeToolPolicy.ts +133 -0
- package/agent-runtime/types.ts +47 -0
- package/agentAliases.ts +1 -1
- package/agentCommandSupport.ts +77 -0
- package/agentInternalCommandEntries.ts +34 -0
- package/agentListCommands.ts +123 -0
- package/agentListHelpers.ts +187 -0
- package/agentMachineCommands.ts +402 -0
- package/agentRecordCommands.ts +141 -0
- package/agentRecordHelpers.ts +395 -0
- package/agentRunCommand.ts +75 -3
- package/agentRuntimeCommands.ts +7 -773
- package/ai/agent/_executeModel.ts +29 -3
- package/ai/agent/buildSystemPrompt.ts +17 -0
- package/ai/agent/cliChatClient.ts +38 -4
- package/ai/agent/cliExecutor.ts +304 -25
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/guidedCreation/assistant.ts +82 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/draft.ts +62 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/machineRunPermissions.ts +21 -3
- package/ai/agent/runtimeGuidance.ts +1 -1
- package/ai/agent/server/fetchPublicAgents.ts +6 -1
- package/ai/agent/streamAgentChatTurn.ts +593 -36
- package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
- package/ai/ai.locale.ts +14 -2
- package/ai/chat/fetchUtils.native.ts +2 -0
- package/ai/chat/fetchUtils.ts +2 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +82 -1
- package/ai/chat/sendOpenAIResponseRequest.ts +65 -0
- package/ai/llm/getPricing.ts +14 -0
- package/ai/llm/kimi.ts +4 -1
- package/ai/llm/modelAvatar.ts +1 -0
- package/ai/llm/providers.ts +4 -1
- package/ai/llm/reasoningModels.ts +1 -0
- package/ai/taskRun/taskRunProtocol.ts +25 -10
- package/ai/token/modelUsageQuery.ts +17 -0
- package/ai/tools/agent/agentTools.ts +38 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/agent/startAgentDialogTool.ts +53 -0
- package/ai/tools/checkEnvTool.ts +52 -20
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
- package/ai/tools/index.ts +24 -14
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +9 -2
- package/ai/tools/readTool.ts +31 -0
- package/ai/tools/toolApiClient.ts +35 -1
- package/ai/tools/toolNameAliases.ts +13 -0
- package/cliCommandAdapters.ts +28 -0
- package/cliCommandDispatch.ts +66 -0
- package/cliCommandFactories.ts +34 -0
- package/cliCommandTypes.ts +49 -0
- package/cliEnvHelpers.ts +44 -0
- package/client/agentRun.test.ts +330 -24
- package/client/agentRun.ts +117 -35
- package/client/hybridRecordStore.test.ts +42 -0
- package/client/hybridRecordStore.ts +11 -0
- package/client/localRuntimeAdapter.test.ts +191 -21
- package/client/localRuntimeAdapter.ts +99 -10
- package/client/localRuntimeDryRun.test.ts +5 -2
- package/client/taskRunPrompt.ts +1 -0
- package/commandRegistry.ts +11 -61
- package/connector-experimental/capabilities.ts +1 -0
- package/database/actions/common.ts +4 -3
- package/database/config.ts +19 -0
- package/database/server/coreDataProxy.ts +43 -26
- package/index.ts +54 -191
- package/internalCommandEntries.ts +12 -0
- package/machineCommands.ts +688 -64
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +22 -2
- package/render/canvas/canvasEditContext.ts +127 -0
- package/render/canvas/canvasRuntime.ts +57 -0
- package/render/canvas/canvasSnapshotParser.ts +76 -0
- package/render/canvas/canvasTree.ts +308 -0
- package/render/canvas/types.ts +46 -0
- package/render/layout/deleteBehavior.ts +52 -0
- package/render/layout/mainLayoutSidebar.ts +17 -0
- package/render/layout/mainLayoutViewMode.ts +56 -0
- package/render/layout/topbarUtils.ts +87 -0
- package/render/layout/useDevReloadPending.ts +30 -0
- package/render/page/createPageAction.ts +183 -0
- package/render/page/docSlice.ts +468 -0
- package/render/page/server/createPage.ts +174 -0
- package/render/page/server/handleCreatePage.ts +91 -0
- package/render/page/server/index.ts +4 -0
- package/render/page/types.ts +17 -0
- package/render/page/useKeyboardSave.ts +48 -0
- package/render/styles/zIndex.ts +12 -0
- package/render/surf/WeatherIconStyles.ts +17 -0
- package/render/surf/color.ts +9 -0
- package/render/surf/config.ts +46 -0
- package/render/surf/screens/style.ts +1 -0
- package/render/surf/styles/ToggleButtonStyles.ts +8 -0
- package/render/surf/utils/groupedWeatherData.ts +32 -0
- package/render/surf/weatherUtils.ts +50 -0
- package/render/table/activityColumns.ts +6 -0
- package/render/table/createTableAction.ts +270 -0
- package/render/table/deleteTableAction.ts +129 -0
- package/render/table/fetchAndCacheTableRows.ts +174 -0
- package/render/table/tableSlice.ts +1106 -0
- package/render/table/tableView.ts +289 -0
- package/render/table/toolValueUtils.ts +363 -0
- package/render/table/types.ts +252 -0
- package/render/table/useCreateTable.ts +72 -0
- package/render/table/useTable.ts +61 -0
- package/render/table/utils/tableSerialization.ts +50 -0
- package/render/web/elements/artifactPreviewCode.ts +43 -0
- package/render/web/elements/artifactRuntimePreload.ts +52 -0
- package/render/web/elements/codeBlockAutoPreview.ts +10 -0
- package/render/web/elements/mermaidPreview.ts +21 -0
- package/render/web/ui/useInlineEdit.ts +135 -0
- package/runtimeDoctorCommands.ts +118 -0
- package/scriptCommandEntries.ts +71 -0
- package/systemInternalCommandEntries.ts +71 -0
- package/tableCommands.ts +42 -5
- package/workflowInternalCommandEntries.ts +11 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
|
|
2
|
+
|
|
3
|
+
type EnvLike = Record<string, string | undefined>;
|
|
4
|
+
|
|
5
|
+
type OutputLike = {
|
|
6
|
+
write(chunk: string): unknown;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type SetupOfflineMarxistsAgentDeps = {
|
|
10
|
+
env?: EnvLike;
|
|
11
|
+
output?: OutputLike;
|
|
12
|
+
fetchImpl?: typeof fetch;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type ParsedArgs = {
|
|
17
|
+
serverUrl: string;
|
|
18
|
+
authToken: string;
|
|
19
|
+
userId: string;
|
|
20
|
+
sourceAgentKey: string;
|
|
21
|
+
targetAgentId: string;
|
|
22
|
+
targetAgentKey: string;
|
|
23
|
+
spaceId: string;
|
|
24
|
+
name: string;
|
|
25
|
+
publicAgent: boolean;
|
|
26
|
+
json: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const DEFAULT_USER_ID = "b2e06f801f";
|
|
30
|
+
const DEFAULT_SPACE_ID = "01KKY77TT0DA9NY7TNW3R7255N";
|
|
31
|
+
const DEFAULT_SOURCE_AGENT_ID = "01MIMO25MONTH00000001PGT4X";
|
|
32
|
+
const DEFAULT_TARGET_AGENT_ID = "01OFFMARXBOOK000000010AHL1";
|
|
33
|
+
const DEFAULT_AGENT_NAME = "离线马克思主义文库书籍转换助手";
|
|
34
|
+
const TOOL_NAME = "convertMarxistsBookToOfflineHtml";
|
|
35
|
+
const AGENT_TOOLS = [TOOL_NAME];
|
|
36
|
+
|
|
37
|
+
function readFlagValue(args: string[], flag: string) {
|
|
38
|
+
const index = args.indexOf(flag);
|
|
39
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseUserIdFromToken(token: string) {
|
|
43
|
+
const payload = token.split(".")[0];
|
|
44
|
+
if (!payload) return "";
|
|
45
|
+
try {
|
|
46
|
+
const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
47
|
+
const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4);
|
|
48
|
+
const parsed = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
49
|
+
return typeof parsed?.userId === "string" ? parsed.userId : "";
|
|
50
|
+
} catch {
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeBaseUrl(value: string) {
|
|
56
|
+
return value.trim().replace(/\/+$/, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseArgs(args: string[], env: EnvLike): ParsedArgs | null {
|
|
60
|
+
if (args.includes("--help") || args.includes("-h")) return null;
|
|
61
|
+
const serverUrl = normalizeBaseUrl(
|
|
62
|
+
readFlagValue(args, "--server") ??
|
|
63
|
+
readFlagValue(args, "--server-url") ??
|
|
64
|
+
env.NOLO_SERVER ??
|
|
65
|
+
env.BASE_URL ??
|
|
66
|
+
DEFAULT_NOLO_SERVER_URL
|
|
67
|
+
);
|
|
68
|
+
const authToken = readFlagValue(args, "--token") ?? env.AUTH_TOKEN ?? env.NOLO_AUTH_TOKEN ?? "";
|
|
69
|
+
const userId =
|
|
70
|
+
readFlagValue(args, "--user-id") ??
|
|
71
|
+
env.NOLO_USER_ID ??
|
|
72
|
+
parseUserIdFromToken(authToken) ??
|
|
73
|
+
DEFAULT_USER_ID;
|
|
74
|
+
const sourceAgentKey =
|
|
75
|
+
readFlagValue(args, "--source-agent") ??
|
|
76
|
+
env.NOLO_MIMO_SOURCE_AGENT_KEY ??
|
|
77
|
+
`agent-${userId}-${DEFAULT_SOURCE_AGENT_ID}`;
|
|
78
|
+
const targetAgentId =
|
|
79
|
+
readFlagValue(args, "--target-agent-id") ??
|
|
80
|
+
env.NOLO_OFFLINE_MARXISTS_AGENT_ID ??
|
|
81
|
+
DEFAULT_TARGET_AGENT_ID;
|
|
82
|
+
const targetAgentKey =
|
|
83
|
+
readFlagValue(args, "--target-agent") ??
|
|
84
|
+
env.NOLO_OFFLINE_MARXISTS_AGENT_KEY ??
|
|
85
|
+
`agent-${userId}-${targetAgentId}`;
|
|
86
|
+
const spaceId =
|
|
87
|
+
readFlagValue(args, "--space") ??
|
|
88
|
+
env.NOLO_OFFLINE_MARXISTS_SPACE_ID ??
|
|
89
|
+
DEFAULT_SPACE_ID;
|
|
90
|
+
|
|
91
|
+
if (!authToken.trim()) return null;
|
|
92
|
+
return {
|
|
93
|
+
serverUrl,
|
|
94
|
+
authToken,
|
|
95
|
+
userId,
|
|
96
|
+
sourceAgentKey,
|
|
97
|
+
targetAgentId,
|
|
98
|
+
targetAgentKey,
|
|
99
|
+
spaceId,
|
|
100
|
+
name: readFlagValue(args, "--name") ?? DEFAULT_AGENT_NAME,
|
|
101
|
+
publicAgent: args.includes("--public"),
|
|
102
|
+
json: args.includes("--json"),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function readDbRecord(args: {
|
|
107
|
+
fetchImpl: typeof fetch;
|
|
108
|
+
serverUrl: string;
|
|
109
|
+
authToken: string;
|
|
110
|
+
dbKey: string;
|
|
111
|
+
}) {
|
|
112
|
+
const res = await args.fetchImpl(
|
|
113
|
+
`${args.serverUrl}/api/v1/db/read/${encodeURIComponent(args.dbKey)}`,
|
|
114
|
+
{ headers: { Authorization: `Bearer ${args.authToken}` } }
|
|
115
|
+
);
|
|
116
|
+
const data = await res.json().catch(() => ({}));
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new Error(`read ${args.dbKey} failed (${res.status}): ${JSON.stringify(data)}`);
|
|
119
|
+
}
|
|
120
|
+
return (data as any)?.data ?? data;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function writeDbRecord(args: {
|
|
124
|
+
fetchImpl: typeof fetch;
|
|
125
|
+
serverUrl: string;
|
|
126
|
+
authToken: string;
|
|
127
|
+
userId: string;
|
|
128
|
+
dbKey: string;
|
|
129
|
+
data: Record<string, any>;
|
|
130
|
+
}) {
|
|
131
|
+
const res = await args.fetchImpl(`${args.serverUrl}/api/v1/db/write/`, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers: {
|
|
134
|
+
"Content-Type": "application/json",
|
|
135
|
+
Authorization: `Bearer ${args.authToken}`,
|
|
136
|
+
},
|
|
137
|
+
body: JSON.stringify({
|
|
138
|
+
customKey: args.dbKey,
|
|
139
|
+
userId: args.userId,
|
|
140
|
+
data: { ...args.data, dbKey: args.dbKey },
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
const data = await res.json().catch(() => ({}));
|
|
144
|
+
if (!res.ok) {
|
|
145
|
+
throw new Error(`write ${args.dbKey} failed (${res.status}): ${JSON.stringify(data)}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function buildTargetAgent(args: {
|
|
150
|
+
parsed: ParsedArgs;
|
|
151
|
+
source: Record<string, any>;
|
|
152
|
+
now: number;
|
|
153
|
+
}) {
|
|
154
|
+
const { parsed, source, now } = args;
|
|
155
|
+
if (!source.apiKey) {
|
|
156
|
+
throw new Error(`source agent ${parsed.sourceAgentKey} has no apiKey`);
|
|
157
|
+
}
|
|
158
|
+
if (!source.customProviderUrl) {
|
|
159
|
+
throw new Error(`source agent ${parsed.sourceAgentKey} has no customProviderUrl`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
id: parsed.targetAgentId,
|
|
163
|
+
type: "agent",
|
|
164
|
+
name: parsed.name,
|
|
165
|
+
description:
|
|
166
|
+
"把 Marxists.org 中文旧式书籍页面转换为可离线打开的单文件 HTML,保留原 CSS、表格排版和背景纹理。",
|
|
167
|
+
prompt:
|
|
168
|
+
"你是离线书籍转换助手。用户给出 Marxists.org 中文书籍页面或要求保存离线书籍时,只能调用 convertMarxistsBookToOfflineHtml,不要使用普通网页抓取流程。最终回答必须逐字给出 rawData.fileUrl、rawData.pageCount、rawData.hasNetworkUrls、rawData.byteLength。",
|
|
169
|
+
isPublic: parsed.publicAgent,
|
|
170
|
+
model: source.model ?? "mimo-v2.5-pro",
|
|
171
|
+
provider: source.provider ?? "custom",
|
|
172
|
+
apiSource: source.apiSource ?? "custom",
|
|
173
|
+
apiKey: source.apiKey,
|
|
174
|
+
customProviderUrl: source.customProviderUrl,
|
|
175
|
+
apiKeyHeader: source.apiKeyHeader ?? "api-key",
|
|
176
|
+
tools: AGENT_TOOLS,
|
|
177
|
+
temperature: source.temperature ?? 0.2,
|
|
178
|
+
maxTokens: source.maxTokens ?? 4096,
|
|
179
|
+
category: "document-tools",
|
|
180
|
+
tags: ["offline-book", "marxists", "mimo"],
|
|
181
|
+
userId: parsed.userId,
|
|
182
|
+
createdAt: source.createdAt ?? now,
|
|
183
|
+
updatedAt: now,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function attachAgentToSpace(args: {
|
|
188
|
+
fetchImpl: typeof fetch;
|
|
189
|
+
parsed: ParsedArgs;
|
|
190
|
+
contentKey: string;
|
|
191
|
+
title: string;
|
|
192
|
+
now: number;
|
|
193
|
+
}) {
|
|
194
|
+
const spaceKey = `space-${args.parsed.spaceId}`;
|
|
195
|
+
const space = await readDbRecord({
|
|
196
|
+
fetchImpl: args.fetchImpl,
|
|
197
|
+
serverUrl: args.parsed.serverUrl,
|
|
198
|
+
authToken: args.parsed.authToken,
|
|
199
|
+
dbKey: spaceKey,
|
|
200
|
+
});
|
|
201
|
+
await writeDbRecord({
|
|
202
|
+
fetchImpl: args.fetchImpl,
|
|
203
|
+
serverUrl: args.parsed.serverUrl,
|
|
204
|
+
authToken: args.parsed.authToken,
|
|
205
|
+
userId: args.parsed.userId,
|
|
206
|
+
dbKey: spaceKey,
|
|
207
|
+
data: {
|
|
208
|
+
...space,
|
|
209
|
+
contents: {
|
|
210
|
+
...(space.contents ?? {}),
|
|
211
|
+
[args.contentKey]: {
|
|
212
|
+
...(space.contents?.[args.contentKey] ?? {}),
|
|
213
|
+
title: args.title,
|
|
214
|
+
type: "agent",
|
|
215
|
+
contentKey: args.contentKey,
|
|
216
|
+
createdAt: space.contents?.[args.contentKey]?.createdAt ?? args.now,
|
|
217
|
+
updatedAt: args.now,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
updatedAt: args.now,
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function writeUsage(output: OutputLike) {
|
|
226
|
+
output.write(
|
|
227
|
+
"Usage: nolo agent setup-offline-marxists [--server https://nolo.chat] [--source-agent <key>] [--space <spaceId>] [--target-agent-id <id>] [--json]\n" +
|
|
228
|
+
"Requires AUTH_TOKEN from `nolo login` or --token.\n"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function runSetupOfflineMarxistsAgentCommand(
|
|
233
|
+
args: string[],
|
|
234
|
+
deps: SetupOfflineMarxistsAgentDeps = {}
|
|
235
|
+
) {
|
|
236
|
+
const env = deps.env ?? process.env;
|
|
237
|
+
const output = deps.output ?? process.stdout;
|
|
238
|
+
const parsed = parseArgs(args, env);
|
|
239
|
+
if (!parsed) {
|
|
240
|
+
writeUsage(output);
|
|
241
|
+
return 1;
|
|
242
|
+
}
|
|
243
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
244
|
+
const now = deps.now?.() ?? Date.now();
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const source = await readDbRecord({
|
|
248
|
+
fetchImpl,
|
|
249
|
+
serverUrl: parsed.serverUrl,
|
|
250
|
+
authToken: parsed.authToken,
|
|
251
|
+
dbKey: parsed.sourceAgentKey,
|
|
252
|
+
});
|
|
253
|
+
const target = buildTargetAgent({ parsed, source, now });
|
|
254
|
+
await writeDbRecord({
|
|
255
|
+
fetchImpl,
|
|
256
|
+
serverUrl: parsed.serverUrl,
|
|
257
|
+
authToken: parsed.authToken,
|
|
258
|
+
userId: parsed.userId,
|
|
259
|
+
dbKey: parsed.targetAgentKey,
|
|
260
|
+
data: target,
|
|
261
|
+
});
|
|
262
|
+
const publicKey = `agent-pub-${parsed.targetAgentId}`;
|
|
263
|
+
if (parsed.publicAgent) {
|
|
264
|
+
await writeDbRecord({
|
|
265
|
+
fetchImpl,
|
|
266
|
+
serverUrl: parsed.serverUrl,
|
|
267
|
+
authToken: parsed.authToken,
|
|
268
|
+
userId: parsed.userId,
|
|
269
|
+
dbKey: publicKey,
|
|
270
|
+
data: target,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
await attachAgentToSpace({
|
|
274
|
+
fetchImpl,
|
|
275
|
+
parsed,
|
|
276
|
+
contentKey: parsed.publicAgent ? publicKey : parsed.targetAgentKey,
|
|
277
|
+
title: parsed.name,
|
|
278
|
+
now,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const result = {
|
|
282
|
+
serverUrl: parsed.serverUrl,
|
|
283
|
+
sourceAgentKey: parsed.sourceAgentKey,
|
|
284
|
+
agentKey: parsed.targetAgentKey,
|
|
285
|
+
agentUrl: `${parsed.serverUrl}/${parsed.targetAgentKey}`,
|
|
286
|
+
publicKey: parsed.publicAgent ? publicKey : null,
|
|
287
|
+
spaceId: parsed.spaceId,
|
|
288
|
+
model: target.model,
|
|
289
|
+
provider: target.provider,
|
|
290
|
+
customProviderUrl: target.customProviderUrl,
|
|
291
|
+
tools: target.tools,
|
|
292
|
+
};
|
|
293
|
+
if (parsed.json) {
|
|
294
|
+
output.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
295
|
+
} else {
|
|
296
|
+
output.write(
|
|
297
|
+
[
|
|
298
|
+
`created/updated: ${result.agentKey}`,
|
|
299
|
+
`url: ${result.agentUrl}`,
|
|
300
|
+
`space: ${result.spaceId}`,
|
|
301
|
+
`model: ${result.model}`,
|
|
302
|
+
`tools: ${result.tools.join(", ")}`,
|
|
303
|
+
"",
|
|
304
|
+
].join("\n")
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return 0;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
output.write(
|
|
310
|
+
`setup-offline-marxists failed: ${error instanceof Error ? error.message : String(error)}\n`
|
|
311
|
+
);
|
|
312
|
+
return 1;
|
|
313
|
+
}
|
|
314
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nolo-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first terminal workspace for Nolo",
|
|
6
6
|
"bin": {
|
|
@@ -11,17 +11,35 @@
|
|
|
11
11
|
"index.ts",
|
|
12
12
|
"agentRuntimeLocal.ts",
|
|
13
13
|
"agentAliases.ts",
|
|
14
|
+
"agentCommandSupport.ts",
|
|
15
|
+
"agentListCommands.ts",
|
|
14
16
|
"localRuntimeDb.ts",
|
|
15
17
|
"agentRuntimeCommands.ts",
|
|
18
|
+
"agentListHelpers.ts",
|
|
19
|
+
"agentMachineCommands.ts",
|
|
16
20
|
"agentPullCommand.ts",
|
|
21
|
+
"agentRecordCommands.ts",
|
|
22
|
+
"agentRecordHelpers.ts",
|
|
17
23
|
"agentRunCommand.ts",
|
|
18
24
|
"authCommands.ts",
|
|
25
|
+
"cliCommandAdapters.ts",
|
|
26
|
+
"cliCommandFactories.ts",
|
|
27
|
+
"cliCommandDispatch.ts",
|
|
28
|
+
"cliCommandTypes.ts",
|
|
29
|
+
"cliEnvHelpers.ts",
|
|
19
30
|
"commandRegistry.ts",
|
|
20
31
|
"connectorRunArtifact.ts",
|
|
21
32
|
"connectorWebSocketTarget.ts",
|
|
22
33
|
"defaultServer.ts",
|
|
34
|
+
"agentInternalCommandEntries.ts",
|
|
35
|
+
"internalCommandEntries.ts",
|
|
23
36
|
"machineCommands.ts",
|
|
37
|
+
"offlineMarxistsAgentCommand.ts",
|
|
38
|
+
"runtimeDoctorCommands.ts",
|
|
39
|
+
"scriptCommandEntries.ts",
|
|
40
|
+
"systemInternalCommandEntries.ts",
|
|
24
41
|
"taskRunCommand.ts",
|
|
42
|
+
"workflowInternalCommandEntries.ts",
|
|
25
43
|
"runtimeModeArgs.ts",
|
|
26
44
|
"tableCommands.ts",
|
|
27
45
|
"updateCommands.ts",
|
|
@@ -32,8 +50,9 @@
|
|
|
32
50
|
"connector-experimental/**/*.ts",
|
|
33
51
|
"agent-runtime/**/*.ts",
|
|
34
52
|
"database/**/*.ts",
|
|
53
|
+
"ai/**/*.ts",
|
|
35
54
|
"connector-experimental/**/*.ts",
|
|
36
|
-
"
|
|
55
|
+
"render/**/*.ts"
|
|
37
56
|
],
|
|
38
57
|
"publishConfig": {
|
|
39
58
|
"access": "public"
|
|
@@ -47,6 +66,7 @@
|
|
|
47
66
|
"dependencies": {
|
|
48
67
|
"date-fns-tz": "^2.0.0",
|
|
49
68
|
"level": "^10.0.0",
|
|
69
|
+
"js-yaml": "^4.1.0",
|
|
50
70
|
"ulid": "^2.3.0"
|
|
51
71
|
}
|
|
52
72
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { AgentRuntimeOptions } from "ai/agent/types";
|
|
3
|
+
import type { CanvasEvent } from "./types";
|
|
4
|
+
|
|
5
|
+
export type CanvasEditSelection = {
|
|
6
|
+
sourceMessageId?: string;
|
|
7
|
+
selectedNodeId: string;
|
|
8
|
+
part: string;
|
|
9
|
+
path: string[];
|
|
10
|
+
type: string;
|
|
11
|
+
props: Record<string, unknown>;
|
|
12
|
+
style: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const CANVAS_EDIT_SELECTION_EVENT = "nolo:canvas-edit-selection";
|
|
16
|
+
|
|
17
|
+
let currentSelection: CanvasEditSelection | null = null;
|
|
18
|
+
let pendingSelection: CanvasEditSelection | null = null;
|
|
19
|
+
const listeners = new Set<(selection: CanvasEditSelection | null) => void>();
|
|
20
|
+
const patchListeners = new Map<string, Set<(events: CanvasEvent[]) => void>>();
|
|
21
|
+
const queuedPatches = new Map<string, CanvasEvent[][]>();
|
|
22
|
+
|
|
23
|
+
export function publishCanvasEditSelection(
|
|
24
|
+
selection: CanvasEditSelection | null
|
|
25
|
+
) {
|
|
26
|
+
currentSelection = selection;
|
|
27
|
+
listeners.forEach((listener) => listener(selection));
|
|
28
|
+
|
|
29
|
+
if (typeof window !== "undefined") {
|
|
30
|
+
window.dispatchEvent(
|
|
31
|
+
new CustomEvent(CANVAS_EDIT_SELECTION_EVENT, { detail: selection })
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function subscribeCanvasEditSelection(
|
|
37
|
+
listener: (selection: CanvasEditSelection | null) => void
|
|
38
|
+
) {
|
|
39
|
+
listeners.add(listener);
|
|
40
|
+
listener(currentSelection);
|
|
41
|
+
return () => {
|
|
42
|
+
listeners.delete(listener);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useCanvasEditSelection() {
|
|
47
|
+
const [selection, setSelection] =
|
|
48
|
+
React.useState<CanvasEditSelection | null>(currentSelection);
|
|
49
|
+
|
|
50
|
+
React.useEffect(
|
|
51
|
+
() => subscribeCanvasEditSelection(setSelection),
|
|
52
|
+
[]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return selection;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function markPendingCanvasEditSelection(
|
|
59
|
+
selection: CanvasEditSelection | null
|
|
60
|
+
) {
|
|
61
|
+
pendingSelection = selection;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function consumePendingCanvasEditSelection() {
|
|
65
|
+
const selection = pendingSelection;
|
|
66
|
+
pendingSelection = null;
|
|
67
|
+
return selection;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function publishCanvasMessagePatch(
|
|
71
|
+
sourceMessageId: string,
|
|
72
|
+
events: CanvasEvent[]
|
|
73
|
+
) {
|
|
74
|
+
if (!events.length) return;
|
|
75
|
+
|
|
76
|
+
const queued = queuedPatches.get(sourceMessageId) ?? [];
|
|
77
|
+
queued.push(events);
|
|
78
|
+
queuedPatches.set(sourceMessageId, queued);
|
|
79
|
+
|
|
80
|
+
patchListeners
|
|
81
|
+
.get(sourceMessageId)
|
|
82
|
+
?.forEach((listener) => listener(events));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function subscribeCanvasMessagePatches(
|
|
86
|
+
sourceMessageId: string,
|
|
87
|
+
listener: (events: CanvasEvent[]) => void
|
|
88
|
+
) {
|
|
89
|
+
const listenersForMessage = patchListeners.get(sourceMessageId) ?? new Set();
|
|
90
|
+
listenersForMessage.add(listener);
|
|
91
|
+
patchListeners.set(sourceMessageId, listenersForMessage);
|
|
92
|
+
|
|
93
|
+
queuedPatches
|
|
94
|
+
.get(sourceMessageId)
|
|
95
|
+
?.forEach((events) => listener(events));
|
|
96
|
+
|
|
97
|
+
return () => {
|
|
98
|
+
listenersForMessage.delete(listener);
|
|
99
|
+
if (!listenersForMessage.size) {
|
|
100
|
+
patchListeners.delete(sourceMessageId);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function buildCanvasNodeEditingTarget(
|
|
106
|
+
selection: CanvasEditSelection
|
|
107
|
+
): NonNullable<AgentRuntimeOptions["editingTarget"]> {
|
|
108
|
+
return {
|
|
109
|
+
kind: "canvas_node",
|
|
110
|
+
key: selection.selectedNodeId,
|
|
111
|
+
title: selection.part,
|
|
112
|
+
summary: [
|
|
113
|
+
"用户正在编辑 Canvas Tree 画布中的一个已选中节点。",
|
|
114
|
+
"只输出针对选中节点的 canvas_snapshot updateNode 事件,除非用户明确要求新增内容。",
|
|
115
|
+
"不要重建整棵树,不要输出 Markdown、解释、React、HTML、CSS 或 JS 源码。",
|
|
116
|
+
].join("\n"),
|
|
117
|
+
metadata: {
|
|
118
|
+
sourceMessageId: selection.sourceMessageId,
|
|
119
|
+
selectedNodeId: selection.selectedNodeId,
|
|
120
|
+
part: selection.part,
|
|
121
|
+
path: selection.path,
|
|
122
|
+
type: selection.type,
|
|
123
|
+
props: selection.props,
|
|
124
|
+
style: selection.style,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type CanvasRuntimeAction =
|
|
2
|
+
| {
|
|
3
|
+
type: "toggle";
|
|
4
|
+
key: string;
|
|
5
|
+
}
|
|
6
|
+
| {
|
|
7
|
+
type: "setState";
|
|
8
|
+
key: string;
|
|
9
|
+
value: unknown;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
type: "scrollTo";
|
|
13
|
+
targetId: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CanvasRuntimeState = Record<string, unknown>;
|
|
17
|
+
|
|
18
|
+
export function parseCanvasRuntimeAction(action: unknown): CanvasRuntimeAction | null {
|
|
19
|
+
if (!action || typeof action !== "object") return null;
|
|
20
|
+
const candidate = action as Record<string, unknown>;
|
|
21
|
+
const type = candidate.type;
|
|
22
|
+
|
|
23
|
+
if (type === "toggle" && typeof candidate.key === "string" && candidate.key.trim()) {
|
|
24
|
+
return { type, key: candidate.key };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (type === "setState" && typeof candidate.key === "string" && candidate.key.trim()) {
|
|
28
|
+
return { type, key: candidate.key, value: candidate.value };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type === "scrollTo" && typeof candidate.targetId === "string" && candidate.targetId.trim()) {
|
|
32
|
+
return { type, targetId: candidate.targetId };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function reduceCanvasRuntimeAction(
|
|
39
|
+
state: CanvasRuntimeState,
|
|
40
|
+
action: CanvasRuntimeAction
|
|
41
|
+
): CanvasRuntimeState {
|
|
42
|
+
if (action.type === "toggle") {
|
|
43
|
+
return {
|
|
44
|
+
...state,
|
|
45
|
+
[action.key]: !state[action.key],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (action.type === "setState") {
|
|
50
|
+
return {
|
|
51
|
+
...state,
|
|
52
|
+
[action.key]: action.value,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return state;
|
|
57
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyCanvasEvent,
|
|
3
|
+
createCanvasDocument,
|
|
4
|
+
normalizeCanvasEvent,
|
|
5
|
+
} from "./canvasTree";
|
|
6
|
+
import type { CanvasDocument, CanvasEvent } from "./types";
|
|
7
|
+
|
|
8
|
+
export type ParsedCanvasSnapshotMessage = {
|
|
9
|
+
document: CanvasDocument;
|
|
10
|
+
events: CanvasEvent[];
|
|
11
|
+
eventCount: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function hasCanvasSnapshotSignal(content: string): boolean {
|
|
15
|
+
return content.includes("canvas_snapshot");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function extractCanvasSnapshotText(content: unknown): string | null {
|
|
19
|
+
if (typeof content === "string") {
|
|
20
|
+
return hasCanvasSnapshotSignal(content) ? content : null;
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(content)) return null;
|
|
23
|
+
|
|
24
|
+
const text = content
|
|
25
|
+
.map((item) =>
|
|
26
|
+
item?.type === "text" && typeof item.text === "string" ? item.text : ""
|
|
27
|
+
)
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join("\n");
|
|
30
|
+
|
|
31
|
+
return hasCanvasSnapshotSignal(text) ? text : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isCanvasEvent(value: unknown): value is CanvasEvent {
|
|
35
|
+
if (!value || typeof value !== "object") return false;
|
|
36
|
+
const event = value as { type?: unknown };
|
|
37
|
+
return (
|
|
38
|
+
event.type === "appendNode" ||
|
|
39
|
+
event.type === "updateNode" ||
|
|
40
|
+
event.type === "selectNode"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseCanvasSnapshotMessage(
|
|
45
|
+
content: string
|
|
46
|
+
): ParsedCanvasSnapshotMessage | null {
|
|
47
|
+
if (!hasCanvasSnapshotSignal(content)) return null;
|
|
48
|
+
|
|
49
|
+
let document = createCanvasDocument("root");
|
|
50
|
+
const events: CanvasEvent[] = [];
|
|
51
|
+
let eventCount = 0;
|
|
52
|
+
|
|
53
|
+
for (const line of content.split(/\r?\n/)) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed.startsWith("{") || !trimmed.includes("canvas_snapshot")) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(trimmed);
|
|
61
|
+
if (parsed?.type !== "canvas_snapshot" || !isCanvasEvent(parsed.event)) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const event = normalizeCanvasEvent(parsed.event);
|
|
65
|
+
if (!event) continue;
|
|
66
|
+
document = applyCanvasEvent(document, event);
|
|
67
|
+
events.push(event);
|
|
68
|
+
eventCount += 1;
|
|
69
|
+
} catch {
|
|
70
|
+
// Streaming messages often end with a partial JSON line. Ignore it until
|
|
71
|
+
// the next render provides a complete line.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return eventCount > 0 ? { document, events, eventCount } : null;
|
|
76
|
+
}
|