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
|
@@ -23,6 +23,7 @@ const PROVIDER_ENDPOINTS: Record<string, string> = {
|
|
|
23
23
|
export type PlatformChatProviderConfig = {
|
|
24
24
|
serverUrl: string;
|
|
25
25
|
authToken: string;
|
|
26
|
+
agentKey: string;
|
|
26
27
|
model: string;
|
|
27
28
|
provider: string;
|
|
28
29
|
endpoint: string;
|
|
@@ -93,6 +94,7 @@ export function resolvePlatformChatProviderConfig(args: {
|
|
|
93
94
|
return {
|
|
94
95
|
serverUrl: resolvePlatformServerUrl(args.env),
|
|
95
96
|
authToken: resolvePlatformAuthToken(args.env),
|
|
97
|
+
agentKey: args.agentConfig.key,
|
|
96
98
|
model: args.agentConfig.model || "gpt-4.1-mini",
|
|
97
99
|
provider,
|
|
98
100
|
endpoint: resolveProviderEndpoint(args.agentConfig),
|
|
@@ -118,6 +120,7 @@ export function buildPlatformChatCompletionRequest(args: {
|
|
|
118
120
|
...(shouldDisableThinking(args.providerConfig) ? { thinking: { type: "disabled" } } : {}),
|
|
119
121
|
url: args.providerConfig.endpoint,
|
|
120
122
|
provider: args.providerConfig.provider,
|
|
123
|
+
agentKey: args.providerConfig.agentKey,
|
|
121
124
|
...(args.providerConfig.apiSource ? { apiSource: args.providerConfig.apiSource } : {}),
|
|
122
125
|
...(args.providerConfig.apiKey ? { KEY: args.providerConfig.apiKey } : {}),
|
|
123
126
|
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { AgentRuntimeAgentConfig } from "./hostAdapter";
|
|
2
|
+
import type { AgentRuntimeHost, AgentRuntimeToolPolicy } from "./types";
|
|
3
|
+
|
|
4
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
5
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function stringArray(value: unknown): string[] {
|
|
9
|
+
if (!Array.isArray(value)) return [];
|
|
10
|
+
return value.filter((item): item is string => typeof item === "string" && !!item.trim());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function unique(values: string[]) {
|
|
14
|
+
return [...new Set(values)];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function mergeRecords<T extends Record<string, unknown>>(
|
|
18
|
+
base: T | undefined,
|
|
19
|
+
override: T | undefined,
|
|
20
|
+
): T | undefined {
|
|
21
|
+
if (!base && !override) return undefined;
|
|
22
|
+
return {
|
|
23
|
+
...(base ?? {}),
|
|
24
|
+
...(override ?? {}),
|
|
25
|
+
} as T;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function normalizeAgentRuntimeToolPolicy(
|
|
29
|
+
value: unknown,
|
|
30
|
+
): AgentRuntimeToolPolicy | undefined {
|
|
31
|
+
if (!isRecord(value)) return undefined;
|
|
32
|
+
return {
|
|
33
|
+
version: 1,
|
|
34
|
+
...(stringArray(value.agentTools).length
|
|
35
|
+
? { agentTools: unique(stringArray(value.agentTools)) }
|
|
36
|
+
: {}),
|
|
37
|
+
...(stringArray(value.runtimeTools).length
|
|
38
|
+
? { runtimeTools: unique(stringArray(value.runtimeTools)) }
|
|
39
|
+
: {}),
|
|
40
|
+
...(isRecord(value.workspace) ? { workspace: { ...value.workspace } } : {}),
|
|
41
|
+
...(isRecord(value.shell) ? { shell: { ...value.shell } } : {}),
|
|
42
|
+
...(isRecord(value.isolation) ? { isolation: { ...value.isolation } } : {}),
|
|
43
|
+
...(isRecord(value.git) ? { git: { ...value.git } } : {}),
|
|
44
|
+
...(isRecord(value.budget) ? { budget: { ...value.budget } } : {}),
|
|
45
|
+
...(isRecord(value.audit) ? { audit: { ...value.audit } } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function mergeAgentRuntimeToolPolicies(
|
|
50
|
+
base?: AgentRuntimeToolPolicy,
|
|
51
|
+
override?: AgentRuntimeToolPolicy,
|
|
52
|
+
): AgentRuntimeToolPolicy | undefined {
|
|
53
|
+
if (!base && !override) return undefined;
|
|
54
|
+
return {
|
|
55
|
+
version: 1,
|
|
56
|
+
agentTools: unique([
|
|
57
|
+
...(base?.agentTools ?? []),
|
|
58
|
+
...(override?.agentTools ?? []),
|
|
59
|
+
]),
|
|
60
|
+
runtimeTools: unique([
|
|
61
|
+
...(base?.runtimeTools ?? []),
|
|
62
|
+
...(override?.runtimeTools ?? []),
|
|
63
|
+
]),
|
|
64
|
+
workspace: {
|
|
65
|
+
...(base?.workspace ?? {}),
|
|
66
|
+
...(override?.workspace ?? {}),
|
|
67
|
+
writableRoots: unique([
|
|
68
|
+
...(base?.workspace?.writableRoots ?? []),
|
|
69
|
+
...(override?.workspace?.writableRoots ?? []),
|
|
70
|
+
]),
|
|
71
|
+
},
|
|
72
|
+
shell: mergeRecords(base?.shell, override?.shell),
|
|
73
|
+
isolation: mergeRecords(base?.isolation, override?.isolation),
|
|
74
|
+
git: mergeRecords(base?.git, override?.git),
|
|
75
|
+
budget: mergeRecords(base?.budget, override?.budget),
|
|
76
|
+
audit: mergeRecords(base?.audit, override?.audit),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function policyRequestsHostedWorkspace(policy: AgentRuntimeToolPolicy | undefined) {
|
|
81
|
+
if (!policy) return false;
|
|
82
|
+
const workspaceMode = policy.workspace?.mode;
|
|
83
|
+
return (
|
|
84
|
+
policy.shell?.enabled === true ||
|
|
85
|
+
policy.runtimeTools?.some((tool) => tool === "execShell") ||
|
|
86
|
+
workspaceMode === "lease" ||
|
|
87
|
+
workspaceMode === "task-worktree"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function applyWebRuntimeSafetyBaseline(
|
|
92
|
+
policy: AgentRuntimeToolPolicy | undefined,
|
|
93
|
+
): AgentRuntimeToolPolicy | undefined {
|
|
94
|
+
if (!policyRequestsHostedWorkspace(policy)) return policy;
|
|
95
|
+
return mergeAgentRuntimeToolPolicies(
|
|
96
|
+
policy,
|
|
97
|
+
{
|
|
98
|
+
version: 1,
|
|
99
|
+
shell: {
|
|
100
|
+
commandPolicy: "approval",
|
|
101
|
+
networkPolicy: "default-deny",
|
|
102
|
+
},
|
|
103
|
+
isolation: {
|
|
104
|
+
mode: "os-sandbox",
|
|
105
|
+
},
|
|
106
|
+
audit: {
|
|
107
|
+
logToolCalls: true,
|
|
108
|
+
logShellCommands: true,
|
|
109
|
+
writeToDialog: true,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function resolveEffectiveRuntimeToolPolicy(args: {
|
|
116
|
+
agentConfig: Pick<AgentRuntimeAgentConfig, "runtimeToolPolicy" | "runtimeBinding">;
|
|
117
|
+
taskRunControl?: { runtimeToolPolicy?: unknown } | null;
|
|
118
|
+
host?: AgentRuntimeHost;
|
|
119
|
+
}): AgentRuntimeToolPolicy | undefined {
|
|
120
|
+
const agentPolicy = normalizeAgentRuntimeToolPolicy(
|
|
121
|
+
args.agentConfig.runtimeToolPolicy ??
|
|
122
|
+
(isRecord(args.agentConfig.runtimeBinding)
|
|
123
|
+
? args.agentConfig.runtimeBinding.runtimeToolPolicy
|
|
124
|
+
: undefined),
|
|
125
|
+
);
|
|
126
|
+
const taskPolicy = normalizeAgentRuntimeToolPolicy(
|
|
127
|
+
args.taskRunControl?.runtimeToolPolicy,
|
|
128
|
+
);
|
|
129
|
+
const effectivePolicy = mergeAgentRuntimeToolPolicies(agentPolicy, taskPolicy);
|
|
130
|
+
return args.host === "web"
|
|
131
|
+
? applyWebRuntimeSafetyBaseline(effectivePolicy)
|
|
132
|
+
: effectivePolicy;
|
|
133
|
+
}
|
package/agent-runtime/types.ts
CHANGED
|
@@ -81,6 +81,53 @@ export type AgentRuntimeDecisionInput = {
|
|
|
81
81
|
serverFallbackAvailable: boolean;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
+
export type AgentRuntimeWorkspaceMode = "none" | "current" | "task-worktree" | "lease";
|
|
85
|
+
|
|
86
|
+
export type AgentRuntimeShellPolicy = {
|
|
87
|
+
enabled?: boolean;
|
|
88
|
+
mode?: "off" | "worktree";
|
|
89
|
+
commandPolicy?: "denylist" | "allowlist" | "approval";
|
|
90
|
+
networkPolicy?: "default-deny" | "allowed" | "approval";
|
|
91
|
+
maxSeconds?: number;
|
|
92
|
+
maxOutputBytes?: number;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type AgentRuntimeGitPolicy = {
|
|
96
|
+
canCommit?: boolean;
|
|
97
|
+
canPushAlpha?: boolean;
|
|
98
|
+
canMergeMain?: boolean;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export type AgentRuntimeAuditPolicy = {
|
|
102
|
+
logToolCalls?: boolean;
|
|
103
|
+
logShellCommands?: boolean;
|
|
104
|
+
writeToDialog?: boolean;
|
|
105
|
+
writeToTask?: boolean;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type AgentRuntimeIsolationPolicy = {
|
|
109
|
+
mode?: "none" | "os-sandbox" | "container" | "gvisor" | "microvm" | "dedicated-vm";
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export type AgentRuntimeToolPolicy = {
|
|
113
|
+
version?: 1;
|
|
114
|
+
agentTools?: string[];
|
|
115
|
+
runtimeTools?: string[];
|
|
116
|
+
workspace?: {
|
|
117
|
+
mode?: AgentRuntimeWorkspaceMode;
|
|
118
|
+
writableRoots?: string[];
|
|
119
|
+
cwd?: string;
|
|
120
|
+
};
|
|
121
|
+
shell?: AgentRuntimeShellPolicy;
|
|
122
|
+
isolation?: AgentRuntimeIsolationPolicy;
|
|
123
|
+
git?: AgentRuntimeGitPolicy;
|
|
124
|
+
budget?: {
|
|
125
|
+
dailyUsdLimit?: number;
|
|
126
|
+
maxRunSeconds?: number;
|
|
127
|
+
};
|
|
128
|
+
audit?: AgentRuntimeAuditPolicy;
|
|
129
|
+
};
|
|
130
|
+
|
|
84
131
|
export type AgentRuntimeDecision = {
|
|
85
132
|
mode: AgentRuntimeMode;
|
|
86
133
|
runnable: boolean;
|
package/agentAliases.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const NOLO_FULLSTACK_AGENT_KEY =
|
|
|
13
13
|
export const NOLO_SENIOR_FULLSTACK_AGENT_ID = "01NOLOFULLSTK00000000T3XR4";
|
|
14
14
|
export const NOLO_SENIOR_FULLSTACK_AGENT_KEY =
|
|
15
15
|
`agent-${PLATFORM_DEMO_USER_ID}-${NOLO_SENIOR_FULLSTACK_AGENT_ID}`;
|
|
16
|
-
export const NOLO_REVIEW_AGENT_ID = "
|
|
16
|
+
export const NOLO_REVIEW_AGENT_ID = "01CODEXCLIMAIN00000001OK";
|
|
17
17
|
export const NOLO_REVIEW_AGENT_KEY =
|
|
18
18
|
`agent-${PLATFORM_DEMO_USER_ID}-${NOLO_REVIEW_AGENT_ID}`;
|
|
19
19
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { MachineHeartbeat } from "./connector-experimental/protocol";
|
|
2
|
+
import type { CliKvDb } from "./client/hybridRecordStore";
|
|
3
|
+
import type { EnvLike } from "./cliEnvHelpers";
|
|
4
|
+
|
|
5
|
+
export type OutputLike = { write(chunk: string): unknown };
|
|
6
|
+
|
|
7
|
+
export type SmokeWebSocketOptions = {
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
onMessage: (message: string) => void | Promise<void>;
|
|
10
|
+
onOpen: () => void | Promise<void>;
|
|
11
|
+
sentMessages: string[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type LocalCliExecutor = (
|
|
15
|
+
provider: string,
|
|
16
|
+
prompt: string,
|
|
17
|
+
options: { model?: string; timeout?: number; cwd?: string; yolo?: boolean }
|
|
18
|
+
) => Promise<{ text: string; raw?: string; elapsed?: number }>;
|
|
19
|
+
|
|
20
|
+
export type LocalRuntimeProbeResult = {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
dbPath: string;
|
|
23
|
+
agentFound: boolean;
|
|
24
|
+
agentKey?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type AgentCommandDeps = {
|
|
29
|
+
env?: EnvLike;
|
|
30
|
+
output?: OutputLike;
|
|
31
|
+
db?: CliKvDb;
|
|
32
|
+
fetchImpl?: typeof fetch;
|
|
33
|
+
fallbackFetchImpl?: typeof fetch;
|
|
34
|
+
machineInfo?: () => MachineHeartbeat;
|
|
35
|
+
connectWebSocket?: (url: string, options: SmokeWebSocketOptions) => Promise<void>;
|
|
36
|
+
executeCli?: LocalCliExecutor;
|
|
37
|
+
connectorRunMessageHandler?: (
|
|
38
|
+
machine: MachineHeartbeat,
|
|
39
|
+
message: string,
|
|
40
|
+
pushMessage: (response: string) => void,
|
|
41
|
+
executeCli: LocalCliExecutor,
|
|
42
|
+
env: EnvLike,
|
|
43
|
+
fetchImpl: typeof fetch
|
|
44
|
+
) => Promise<void>;
|
|
45
|
+
localRuntimeProbe?: (env: EnvLike) => Promise<LocalRuntimeProbeResult>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
async function getDefaultCliDb() {
|
|
49
|
+
const { getDefaultCliLocalRuntimeDb } = await import("./localRuntimeDb");
|
|
50
|
+
return getDefaultCliLocalRuntimeDb();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createNullCliDb(): CliKvDb {
|
|
54
|
+
return {
|
|
55
|
+
get: async () => {
|
|
56
|
+
throw new Error("local cache unavailable");
|
|
57
|
+
},
|
|
58
|
+
put: async () => undefined,
|
|
59
|
+
batch: async () => undefined,
|
|
60
|
+
iterator: () => {
|
|
61
|
+
throw new Error("local cache unavailable");
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function getReadableCliDb(output: OutputLike) {
|
|
67
|
+
try {
|
|
68
|
+
return await getDefaultCliDb();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
output.write(
|
|
71
|
+
`[nolo] local agent cache unavailable; falling back to remote reads only: ${
|
|
72
|
+
error instanceof Error ? error.message : String(error)
|
|
73
|
+
}\n`
|
|
74
|
+
);
|
|
75
|
+
return createNullCliDb();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { runAgentListCommand } from "./agentListCommands";
|
|
2
|
+
import {
|
|
3
|
+
runAgentBindCurrentCommand,
|
|
4
|
+
runAgentRuntimeDoctorCommand,
|
|
5
|
+
runAgentSmokeCurrentCommand,
|
|
6
|
+
} from "./agentMachineCommands";
|
|
7
|
+
import { runAgentPullCommand } from "./agentPullCommand";
|
|
8
|
+
import {
|
|
9
|
+
runAgentReadCommand,
|
|
10
|
+
runAgentUpdateCommand,
|
|
11
|
+
} from "./agentRecordCommands";
|
|
12
|
+
import { runAgentRunCommand } from "./agentRunCommand";
|
|
13
|
+
import {
|
|
14
|
+
createEnvCommand,
|
|
15
|
+
createEnvScriptDirCommand,
|
|
16
|
+
} from "./cliCommandFactories";
|
|
17
|
+
import type { CommandEntry } from "./cliCommandTypes";
|
|
18
|
+
import { runSetupOfflineMarxistsAgentCommand } from "./offlineMarxistsAgentCommand";
|
|
19
|
+
|
|
20
|
+
export function getAgentInternalCommandEntries(): CommandEntry[] {
|
|
21
|
+
return [
|
|
22
|
+
createEnvScriptDirCommand(["chat"], "Chat with an agent", runAgentRunCommand),
|
|
23
|
+
createEnvCommand(["agent", "list"], "List owned agents", runAgentListCommand),
|
|
24
|
+
createEnvCommand(["agent", "pull"], "Cache an agent for local runs", runAgentPullCommand),
|
|
25
|
+
createEnvCommand(["agent", "read"], "Read a single agent", runAgentReadCommand),
|
|
26
|
+
createEnvScriptDirCommand(["agent", "run"], "Run an agent", runAgentRunCommand),
|
|
27
|
+
createEnvCommand(["agent", "setup-offline-marxists"], "Create or update the Marxists.org offline book agent", runSetupOfflineMarxistsAgentCommand),
|
|
28
|
+
createEnvCommand(["agent", "update"], "Update agent fields", runAgentUpdateCommand),
|
|
29
|
+
createEnvCommand(["agent", "bind-current"], "Bind an agent to this machine", runAgentBindCurrentCommand),
|
|
30
|
+
createEnvCommand(["agent", "smoke-current"], "Smoke test a bound agent through this machine", runAgentSmokeCurrentCommand),
|
|
31
|
+
createEnvCommand(["agent", "runtime-doctor"], "Diagnose current machine runtime compatibility", runAgentRuntimeDoctorCommand),
|
|
32
|
+
createEnvScriptDirCommand(["agent", "chat"], "Chat with an agent", runAgentRunCommand),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { getReadableCliDb, type AgentCommandDeps } from "./agentCommandSupport";
|
|
2
|
+
import {
|
|
3
|
+
listLocalCachedAgents,
|
|
4
|
+
listRemoteAgents,
|
|
5
|
+
parseAgentListArgs,
|
|
6
|
+
type ListedAgent,
|
|
7
|
+
} from "./agentListHelpers";
|
|
8
|
+
import {
|
|
9
|
+
queryUserRecords,
|
|
10
|
+
readDbRecord,
|
|
11
|
+
} from "./agentRecordHelpers";
|
|
12
|
+
import {
|
|
13
|
+
parseUserIdFromAuthToken,
|
|
14
|
+
resolveAuthToken,
|
|
15
|
+
resolveServerUrl,
|
|
16
|
+
} from "./cliEnvHelpers";
|
|
17
|
+
|
|
18
|
+
export async function runAgentListCommand(
|
|
19
|
+
args: string[],
|
|
20
|
+
deps: AgentCommandDeps = {}
|
|
21
|
+
) {
|
|
22
|
+
const env = deps.env ?? process.env;
|
|
23
|
+
const output = deps.output ?? process.stdout;
|
|
24
|
+
const { wantJson, publicOnly, idsOnly, includeLegacy } = parseAgentListArgs(args);
|
|
25
|
+
|
|
26
|
+
const authToken = resolveAuthToken(env);
|
|
27
|
+
if (!authToken) {
|
|
28
|
+
output.write("[nolo] agent list requires an auth token. Run `nolo login` or set AUTH_TOKEN.\n");
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const userId = parseUserIdFromAuthToken(authToken);
|
|
33
|
+
if (!userId) {
|
|
34
|
+
output.write("[nolo] agent list could not read userId from AUTH_TOKEN.\n");
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const db = deps.db ?? await getReadableCliDb(output);
|
|
39
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
40
|
+
const fallbackFetchImpl = deps.fallbackFetchImpl;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
let agents: ListedAgent[];
|
|
44
|
+
let source: "local-cache" | "remote-cache";
|
|
45
|
+
try {
|
|
46
|
+
agents = await listLocalCachedAgents({ db, userId });
|
|
47
|
+
source = "local-cache";
|
|
48
|
+
} catch {
|
|
49
|
+
const serverUrl = resolveServerUrl(env);
|
|
50
|
+
agents = await listRemoteAgents({
|
|
51
|
+
authToken,
|
|
52
|
+
fallbackFetchImpl,
|
|
53
|
+
fetchImpl,
|
|
54
|
+
includeLegacy,
|
|
55
|
+
serverUrl,
|
|
56
|
+
userId,
|
|
57
|
+
queryUserRecords,
|
|
58
|
+
readDbRecord,
|
|
59
|
+
});
|
|
60
|
+
source = "remote-cache";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!includeLegacy) {
|
|
64
|
+
agents = agents.filter((agent) => !agent.privateKey.startsWith("cybot-"));
|
|
65
|
+
}
|
|
66
|
+
if (publicOnly) {
|
|
67
|
+
agents = agents.filter((agent) => agent.publicRecordExists);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (idsOnly) {
|
|
71
|
+
output.write(`${agents.map((agent) => agent.id).join("\n")}\n`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (wantJson) {
|
|
76
|
+
output.write(JSON.stringify({
|
|
77
|
+
userId,
|
|
78
|
+
total: agents.length,
|
|
79
|
+
publicCount: agents.filter((agent) => agent.publicRecordExists).length,
|
|
80
|
+
source,
|
|
81
|
+
agents,
|
|
82
|
+
}, null, 2));
|
|
83
|
+
output.write("\n");
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
output.write(`userId: ${userId}\n`);
|
|
88
|
+
output.write(`total agents: ${agents.length}\n`);
|
|
89
|
+
output.write(`public agents: ${agents.filter((agent) => agent.publicRecordExists).length}\n`);
|
|
90
|
+
output.write(`source: ${source}\n`);
|
|
91
|
+
if (agents.length === 0) {
|
|
92
|
+
output.write("\n(no agents found)\n");
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
for (const agent of agents) {
|
|
96
|
+
const status = agent.publicRecordExists ? "public" : "private";
|
|
97
|
+
const flagMismatch = agent.isPublicFlag !== agent.publicRecordExists
|
|
98
|
+
? ` flag=${agent.isPublicFlag}`
|
|
99
|
+
: "";
|
|
100
|
+
output.write(
|
|
101
|
+
[
|
|
102
|
+
`\n[${status}] ${agent.name}`,
|
|
103
|
+
`id=${agent.id}`,
|
|
104
|
+
`type=${agent.type ?? "-"}`,
|
|
105
|
+
`model=${agent.model}`,
|
|
106
|
+
`updatedAt=${agent.updatedAt ?? "-"}`,
|
|
107
|
+
`privateKey=${agent.privateKey}`,
|
|
108
|
+
`publicKey=${agent.publicKey}${flagMismatch}`,
|
|
109
|
+
`tools=${agent.tools.join(", ") || "-"}`,
|
|
110
|
+
].join("\n")
|
|
111
|
+
);
|
|
112
|
+
output.write("\n");
|
|
113
|
+
}
|
|
114
|
+
return 0;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
output.write(
|
|
117
|
+
`[nolo] agent list failed: ${
|
|
118
|
+
error instanceof Error ? error.message : String(error)
|
|
119
|
+
}\n`
|
|
120
|
+
);
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { CliKvDb } from "./client/hybridRecordStore";
|
|
2
|
+
|
|
3
|
+
export type ListedAgent = {
|
|
4
|
+
id: string;
|
|
5
|
+
privateKey: string;
|
|
6
|
+
publicKey: string;
|
|
7
|
+
name: string;
|
|
8
|
+
model: string;
|
|
9
|
+
updatedAt: string | number | null;
|
|
10
|
+
isPublicFlag: boolean;
|
|
11
|
+
publicRecordExists: boolean;
|
|
12
|
+
type: string | null;
|
|
13
|
+
tools: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function parseAgentRecordId(privateKey: string, explicitId?: string) {
|
|
17
|
+
if (explicitId) return explicitId;
|
|
18
|
+
const agentMatch = privateKey.match(/^agent-[^-]+-(.+)$/i);
|
|
19
|
+
if (agentMatch?.[1]) return agentMatch[1];
|
|
20
|
+
const cybotMatch = privateKey.match(/^cybot-[^-]+-([0-9A-HJKMNP-TV-Z]{26})$/i);
|
|
21
|
+
return cybotMatch?.[1] ?? "";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildCompanionKeys(privateKey: string, rawId: string, userId: string) {
|
|
25
|
+
if (privateKey.startsWith("cybot-")) {
|
|
26
|
+
return {
|
|
27
|
+
privateKey: `cybot-${userId}-${rawId}`,
|
|
28
|
+
publicKey: `cybot-pub-${rawId}`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
privateKey: `agent-${userId}-${rawId}`,
|
|
33
|
+
publicKey: `agent-pub-${rawId}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function normalizeListedAgent(record: any): ListedAgent | null {
|
|
38
|
+
const privateKey = typeof record?.dbKey === "string" ? record.dbKey : "";
|
|
39
|
+
const explicitId = typeof record?.id === "string" && record.id ? record.id : undefined;
|
|
40
|
+
const ownerUserId = typeof record?.userId === "string" ? record.userId : "";
|
|
41
|
+
const rawId = explicitId
|
|
42
|
+
|| (ownerUserId && privateKey.startsWith(`agent-${ownerUserId}-`)
|
|
43
|
+
? privateKey.slice(`agent-${ownerUserId}-`.length)
|
|
44
|
+
: ownerUserId && privateKey.startsWith(`cybot-${ownerUserId}-`)
|
|
45
|
+
? privateKey.slice(`cybot-${ownerUserId}-`.length)
|
|
46
|
+
: parseAgentRecordId(privateKey, explicitId));
|
|
47
|
+
if (!privateKey || !rawId || !ownerUserId) return null;
|
|
48
|
+
const keys = buildCompanionKeys(privateKey, rawId, ownerUserId);
|
|
49
|
+
return {
|
|
50
|
+
id: rawId,
|
|
51
|
+
privateKey,
|
|
52
|
+
publicKey: keys.publicKey,
|
|
53
|
+
name: typeof record?.name === "string" && record.name ? record.name : "(unnamed)",
|
|
54
|
+
model: typeof record?.model === "string" && record.model ? record.model : "-",
|
|
55
|
+
updatedAt:
|
|
56
|
+
typeof record?.updatedAt === "string" || typeof record?.updatedAt === "number"
|
|
57
|
+
? record.updatedAt
|
|
58
|
+
: typeof record?.createdAt === "string" || typeof record?.createdAt === "number"
|
|
59
|
+
? record.createdAt
|
|
60
|
+
: typeof record?.created === "string" || typeof record?.created === "number"
|
|
61
|
+
? record.created
|
|
62
|
+
: null,
|
|
63
|
+
isPublicFlag: !!record?.isPublic,
|
|
64
|
+
publicRecordExists: false,
|
|
65
|
+
type: typeof record?.type === "string" ? record.type : null,
|
|
66
|
+
tools: Array.isArray(record?.tools)
|
|
67
|
+
? record.tools.filter((tool: unknown): tool is string => typeof tool === "string")
|
|
68
|
+
: [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function sortListedAgents(agents: ListedAgent[]) {
|
|
73
|
+
agents.sort((a, b) => {
|
|
74
|
+
const ta = a.updatedAt == null ? 0 : new Date(a.updatedAt).getTime();
|
|
75
|
+
const tb = b.updatedAt == null ? 0 : new Date(b.updatedAt).getTime();
|
|
76
|
+
return tb - ta;
|
|
77
|
+
});
|
|
78
|
+
return agents;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function parseAgentListArgs(args: string[]) {
|
|
82
|
+
return {
|
|
83
|
+
wantJson: args.includes("--json"),
|
|
84
|
+
publicOnly: args.includes("--public-only"),
|
|
85
|
+
idsOnly: args.includes("--ids-only"),
|
|
86
|
+
includeLegacy: args.includes("--include-legacy"),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function listLocalCachedAgents(args: {
|
|
91
|
+
db: CliKvDb;
|
|
92
|
+
userId: string;
|
|
93
|
+
}) {
|
|
94
|
+
const privateRecords = new Map<string, any>();
|
|
95
|
+
const publicKeys = new Set<string>();
|
|
96
|
+
for await (const [key, value] of args.db.iterator()) {
|
|
97
|
+
if (typeof key !== "string" || !value || typeof value !== "object") continue;
|
|
98
|
+
if (key.startsWith(`agent-${args.userId}-`) || key.startsWith(`cybot-${args.userId}-`)) {
|
|
99
|
+
privateRecords.set(key, { ...(value as Record<string, unknown>), dbKey: key });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (key.startsWith("agent-pub-") || key.startsWith("cybot-pub-")) {
|
|
103
|
+
publicKeys.add(key);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const agents = sortListedAgents(
|
|
108
|
+
[...privateRecords.values()]
|
|
109
|
+
.map((record) => normalizeListedAgent(record))
|
|
110
|
+
.filter((agent): agent is ListedAgent => agent != null)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
for (const agent of agents) {
|
|
114
|
+
agent.publicRecordExists = publicKeys.has(agent.publicKey);
|
|
115
|
+
}
|
|
116
|
+
return agents;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function hasReadableRecord(args: {
|
|
120
|
+
authToken: string;
|
|
121
|
+
dbKey: string;
|
|
122
|
+
fallbackFetchImpl?: typeof fetch;
|
|
123
|
+
fetchImpl: typeof fetch;
|
|
124
|
+
serverUrl: string;
|
|
125
|
+
readDbRecord: (args: {
|
|
126
|
+
authToken: string;
|
|
127
|
+
dbKey: string;
|
|
128
|
+
fallbackFetchImpl?: typeof fetch;
|
|
129
|
+
fetchImpl: typeof fetch;
|
|
130
|
+
serverUrl: string;
|
|
131
|
+
}) => Promise<any>;
|
|
132
|
+
}) {
|
|
133
|
+
try {
|
|
134
|
+
await args.readDbRecord(args);
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function listRemoteAgents(args: {
|
|
142
|
+
authToken: string;
|
|
143
|
+
fallbackFetchImpl?: typeof fetch;
|
|
144
|
+
fetchImpl: typeof fetch;
|
|
145
|
+
includeLegacy: boolean;
|
|
146
|
+
serverUrl: string;
|
|
147
|
+
userId: string;
|
|
148
|
+
queryUserRecords: (args: {
|
|
149
|
+
authToken: string;
|
|
150
|
+
fallbackFetchImpl?: typeof fetch;
|
|
151
|
+
fetchImpl: typeof fetch;
|
|
152
|
+
serverUrl: string;
|
|
153
|
+
userId: string;
|
|
154
|
+
type: "agent" | "cybot";
|
|
155
|
+
}) => Promise<any[]>;
|
|
156
|
+
readDbRecord: (args: {
|
|
157
|
+
authToken: string;
|
|
158
|
+
dbKey: string;
|
|
159
|
+
fallbackFetchImpl?: typeof fetch;
|
|
160
|
+
fetchImpl: typeof fetch;
|
|
161
|
+
serverUrl: string;
|
|
162
|
+
}) => Promise<any>;
|
|
163
|
+
}) {
|
|
164
|
+
const recordGroups = [await args.queryUserRecords({ ...args, type: "agent" as const })];
|
|
165
|
+
if (args.includeLegacy) {
|
|
166
|
+
recordGroups.push(await args.queryUserRecords({ ...args, type: "cybot" as const }));
|
|
167
|
+
}
|
|
168
|
+
const agents = sortListedAgents(
|
|
169
|
+
recordGroups
|
|
170
|
+
.flat()
|
|
171
|
+
.map((record) => normalizeListedAgent(record))
|
|
172
|
+
.filter((agent): agent is ListedAgent => agent != null)
|
|
173
|
+
);
|
|
174
|
+
await Promise.all(
|
|
175
|
+
agents.map(async (agent) => {
|
|
176
|
+
agent.publicRecordExists = await hasReadableRecord({
|
|
177
|
+
authToken: args.authToken,
|
|
178
|
+
dbKey: agent.publicKey,
|
|
179
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
180
|
+
fetchImpl: args.fetchImpl,
|
|
181
|
+
serverUrl: args.serverUrl,
|
|
182
|
+
readDbRecord: args.readDbRecord,
|
|
183
|
+
});
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
return agents;
|
|
187
|
+
}
|