nolo-cli 0.1.7 → 0.1.9
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 +107 -5
- package/agentRuntimeCommands.ts +464 -0
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +525 -0
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/avatarUtils.ts +24 -0
- package/ai/agent/buildEditingContext.ts +373 -0
- package/ai/agent/buildSystemPrompt.ts +532 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +119 -0
- package/ai/agent/cliExecutor.ts +733 -0
- package/ai/agent/cliPrompt.ts +10 -0
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +234 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
- package/ai/agent/hooks/useAgentConfig.ts +61 -0
- package/ai/agent/hooks/useAgentDialog.ts +35 -0
- package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
- package/ai/agent/hooks/usePublicAgents.ts +473 -0
- package/ai/agent/machineRunPermissions.ts +95 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +238 -0
- package/ai/agent/runAgentClientLoop.ts +138 -0
- package/ai/agent/runtimeGuidance.ts +97 -0
- package/ai/agent/runtimeServerBase.ts +37 -0
- package/ai/agent/server/fetchPublicAgents.ts +128 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +1278 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +33 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1075 -0
- package/ai/chat/accumulateToolCallChunks.ts +95 -0
- package/ai/chat/fetchUtils.native.ts +276 -0
- package/ai/chat/fetchUtils.ts +153 -0
- package/ai/chat/parseApiError.ts +64 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/sseClient.native.ts +91 -0
- package/ai/chat/sseClient.ts +67 -0
- package/ai/chat/streamReader.native.ts +31 -0
- package/ai/chat/streamReader.ts +62 -0
- package/ai/chat/updateTotalUsage.ts +72 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/calculateContextUsage.ts +133 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/index.ts +1 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +50 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +31 -0
- package/ai/llm/getPricing.ts +199 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +40 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/mimo.ts +71 -0
- package/ai/llm/mistral.ts +22 -0
- package/ai/llm/modelAvatar.ts +427 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +269 -0
- package/ai/llm/providers.ts +306 -0
- package/ai/llm/reasoningModels.ts +28 -0
- package/ai/llm/types.ts +59 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/capture.ts +148 -0
- package/ai/memory/consolidate.ts +104 -0
- package/ai/memory/delete.ts +147 -0
- package/ai/memory/overlay.ts +84 -0
- package/ai/memory/query.ts +38 -0
- package/ai/memory/queryShared.ts +160 -0
- package/ai/memory/rank.ts +105 -0
- package/ai/memory/recentRelationshipRecap.ts +249 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/runtime.ts +76 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/messages/type.ts +20 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +440 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillDocProtocol.ts +434 -0
- package/ai/skills/skillReferenceSummary.ts +63 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +544 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +330 -0
- package/ai/token/hooks/useRecords.ts +65 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/modelUsageQuery.ts +252 -0
- package/ai/token/normalizeUsage.ts +84 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +88 -0
- package/ai/token/query.ts +88 -0
- package/ai/token/queryUserTokens.ts +59 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/serverDialogProjection.ts +78 -0
- package/ai/token/serverTokenWriter.ts +143 -0
- package/ai/token/stats.ts +21 -0
- package/ai/token/tokenThunks.ts +24 -0
- package/ai/token/types.ts +93 -0
- package/ai/tools/agent/agentTools.ts +176 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/skillAgentPreset.ts +89 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +256 -0
- package/ai/tools/applyLineEditsTool.ts +312 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +65 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +111 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createPlanTool.ts +999 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/deepseekOcrTool.ts +34 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +64 -0
- package/ai/tools/execBashTool.ts +379 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +309 -0
- package/ai/tools/fetchWebpageTool.ts +84 -0
- package/ai/tools/geminiImagePreviewTool.ts +361 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +1858 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +142 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +218 -0
- package/ai/tools/paddleOcrTool.ts +34 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +84 -0
- package/ai/tools/readFileTool.ts +211 -0
- package/ai/tools/readTool.ts +163 -0
- package/ai/tools/readXPostTool.ts +233 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchRepoTool.ts +115 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +366 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/toolApiClient.ts +198 -0
- package/ai/tools/toolNameAliases.ts +57 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/toolVisibility.ts +4 -0
- package/ai/tools/types.ts +20 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
- package/ai/tools/whisperTool.ts +77 -0
- package/ai/tools/writeFileTool.ts +210 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/client/agentRun.ts +198 -167
- package/client/compactDialog.ts +222 -0
- package/commandRegistry.ts +14 -0
- package/connector-experimental/capabilities.ts +73 -0
- package/connector-experimental/codexBinary.ts +41 -0
- package/connector-experimental/heartbeatLoop.ts +22 -0
- package/connector-experimental/index.ts +5 -0
- package/connector-experimental/machineInfo.ts +46 -0
- package/connector-experimental/protocol.ts +54 -0
- package/connectorWebSocketTarget.ts +29 -0
- package/defaultServer.ts +1 -0
- package/index.ts +158 -104
- package/machineCommands.ts +382 -0
- package/package.json +12 -2
- package/tui/readlineWorkspace.ts +50 -0
- package/tui/session.ts +40 -2
- package/updateCommands.ts +70 -5
package/client/agentRun.ts
CHANGED
|
@@ -1,167 +1,198 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
type EnvLike = Record<string, string | undefined>;
|
|
5
|
-
|
|
6
|
-
type OutputLike = {
|
|
7
|
-
write(chunk: string): unknown;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
type RunAgentTurnOptions = {
|
|
11
|
-
agentName: string;
|
|
12
|
-
agentKey: string;
|
|
13
|
-
serverUrl: string;
|
|
14
|
-
message: string;
|
|
15
|
-
continueDialogId?: string;
|
|
16
|
-
scriptDir: string;
|
|
17
|
-
env: EnvLike;
|
|
18
|
-
output: OutputLike;
|
|
19
|
-
scriptPathExists?: (path: string) => boolean;
|
|
20
|
-
fetchImpl?: typeof fetch;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type RunAgentTurnResult = {
|
|
24
|
-
exitCode: number;
|
|
25
|
-
dialogId?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
type ScriptBridgeDecision = {
|
|
29
|
-
hasAuthToken: boolean;
|
|
30
|
-
scriptPathExists: boolean;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export function shouldUseScriptBridge(decision: ScriptBridgeDecision) {
|
|
34
|
-
return !decision.hasAuthToken && decision.scriptPathExists;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function resolveAuthToken(env: EnvLike) {
|
|
38
|
-
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function shouldShowUsage(env: EnvLike) {
|
|
42
|
-
return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function formatUsage(usage: any, dialogId: unknown) {
|
|
46
|
-
const parts: string[] = [];
|
|
47
|
-
if (typeof dialogId === "string" && dialogId) parts.push(`dialog=${dialogId}`);
|
|
48
|
-
|
|
49
|
-
const input = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
|
|
50
|
-
const output = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
|
|
51
|
-
if (input || output) parts.push(`tokens=${input}+${output}`);
|
|
52
|
-
|
|
53
|
-
return parts.length ? ` (${parts.join(" ")})` : "";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
type EnvLike = Record<string, string | undefined>;
|
|
5
|
+
|
|
6
|
+
type OutputLike = {
|
|
7
|
+
write(chunk: string): unknown;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type RunAgentTurnOptions = {
|
|
11
|
+
agentName: string;
|
|
12
|
+
agentKey: string;
|
|
13
|
+
serverUrl: string;
|
|
14
|
+
message: string;
|
|
15
|
+
continueDialogId?: string;
|
|
16
|
+
scriptDir: string;
|
|
17
|
+
env: EnvLike;
|
|
18
|
+
output: OutputLike;
|
|
19
|
+
scriptPathExists?: (path: string) => boolean;
|
|
20
|
+
fetchImpl?: typeof fetch;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type RunAgentTurnResult = {
|
|
24
|
+
exitCode: number;
|
|
25
|
+
dialogId?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type ScriptBridgeDecision = {
|
|
29
|
+
hasAuthToken: boolean;
|
|
30
|
+
scriptPathExists: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function shouldUseScriptBridge(decision: ScriptBridgeDecision) {
|
|
34
|
+
return !decision.hasAuthToken && decision.scriptPathExists;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveAuthToken(env: EnvLike) {
|
|
38
|
+
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function shouldShowUsage(env: EnvLike) {
|
|
42
|
+
return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function formatUsage(usage: any, dialogId: unknown) {
|
|
46
|
+
const parts: string[] = [];
|
|
47
|
+
if (typeof dialogId === "string" && dialogId) parts.push(`dialog=${dialogId}`);
|
|
48
|
+
|
|
49
|
+
const input = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
|
|
50
|
+
const output = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
|
|
51
|
+
if (input || output) parts.push(`tokens=${input}+${output}`);
|
|
52
|
+
|
|
53
|
+
return parts.length ? ` (${parts.join(" ")})` : "";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildTransportErrorHint(serverUrl: string, error: unknown) {
|
|
57
|
+
const endpoint = `${serverUrl}/api/agent/run`;
|
|
58
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
59
|
+
|
|
60
|
+
let detail =
|
|
61
|
+
`[nolo] Could not reach ${endpoint}.\n` +
|
|
62
|
+
`Reason: ${reason}\n`;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const parsed = new URL(serverUrl);
|
|
66
|
+
if (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost") {
|
|
67
|
+
detail +=
|
|
68
|
+
"If you meant local dev, start the local API first.\n" +
|
|
69
|
+
"Otherwise set NOLO_SERVER to a reachable server, or re-run `nolo login --server https://nolo.chat`.\n";
|
|
70
|
+
return detail;
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Keep the generic hint below when serverUrl is not a valid absolute URL.
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
detail +=
|
|
77
|
+
"Check NOLO_SERVER / BASE_URL and make sure the configured server is reachable.\n";
|
|
78
|
+
return detail;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function runScriptBridge(options: RunAgentTurnOptions, scriptPath: string) {
|
|
82
|
+
options.output.write(`\n${options.agentName} -> working via local script...\n\n`);
|
|
83
|
+
const proc = Bun.spawn({
|
|
84
|
+
cmd: [
|
|
85
|
+
process.execPath,
|
|
86
|
+
scriptPath,
|
|
87
|
+
"--agent",
|
|
88
|
+
options.agentKey,
|
|
89
|
+
"--server",
|
|
90
|
+
options.serverUrl,
|
|
91
|
+
"--msg",
|
|
92
|
+
options.message,
|
|
93
|
+
"--no-default-test-root",
|
|
94
|
+
...(options.continueDialogId
|
|
95
|
+
? ["--continue", options.continueDialogId]
|
|
96
|
+
: []),
|
|
97
|
+
],
|
|
98
|
+
stdin: "inherit",
|
|
99
|
+
stdout: "inherit",
|
|
100
|
+
stderr: "inherit",
|
|
101
|
+
env: {
|
|
102
|
+
...process.env,
|
|
103
|
+
...options.env,
|
|
104
|
+
ASSISTANT_LABEL: options.agentName,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const exitCode = await proc.exited;
|
|
109
|
+
if (exitCode !== 0) {
|
|
110
|
+
options.output.write(`\n[nolo] Agent run exited with code ${exitCode}.\n`);
|
|
111
|
+
}
|
|
112
|
+
return { exitCode };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
|
|
116
|
+
options.output.write(`\n${options.agentName} -> working...\n`);
|
|
117
|
+
|
|
118
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
119
|
+
let res: Response;
|
|
120
|
+
try {
|
|
121
|
+
res = await fetchImpl(`${options.serverUrl}/api/agent/run`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${authToken}`,
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
agentKey: options.agentKey,
|
|
129
|
+
userInput: options.message,
|
|
130
|
+
runtimeContext: {
|
|
131
|
+
surface: "cli",
|
|
132
|
+
host: "terminal",
|
|
133
|
+
runtime: "bun",
|
|
134
|
+
entrypoint: "nolo-cli",
|
|
135
|
+
capabilities: ["text-io", "slash-commands"],
|
|
136
|
+
},
|
|
137
|
+
...(options.continueDialogId
|
|
138
|
+
? { continueDialogId: options.continueDialogId }
|
|
139
|
+
: {}),
|
|
140
|
+
stream: false,
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
options.output.write(buildTransportErrorHint(options.serverUrl, error));
|
|
145
|
+
return { exitCode: 1 };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let data: any = {};
|
|
149
|
+
try {
|
|
150
|
+
data = await res.json();
|
|
151
|
+
} catch {
|
|
152
|
+
data = {};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
|
|
157
|
+
if (data?.error || data?.message) {
|
|
158
|
+
options.output.write(`${data.error || data.message}\n`);
|
|
159
|
+
}
|
|
160
|
+
return { exitCode: 1 };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const content = String(data?.content ?? data?.message ?? "").trim();
|
|
164
|
+
if (content) {
|
|
165
|
+
options.output.write(`\n${options.agentName} > ${content}\n`);
|
|
166
|
+
} else {
|
|
167
|
+
options.output.write(`\n${options.agentName} > (no text response)\n`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const usage = formatUsage(data?.usage, data?.dialogId);
|
|
171
|
+
if (usage && shouldShowUsage(options.env)) options.output.write(`${usage}\n`);
|
|
172
|
+
return {
|
|
173
|
+
exitCode: 0,
|
|
174
|
+
...(typeof data?.dialogId === "string" && data.dialogId
|
|
175
|
+
? { dialogId: data.dialogId }
|
|
176
|
+
: {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function runAgentTurn(options: RunAgentTurnOptions) {
|
|
181
|
+
const authToken = resolveAuthToken(options.env);
|
|
182
|
+
const scriptPath = join(options.scriptDir, "chatWithAgent.ts");
|
|
183
|
+
const scriptPathExists = (options.scriptPathExists ?? existsSync)(scriptPath);
|
|
184
|
+
|
|
185
|
+
if (shouldUseScriptBridge({ hasAuthToken: Boolean(authToken), scriptPathExists })) {
|
|
186
|
+
return runScriptBridge(options, scriptPath);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!authToken) {
|
|
190
|
+
options.output.write(
|
|
191
|
+
"[nolo] This install needs an auth token before it can talk to agents.\n" +
|
|
192
|
+
"Set AUTH_TOKEN, or use the repo-local CLI where the dev script bridge is available.\n"
|
|
193
|
+
);
|
|
194
|
+
return { exitCode: 1 };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return runHttpAgentTurn(options, authToken);
|
|
198
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// packages/cli/client/compactDialog.ts
|
|
2
|
+
// HTTP-only compact helper for CLI TUI (no Redux store available).
|
|
3
|
+
|
|
4
|
+
import { ulid } from "ulid";
|
|
5
|
+
|
|
6
|
+
const DB_PATH = "/api/v1/db";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extract userId from a JWT-style auth token without verifying the signature.
|
|
10
|
+
* Mirrors the logic of `parseToken` in `auth/token.ts` without the crypto imports.
|
|
11
|
+
* @internal - exported for testing only
|
|
12
|
+
*/
|
|
13
|
+
export function parseTokenUserId(token: string): string | null {
|
|
14
|
+
try {
|
|
15
|
+
const parts = token.split(".");
|
|
16
|
+
if (parts.length < 2) return null;
|
|
17
|
+
const payloadBase64 = parts[1];
|
|
18
|
+
const payload = JSON.parse(
|
|
19
|
+
Buffer.from(payloadBase64, "base64").toString("utf8")
|
|
20
|
+
);
|
|
21
|
+
return typeof payload?.userId === "string" ? payload.userId : null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract the custom ID (ULID) from a dialog key like `dialog-{userId}-{id}`.
|
|
29
|
+
* Mirrors `extractCustomId` from `core/prefix` without importing it.
|
|
30
|
+
*/
|
|
31
|
+
function extractCustomId(key: string): string {
|
|
32
|
+
const parts = key.split("-");
|
|
33
|
+
return parts.slice(2).join("-");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function readDialogRecord(
|
|
37
|
+
fetchImpl: typeof fetch,
|
|
38
|
+
serverUrl: string,
|
|
39
|
+
authToken: string,
|
|
40
|
+
dialogKey: string
|
|
41
|
+
): Promise<Record<string, unknown>> {
|
|
42
|
+
const res = await fetchImpl(`${serverUrl}${DB_PATH}/read/${dialogKey}`, {
|
|
43
|
+
headers: { Authorization: `Bearer ${authToken}` },
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
throw new Error(`Failed to read dialog "${dialogKey}": HTTP ${res.status}`);
|
|
47
|
+
}
|
|
48
|
+
const data = await res.json();
|
|
49
|
+
return data as Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fields carried forward from the source dialog into the fork.
|
|
54
|
+
* Conversation summary/compression state is intentionally excluded so the new
|
|
55
|
+
* dialog starts clean: summary, summarizedBeforeId, proactiveSummary,
|
|
56
|
+
* proactiveSummaryBeforeId, compressionCount, summaryPending must NOT be
|
|
57
|
+
* inherited — they describe the old conversation, not the fork.
|
|
58
|
+
*/
|
|
59
|
+
const FORKED_CARRY_FIELDS = [
|
|
60
|
+
"cybots",
|
|
61
|
+
"type",
|
|
62
|
+
"title",
|
|
63
|
+
"spaceId",
|
|
64
|
+
"category",
|
|
65
|
+
"referenceKeys",
|
|
66
|
+
"triggerType",
|
|
67
|
+
"schedule",
|
|
68
|
+
"taskPrompt",
|
|
69
|
+
"executionMode",
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
function buildForkedDialogRecord(
|
|
73
|
+
current: Record<string, unknown>,
|
|
74
|
+
userId: string
|
|
75
|
+
): Record<string, unknown> & { dbKey: string; id: string } {
|
|
76
|
+
const newId = ulid();
|
|
77
|
+
const dbKey = `dialog-${userId}-${newId}`;
|
|
78
|
+
const now = new Date().toISOString();
|
|
79
|
+
|
|
80
|
+
// Explicitly pick only the allowed fields — never spread `current` wholesale.
|
|
81
|
+
const carried: Record<string, unknown> = {};
|
|
82
|
+
for (const field of FORKED_CARRY_FIELDS) {
|
|
83
|
+
if (current[field] !== undefined) {
|
|
84
|
+
carried[field] = current[field];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
...carried,
|
|
90
|
+
id: newId,
|
|
91
|
+
dbKey,
|
|
92
|
+
inheritedFromDialogKey: current.dbKey,
|
|
93
|
+
inheritedFromDialogTitle: current.title,
|
|
94
|
+
createdAt: now,
|
|
95
|
+
updatedAt: now,
|
|
96
|
+
// reset per-dialog stats
|
|
97
|
+
inputTokens: 0,
|
|
98
|
+
outputTokens: 0,
|
|
99
|
+
totalCost: 0,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function writeDialogRecord(
|
|
104
|
+
fetchImpl: typeof fetch,
|
|
105
|
+
serverUrl: string,
|
|
106
|
+
authToken: string,
|
|
107
|
+
record: Record<string, unknown> & { dbKey: string }
|
|
108
|
+
): Promise<void> {
|
|
109
|
+
const res = await fetchImpl(`${serverUrl}${DB_PATH}/write/`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
Authorization: `Bearer ${authToken}`,
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({ data: record, customKey: record.dbKey }),
|
|
116
|
+
});
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Failed to write forked dialog "${record.dbKey}": HTTP ${res.status}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Best-effort: register the forked dialog in the space sidebar.
|
|
126
|
+
* Failure here is non-fatal since the dialog is already stored.
|
|
127
|
+
*/
|
|
128
|
+
async function addDialogToSpaceIfNeeded(
|
|
129
|
+
fetchImpl: typeof fetch,
|
|
130
|
+
serverUrl: string,
|
|
131
|
+
authToken: string,
|
|
132
|
+
record: Record<string, unknown> & { dbKey: string }
|
|
133
|
+
): Promise<void> {
|
|
134
|
+
const rawSpaceId = record.spaceId;
|
|
135
|
+
if (!rawSpaceId || typeof rawSpaceId !== "string") return;
|
|
136
|
+
|
|
137
|
+
const normalizedSpaceId = rawSpaceId.startsWith("space-")
|
|
138
|
+
? rawSpaceId.slice("space-".length)
|
|
139
|
+
: rawSpaceId;
|
|
140
|
+
const spaceKey = `space-${normalizedSpaceId}`;
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
|
|
143
|
+
const contentEntry = {
|
|
144
|
+
title: typeof record.title === "string" ? record.title : record.id,
|
|
145
|
+
type: "dialog",
|
|
146
|
+
contentKey: record.dbKey,
|
|
147
|
+
pinned: false,
|
|
148
|
+
createdAt: now,
|
|
149
|
+
updatedAt: now,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const res = await fetchImpl(`${serverUrl}${DB_PATH}/patch/${spaceKey}`, {
|
|
154
|
+
method: "PATCH",
|
|
155
|
+
headers: {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
Authorization: `Bearer ${authToken}`,
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify({ contents: { [record.dbKey]: contentEntry } }),
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok) {
|
|
162
|
+
console.warn(
|
|
163
|
+
`[nolo] compact: addDialogToSpace failed for ${spaceKey}: HTTP ${res.status}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.warn(`[nolo] compact: addDialogToSpace error: ${error}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export type CompactDialogResult = {
|
|
172
|
+
dialogId: string;
|
|
173
|
+
dialogKey: string;
|
|
174
|
+
spaceId?: string;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Compact the current dialog by forking it:
|
|
179
|
+
* 1. Read the current dialog config from the server.
|
|
180
|
+
* 2. Build a new dialog record that inherits from the old one.
|
|
181
|
+
* 3. Write the new record to the server.
|
|
182
|
+
* 4. Register the new dialog in the space sidebar (best-effort).
|
|
183
|
+
*
|
|
184
|
+
* Returns the new dialog's ID so the TUI can switch to it.
|
|
185
|
+
*/
|
|
186
|
+
export async function compactDialog(options: {
|
|
187
|
+
serverUrl: string;
|
|
188
|
+
authToken: string;
|
|
189
|
+
dialogId: string;
|
|
190
|
+
fetchImpl?: typeof fetch;
|
|
191
|
+
}): Promise<CompactDialogResult> {
|
|
192
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
193
|
+
|
|
194
|
+
const userId = parseTokenUserId(options.authToken);
|
|
195
|
+
if (!userId) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
"[nolo] compact: cannot compact — invalid or missing auth token"
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const dialogKey = `dialog-${userId}-${options.dialogId}`;
|
|
202
|
+
const current = await readDialogRecord(
|
|
203
|
+
fetchImpl,
|
|
204
|
+
options.serverUrl,
|
|
205
|
+
options.authToken,
|
|
206
|
+
dialogKey
|
|
207
|
+
);
|
|
208
|
+
const next = buildForkedDialogRecord(current, userId);
|
|
209
|
+
await writeDialogRecord(fetchImpl, options.serverUrl, options.authToken, next);
|
|
210
|
+
await addDialogToSpaceIfNeeded(
|
|
211
|
+
fetchImpl,
|
|
212
|
+
options.serverUrl,
|
|
213
|
+
options.authToken,
|
|
214
|
+
next
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
dialogId: extractCustomId(next.dbKey),
|
|
219
|
+
dialogKey: next.dbKey,
|
|
220
|
+
spaceId: typeof next.spaceId === "string" ? next.spaceId : undefined,
|
|
221
|
+
};
|
|
222
|
+
}
|
package/commandRegistry.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const COMMANDS: CommandEntry[] = [
|
|
|
19
19
|
|
|
20
20
|
{ path: ["dialog", "read"], script: "readDialog.ts", description: "Read a dialog" },
|
|
21
21
|
{ path: ["space", "read"], script: "readSpace.ts", description: "Read a space" },
|
|
22
|
+
{ path: ["space", "delete"], script: "deleteSpaces.ts", description: "Delete spaces by safe filters" },
|
|
22
23
|
{ path: ["space", "category"], script: "upsertSpaceCategory.ts", description: "Create or update a space category" },
|
|
23
24
|
{ path: ["space", "content-category"], script: "setSpaceContentCategory.ts", description: "Move content into a space category" },
|
|
24
25
|
|
|
@@ -28,6 +29,9 @@ export const COMMANDS: CommandEntry[] = [
|
|
|
28
29
|
{ path: ["agent", "list"], script: "listMyAgents.ts", description: "List owned agents" },
|
|
29
30
|
{ path: ["agent", "read"], script: "readAgent.ts", description: "Read a single agent" },
|
|
30
31
|
{ path: ["agent", "update"], script: "updateAgent.ts", description: "Update agent fields" },
|
|
32
|
+
{ path: ["agent", "bind-current"], script: "", description: "Bind an agent to this machine" },
|
|
33
|
+
{ path: ["agent", "smoke-current"], script: "", description: "Smoke test a bound agent through this machine" },
|
|
34
|
+
{ path: ["agent", "runtime-doctor"], script: "", description: "Diagnose current machine runtime compatibility" },
|
|
31
35
|
{ path: ["agent", "unpublish"], script: "unpublishAgent.ts", description: "Remove an agent's public record" },
|
|
32
36
|
{ path: ["agent", "chat"], script: "chatWithAgent.ts", description: "Chat with an agent" },
|
|
33
37
|
{ path: ["agent", "dialogs"], script: "queryAgentDialogs.ts", description: "Inspect recent dialogs for an agent" },
|
|
@@ -70,13 +74,23 @@ export function renderHelpText() {
|
|
|
70
74
|
" nolo chat",
|
|
71
75
|
" nolo login",
|
|
72
76
|
" nolo whoami",
|
|
77
|
+
" nolo connect",
|
|
78
|
+
" nolo connect --watch",
|
|
79
|
+
" nolo connect --ws",
|
|
80
|
+
" nolo connect --daemon",
|
|
81
|
+
" nolo daemon --server-url https://api.nolo.chat --api-key sk_machine_xxx",
|
|
82
|
+
" nolo machine status",
|
|
73
83
|
" nolo doctor",
|
|
74
84
|
" nolo update",
|
|
75
85
|
' nolo doc create --title "Trip Notes" --body "hello"',
|
|
76
86
|
' nolo skill-doc create --title "Agent Query Skill" --description "Inspect recent agent dialogs"',
|
|
77
87
|
" nolo agent list --json",
|
|
78
88
|
" nolo agent read agent-pub-01APPBUILDER00000001YAII3I",
|
|
89
|
+
" nolo agent bind-current agent-user-1-agent-1",
|
|
90
|
+
" nolo agent runtime-doctor agent-user-1-agent-1",
|
|
91
|
+
' nolo agent smoke-current agent-user-1-agent-1 --msg "ping"',
|
|
79
92
|
' nolo chat --agent agent-pub-01APPBUILDER00000001YAII3I --msg "你好"',
|
|
93
|
+
" nolo space delete --name-prefix rn_owner_verify_0504 --yes",
|
|
80
94
|
" nolo table data --table 01ABCXYZ --action query",
|
|
81
95
|
" nolo llama status",
|
|
82
96
|
];
|