@aiden-ade/sandbox-agent 0.1.19 → 0.1.21
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/dist/core-agent.d.ts +1 -5
- package/dist/core-agent.d.ts.map +1 -1
- package/dist/core-agent.js +12 -101
- package/dist/core-agent.js.map +1 -1
- package/dist/index.cjs +7615 -6994
- package/dist/index.js +42 -94
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +3 -18
- package/dist/sandbox.js.map +1 -1
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +4 -0
- package/dist/updater.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/web-presenter.d.ts +2 -6
- package/dist/web-presenter.d.ts.map +1 -1
- package/dist/web-presenter.js +1 -20
- package/dist/web-presenter.js.map +1 -1
- package/dist/ws-client.d.ts +0 -35
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +0 -1
- package/dist/ws-client.js.map +1 -1
- package/package.json +5 -1
- package/dist/__tests__/cli-executable.test.d.ts +0 -2
- package/dist/__tests__/cli-executable.test.d.ts.map +0 -1
- package/dist/__tests__/cli-executable.test.js +0 -83
- package/dist/__tests__/cli-executable.test.js.map +0 -1
- package/dist/__tests__/cli-help.test.d.ts +0 -2
- package/dist/__tests__/cli-help.test.d.ts.map +0 -1
- package/dist/__tests__/cli-help.test.js +0 -33
- package/dist/__tests__/cli-help.test.js.map +0 -1
- package/dist/__tests__/daemon-socket.integration.test.d.ts +0 -2
- package/dist/__tests__/daemon-socket.integration.test.d.ts.map +0 -1
- package/dist/__tests__/daemon-socket.integration.test.js +0 -152
- package/dist/__tests__/daemon-socket.integration.test.js.map +0 -1
- package/dist/__tests__/daemon.test.d.ts +0 -2
- package/dist/__tests__/daemon.test.d.ts.map +0 -1
- package/dist/__tests__/daemon.test.js +0 -978
- package/dist/__tests__/daemon.test.js.map +0 -1
- package/dist/__tests__/generated-images.test.d.ts +0 -2
- package/dist/__tests__/generated-images.test.d.ts.map +0 -1
- package/dist/__tests__/generated-images.test.js +0 -38
- package/dist/__tests__/generated-images.test.js.map +0 -1
- package/dist/aiden-system-prompt.d.ts +0 -6
- package/dist/aiden-system-prompt.d.ts.map +0 -1
- package/dist/aiden-system-prompt.js +0 -6
- package/dist/aiden-system-prompt.js.map +0 -1
- package/dist/cli-executable.d.ts +0 -65
- package/dist/cli-executable.d.ts.map +0 -1
- package/dist/cli-executable.js +0 -298
- package/dist/cli-executable.js.map +0 -1
- package/dist/cli-help.d.ts +0 -7
- package/dist/cli-help.d.ts.map +0 -1
- package/dist/cli-help.js +0 -45
- package/dist/cli-help.js.map +0 -1
- package/dist/daemon.d.ts +0 -46
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js +0 -836
- package/dist/daemon.js.map +0 -1
- package/dist/generated-images.d.ts +0 -3
- package/dist/generated-images.d.ts.map +0 -1
- package/dist/generated-images.js +0 -123
- package/dist/generated-images.js.map +0 -1
package/dist/daemon.js
DELETED
|
@@ -1,836 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, statfsSync, writeFileSync } from "node:fs";
|
|
2
|
-
import http from "node:http";
|
|
3
|
-
import { randomBytes } from "node:crypto";
|
|
4
|
-
import { arch, cpus, freemem, homedir, hostname, platform, release, totalmem } from "node:os";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
6
|
-
import { io } from "socket.io-client";
|
|
7
|
-
import { collectLocalAgentProviderLimits } from "@aiden/shared/node/provider-limits";
|
|
8
|
-
import { DISCOVERABLE_PROVIDER_KINDS, getDaemonCliEnvironment, resolveBackendRuntimeCommand, resolveCliExecutable, resolveProviderCliCommand, } from "./cli-executable.js";
|
|
9
|
-
import { AGENT_VERSION } from "./version.js";
|
|
10
|
-
import { CoreAgent } from "./core-agent.js";
|
|
11
|
-
import { extractGeneratedImagesFromToolResult } from "./generated-images.js";
|
|
12
|
-
const PRODUCTION_API_URL = "https://api.aiden-platform.com";
|
|
13
|
-
const PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
|
|
14
|
-
const LOCAL_API_URL = "http://localhost:8400";
|
|
15
|
-
const LOCAL_WS_URL = "ws://localhost:8401";
|
|
16
|
-
async function probeLocalDaemonEndpoint(port, token, timeoutMs = 750) {
|
|
17
|
-
try {
|
|
18
|
-
const headers = {};
|
|
19
|
-
if (token)
|
|
20
|
-
headers.authorization = `Bearer ${token}`;
|
|
21
|
-
const response = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
22
|
-
headers,
|
|
23
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
24
|
-
});
|
|
25
|
-
if (response.status === 401)
|
|
26
|
-
return { state: "listening_unauthorized" };
|
|
27
|
-
if (!response.ok)
|
|
28
|
-
return { state: "absent" };
|
|
29
|
-
const body = await response.json();
|
|
30
|
-
if (!body.runtimeId)
|
|
31
|
-
return { state: "absent" };
|
|
32
|
-
return { state: "running", status: body };
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return { state: "absent" };
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
export async function probeLocalDaemon(port, token, timeoutMs = 750) {
|
|
39
|
-
const probe = await probeLocalDaemonEndpoint(port, token, timeoutMs);
|
|
40
|
-
return probe.state === "running" ? probe.status : null;
|
|
41
|
-
}
|
|
42
|
-
export async function isLocalDaemonListening(port, token) {
|
|
43
|
-
const probe = await probeLocalDaemonEndpoint(port, token);
|
|
44
|
-
return probe.state !== "absent";
|
|
45
|
-
}
|
|
46
|
-
function describeOccupiedLocalDaemon(port) {
|
|
47
|
-
return `local daemon API port ${port} is already in use. `
|
|
48
|
-
+ "If Aiden desktop is open, it manages the daemon automatically. "
|
|
49
|
-
+ "Quit Aiden desktop or stop the existing daemon before starting another.";
|
|
50
|
-
}
|
|
51
|
-
function getConfigPath() {
|
|
52
|
-
return process.env.AIDEN_AGENT_CONFIG_PATH ??
|
|
53
|
-
join(homedir(), ".aiden", "agent", "config.json");
|
|
54
|
-
}
|
|
55
|
-
function getEndpointDefaultsPath() {
|
|
56
|
-
return process.env.AIDEN_AGENT_ENDPOINTS_PATH ??
|
|
57
|
-
join(dirname(getConfigPath()), "endpoints.json");
|
|
58
|
-
}
|
|
59
|
-
function readConfig() {
|
|
60
|
-
const configPath = getConfigPath();
|
|
61
|
-
if (!existsSync(configPath))
|
|
62
|
-
return {};
|
|
63
|
-
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
64
|
-
}
|
|
65
|
-
function readEndpointDefaults() {
|
|
66
|
-
const endpointsPath = getEndpointDefaultsPath();
|
|
67
|
-
if (!existsSync(endpointsPath))
|
|
68
|
-
return {};
|
|
69
|
-
try {
|
|
70
|
-
return JSON.parse(readFileSync(endpointsPath, "utf8"));
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return {};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function writeConfig(config) {
|
|
77
|
-
const configPath = getConfigPath();
|
|
78
|
-
mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });
|
|
79
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
80
|
-
}
|
|
81
|
-
function removeConfig() {
|
|
82
|
-
const configPath = getConfigPath();
|
|
83
|
-
if (existsSync(configPath))
|
|
84
|
-
rmSync(configPath, { force: true });
|
|
85
|
-
}
|
|
86
|
-
function argValue(args, name) {
|
|
87
|
-
const index = args.indexOf(name);
|
|
88
|
-
if (index === -1)
|
|
89
|
-
return undefined;
|
|
90
|
-
return args[index + 1];
|
|
91
|
-
}
|
|
92
|
-
function resolveEndpointProfile(args) {
|
|
93
|
-
const profile = argValue(args, "--profile") ?? process.env.AIDEN_AGENT_PROFILE;
|
|
94
|
-
if (!profile)
|
|
95
|
-
return "production";
|
|
96
|
-
if (profile === "production" || profile === "prod")
|
|
97
|
-
return "production";
|
|
98
|
-
if (profile === "local" || profile === "dev")
|
|
99
|
-
return "local";
|
|
100
|
-
throw new Error("Unsupported endpoint profile. Use --profile production or --profile local.");
|
|
101
|
-
}
|
|
102
|
-
function resolveEndpoints(args, stored = {}) {
|
|
103
|
-
const requestedProfile = resolveEndpointProfile(args);
|
|
104
|
-
const profileApiUrl = requestedProfile === "local" ? LOCAL_API_URL : PRODUCTION_API_URL;
|
|
105
|
-
const profileWsUrl = requestedProfile === "local" ? LOCAL_WS_URL : PRODUCTION_WS_URL;
|
|
106
|
-
const endpointDefaults = readEndpointDefaults();
|
|
107
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.AIDEN_API_URL ?? stored.apiUrl ?? endpointDefaults.apiUrl ?? profileApiUrl;
|
|
108
|
-
const wsUrl = argValue(args, "--ws-url") ?? process.env.AIDEN_WS_URL ?? stored.wsUrl ?? endpointDefaults.wsUrl ?? profileWsUrl;
|
|
109
|
-
const endpointProfile = apiUrl === profileApiUrl && wsUrl === profileWsUrl
|
|
110
|
-
? requestedProfile
|
|
111
|
-
: apiUrl === endpointDefaults.apiUrl && wsUrl === endpointDefaults.wsUrl && endpointDefaults.endpointProfile
|
|
112
|
-
? endpointDefaults.endpointProfile
|
|
113
|
-
: "custom";
|
|
114
|
-
return { apiUrl, wsUrl, endpointProfile };
|
|
115
|
-
}
|
|
116
|
-
function isLocalEndpoint(url) {
|
|
117
|
-
return Boolean(url && /^(https?|wss?):\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(url));
|
|
118
|
-
}
|
|
119
|
-
function getLocalApiPort(args, stored) {
|
|
120
|
-
const raw = argValue(args, "--local-api-port") ?? process.env.AIDEN_AGENT_LOCAL_API_PORT;
|
|
121
|
-
if (raw)
|
|
122
|
-
return Number.parseInt(raw, 10);
|
|
123
|
-
const storedPort = stored.localApiPort;
|
|
124
|
-
if (storedPort && storedPort > 0)
|
|
125
|
-
return storedPort;
|
|
126
|
-
return 47831;
|
|
127
|
-
}
|
|
128
|
-
function sleep(ms) {
|
|
129
|
-
if (ms <= 0)
|
|
130
|
-
return Promise.resolve();
|
|
131
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
132
|
-
}
|
|
133
|
-
function normalizeBackendKind(backendKind) {
|
|
134
|
-
if (backendKind === "codex")
|
|
135
|
-
return "codex_app_server";
|
|
136
|
-
return backendKind;
|
|
137
|
-
}
|
|
138
|
-
export function discoverCapabilities() {
|
|
139
|
-
const now = new Date().toISOString();
|
|
140
|
-
const env = getDaemonCliEnvironment();
|
|
141
|
-
return {
|
|
142
|
-
agents: DISCOVERABLE_PROVIDER_KINDS.map((provider) => {
|
|
143
|
-
const executable = resolveProviderCliCommand(provider, env);
|
|
144
|
-
return {
|
|
145
|
-
provider,
|
|
146
|
-
available: Boolean(executable),
|
|
147
|
-
models: [],
|
|
148
|
-
lastCheckedAt: now,
|
|
149
|
-
};
|
|
150
|
-
}),
|
|
151
|
-
hasGit: Boolean(resolveCliExecutable("git", env)),
|
|
152
|
-
hasTerminal: true,
|
|
153
|
-
supportsFilesystem: true,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const USER_INTERRUPTED_RESULT = {
|
|
157
|
-
success: false,
|
|
158
|
-
summary: "Interrupted by user",
|
|
159
|
-
filesModified: [],
|
|
160
|
-
planFilesCreated: [],
|
|
161
|
-
iterations: 0,
|
|
162
|
-
error: "Interrupted by user",
|
|
163
|
-
};
|
|
164
|
-
function abortActiveAgent(entry) {
|
|
165
|
-
entry.presenter.onComplete(USER_INTERRUPTED_RESULT);
|
|
166
|
-
entry.agent.kill();
|
|
167
|
-
}
|
|
168
|
-
function collectRuntimeMetadata() {
|
|
169
|
-
const cpuList = cpus();
|
|
170
|
-
const metadata = {
|
|
171
|
-
platform: platform(),
|
|
172
|
-
arch: arch(),
|
|
173
|
-
osVersion: release(),
|
|
174
|
-
agentVersion: AGENT_VERSION,
|
|
175
|
-
memoryBytes: totalmem(),
|
|
176
|
-
memoryFreeBytes: freemem(),
|
|
177
|
-
};
|
|
178
|
-
if (cpuList.length > 0) {
|
|
179
|
-
metadata.cpuModel = cpuList[0]?.model;
|
|
180
|
-
metadata.cpuCores = cpuList.length;
|
|
181
|
-
}
|
|
182
|
-
try {
|
|
183
|
-
const disk = statfsSync(homedir());
|
|
184
|
-
metadata.diskFreeBytes = disk.bavail * disk.bsize;
|
|
185
|
-
metadata.diskTotalBytes = disk.blocks * disk.bsize;
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
// Disk stats are best-effort and may be unavailable in restricted runtimes.
|
|
189
|
-
}
|
|
190
|
-
return metadata;
|
|
191
|
-
}
|
|
192
|
-
async function collectRuntimeMetadataWithProviderLimits() {
|
|
193
|
-
const metadata = collectRuntimeMetadata();
|
|
194
|
-
try {
|
|
195
|
-
const agentProviderLimits = await collectLocalAgentProviderLimits(getDaemonCliEnvironment());
|
|
196
|
-
if (Object.keys(agentProviderLimits).length > 0) {
|
|
197
|
-
metadata.agentProviderLimits = agentProviderLimits;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
// Best-effort only. Missing or expired provider credentials should not affect runtime presence.
|
|
202
|
-
}
|
|
203
|
-
return metadata;
|
|
204
|
-
}
|
|
205
|
-
class RuntimePresenter {
|
|
206
|
-
socket;
|
|
207
|
-
conversationId;
|
|
208
|
-
runId;
|
|
209
|
-
cwd;
|
|
210
|
-
emittedGeneratedImageKeys = new Set();
|
|
211
|
-
constructor(socket, conversationId, runId, cwd) {
|
|
212
|
-
this.socket = socket;
|
|
213
|
-
this.conversationId = conversationId;
|
|
214
|
-
this.runId = runId;
|
|
215
|
-
this.cwd = cwd;
|
|
216
|
-
}
|
|
217
|
-
emitEvent(event) {
|
|
218
|
-
this.socket.emit("agent.event", {
|
|
219
|
-
conversationId: this.conversationId,
|
|
220
|
-
runId: this.runId,
|
|
221
|
-
event,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
onStart(config) {
|
|
225
|
-
this.emitEvent({ type: "start", task: config.task, ts: Date.now() });
|
|
226
|
-
}
|
|
227
|
-
onLog(message) {
|
|
228
|
-
console.error(message);
|
|
229
|
-
}
|
|
230
|
-
onAssistantText(text) {
|
|
231
|
-
this.emitEvent({ type: "text", text, ts: Date.now() });
|
|
232
|
-
}
|
|
233
|
-
onThinking(text) {
|
|
234
|
-
this.emitEvent({ type: "thinking", text, ts: Date.now() });
|
|
235
|
-
}
|
|
236
|
-
onToolUse(tool, input, toolId) {
|
|
237
|
-
this.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
|
|
238
|
-
}
|
|
239
|
-
onToolResult(toolId, content, isError) {
|
|
240
|
-
this.emitEvent({
|
|
241
|
-
type: "tool_result",
|
|
242
|
-
id: toolId,
|
|
243
|
-
content,
|
|
244
|
-
ts: Date.now(),
|
|
245
|
-
...(isError ? { is_error: true } : {}),
|
|
246
|
-
});
|
|
247
|
-
if (isError)
|
|
248
|
-
return;
|
|
249
|
-
for (const image of extractGeneratedImagesFromToolResult(toolId, content, this.cwd)) {
|
|
250
|
-
this.emitGeneratedImage(image);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
onGeneratedImage(image) {
|
|
254
|
-
this.emitGeneratedImage(image);
|
|
255
|
-
}
|
|
256
|
-
emitGeneratedImage(image) {
|
|
257
|
-
const key = image.sourceHash ?? `${image.sourcePath ?? image.id}:${image.size}`;
|
|
258
|
-
if (this.emittedGeneratedImageKeys.has(key))
|
|
259
|
-
return;
|
|
260
|
-
this.emittedGeneratedImageKeys.add(key);
|
|
261
|
-
this.emitEvent(image);
|
|
262
|
-
}
|
|
263
|
-
onTurnComplete(content) {
|
|
264
|
-
this.emitEvent({ type: "turn_complete", content, ts: Date.now() });
|
|
265
|
-
}
|
|
266
|
-
onUsageUpdate(usage) {
|
|
267
|
-
this.emitEvent({ type: "usage", usage });
|
|
268
|
-
}
|
|
269
|
-
onAskUser(id, questions) {
|
|
270
|
-
this.emitEvent({ type: "ask_user", id, questions });
|
|
271
|
-
}
|
|
272
|
-
onTodoWrite(todos) {
|
|
273
|
-
this.emitEvent({ type: "todo_write", todos });
|
|
274
|
-
}
|
|
275
|
-
onExitPlanMode(id, input, planFilePath) {
|
|
276
|
-
this.emitEvent({ type: "exit_plan_mode", id, input, ...(planFilePath ? { planFilePath } : {}) });
|
|
277
|
-
}
|
|
278
|
-
onCheckpoint(id) {
|
|
279
|
-
this.emitEvent({ type: "checkpoint", id });
|
|
280
|
-
}
|
|
281
|
-
onActivity(status) {
|
|
282
|
-
this.emitEvent({ type: "activity", status, ts: Date.now() });
|
|
283
|
-
}
|
|
284
|
-
onOpenBrowserTab(browserId, url) {
|
|
285
|
-
this.emitEvent({ type: "open_browser_tab", browserId, ...(url ? { url } : {}) });
|
|
286
|
-
}
|
|
287
|
-
recordRawTranscript() {
|
|
288
|
-
// Durable daemon transcripts stay with the underlying CLI runtime.
|
|
289
|
-
}
|
|
290
|
-
onError(message) {
|
|
291
|
-
this.emitEvent({ type: "session_error", error: message });
|
|
292
|
-
}
|
|
293
|
-
onComplete(result) {
|
|
294
|
-
this.emitEvent({ type: "query_complete", result, ts: Date.now() });
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
export async function setupDaemon(args) {
|
|
298
|
-
const { apiUrl, wsUrl, endpointProfile } = resolveEndpoints(args);
|
|
299
|
-
const teamId = argValue(args, "--team") ?? process.env.AIDEN_TEAM_ID;
|
|
300
|
-
const setupToken = argValue(args, "--setup-token") ?? process.env.AIDEN_RUNTIME_SETUP_TOKEN;
|
|
301
|
-
const token = argValue(args, "--token") ?? process.env.AIDEN_SETUP_TOKEN;
|
|
302
|
-
const displayName = argValue(args, "--name") ?? hostname();
|
|
303
|
-
const legacyLocalMachineId = argValue(args, "--legacy-local-machine-id") ?? process.env.AIDEN_LEGACY_LOCAL_MACHINE_ID;
|
|
304
|
-
const scope = argValue(args, "--scope") ?? process.env.AIDEN_RUNTIME_SCOPE ?? "team";
|
|
305
|
-
if (scope !== "team" && scope !== "user") {
|
|
306
|
-
throw new Error("setup --scope must be either 'team' or 'user'");
|
|
307
|
-
}
|
|
308
|
-
if (!setupToken && (!teamId || !token)) {
|
|
309
|
-
throw new Error("setup requires --setup-token <token> or --team <team-id> and --token <aiden PAT>.\n"
|
|
310
|
-
+ "For normal setup, copy the setup token from Aiden and run: aiden-agent setup --setup-token <token>\n"
|
|
311
|
-
+ "For browser authorization, run: aiden-agent login");
|
|
312
|
-
}
|
|
313
|
-
const response = await fetch(`${apiUrl}/public/runtimes${setupToken ? "/register-with-setup-token" : ""}`, {
|
|
314
|
-
method: "POST",
|
|
315
|
-
headers: {
|
|
316
|
-
"content-type": "application/json",
|
|
317
|
-
...(setupToken ? {} : { authorization: `Bearer ${token}` }),
|
|
318
|
-
},
|
|
319
|
-
body: JSON.stringify({
|
|
320
|
-
...(teamId ? { teamId } : {}),
|
|
321
|
-
...(setupToken ? { setupToken } : {}),
|
|
322
|
-
displayName,
|
|
323
|
-
hostname: hostname(),
|
|
324
|
-
...(legacyLocalMachineId ? { legacyLocalMachineId } : {}),
|
|
325
|
-
runtimeKind: "machine",
|
|
326
|
-
managementKind: "user_managed",
|
|
327
|
-
hostKind: "daemon",
|
|
328
|
-
lifecycle: "durable",
|
|
329
|
-
ownerType: scope,
|
|
330
|
-
visibility: scope,
|
|
331
|
-
capabilities: discoverCapabilities(),
|
|
332
|
-
metadata: await collectRuntimeMetadataWithProviderLimits(),
|
|
333
|
-
}),
|
|
334
|
-
});
|
|
335
|
-
if (!response.ok) {
|
|
336
|
-
throw new Error(`runtime registration failed: ${response.status} ${await response.text()}`);
|
|
337
|
-
}
|
|
338
|
-
const body = await response.json();
|
|
339
|
-
writeConfig({
|
|
340
|
-
apiUrl,
|
|
341
|
-
wsUrl,
|
|
342
|
-
endpointProfile,
|
|
343
|
-
teamId,
|
|
344
|
-
runtimeId: body.runtime.id,
|
|
345
|
-
runtimeToken: body.runtimeToken,
|
|
346
|
-
localApiPort: getLocalApiPort(args, {}),
|
|
347
|
-
localApiToken: randomBytes(24).toString("hex"),
|
|
348
|
-
displayName: body.runtime.displayName ?? displayName,
|
|
349
|
-
});
|
|
350
|
-
console.info("[aiden-agent] Runtime registered", { runtimeId: body.runtime.id, configPath: getConfigPath() });
|
|
351
|
-
}
|
|
352
|
-
export async function loginWithDeviceCode(args) {
|
|
353
|
-
const { apiUrl, wsUrl, endpointProfile } = resolveEndpoints(args);
|
|
354
|
-
const displayName = argValue(args, "--name") ?? hostname();
|
|
355
|
-
const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
|
|
356
|
-
const start = await fetch(`${apiUrl}/public/runtimes/device-authorizations`, {
|
|
357
|
-
method: "POST",
|
|
358
|
-
headers: { "content-type": "application/json" },
|
|
359
|
-
body: JSON.stringify({
|
|
360
|
-
displayName,
|
|
361
|
-
hostname: hostname(),
|
|
362
|
-
capabilities: discoverCapabilities(),
|
|
363
|
-
metadata: await collectRuntimeMetadataWithProviderLimits(),
|
|
364
|
-
}),
|
|
365
|
-
});
|
|
366
|
-
if (!start.ok) {
|
|
367
|
-
throw new Error(`device authorization failed: ${start.status} ${await start.text()}`);
|
|
368
|
-
}
|
|
369
|
-
const authorization = await start.json();
|
|
370
|
-
const intervalMs = Math.max(0, (authorization.intervalSeconds ?? 2) * 1000);
|
|
371
|
-
console.info(`[aiden-agent] Open ${authorization.verificationUri}`);
|
|
372
|
-
console.info(`[aiden-agent] Enter code ${authorization.userCode}`);
|
|
373
|
-
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
374
|
-
if (attempt > 0)
|
|
375
|
-
await sleep(intervalMs);
|
|
376
|
-
const token = await fetch(`${apiUrl}/public/runtimes/device-authorizations/token`, {
|
|
377
|
-
method: "POST",
|
|
378
|
-
headers: { "content-type": "application/json" },
|
|
379
|
-
body: JSON.stringify({ deviceCode: authorization.deviceCode }),
|
|
380
|
-
});
|
|
381
|
-
if (token.status === 428)
|
|
382
|
-
continue;
|
|
383
|
-
if (!token.ok) {
|
|
384
|
-
throw new Error(`device authorization token exchange failed: ${token.status} ${await token.text()}`);
|
|
385
|
-
}
|
|
386
|
-
const body = await token.json();
|
|
387
|
-
writeConfig({
|
|
388
|
-
apiUrl,
|
|
389
|
-
wsUrl,
|
|
390
|
-
endpointProfile,
|
|
391
|
-
teamId: body.runtime.teamId ?? undefined,
|
|
392
|
-
runtimeId: body.runtime.id,
|
|
393
|
-
runtimeToken: body.runtimeToken,
|
|
394
|
-
localApiPort: getLocalApiPort(args, {}),
|
|
395
|
-
localApiToken: randomBytes(24).toString("hex"),
|
|
396
|
-
displayName: body.runtime.displayName ?? displayName,
|
|
397
|
-
});
|
|
398
|
-
console.info("[aiden-agent] Runtime registered", { runtimeId: body.runtime.id, configPath: getConfigPath() });
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
throw new Error("device authorization timed out before approval");
|
|
402
|
-
}
|
|
403
|
-
export async function startDaemon(args) {
|
|
404
|
-
const stored = readConfig();
|
|
405
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.AIDEN_RUNTIME_ID ?? stored.runtimeId;
|
|
406
|
-
const runtimeToken = argValue(args, "--token") ?? process.env.AIDEN_RUNTIME_TOKEN ?? stored.runtimeToken;
|
|
407
|
-
const wsUrl = argValue(args, "--ws-url") ?? process.env.AIDEN_WS_URL ?? stored.wsUrl;
|
|
408
|
-
let localApiPort = getLocalApiPort(args, stored);
|
|
409
|
-
const localApiToken = stored.localApiToken ?? randomBytes(24).toString("hex");
|
|
410
|
-
if (!runtimeId || !runtimeToken || !wsUrl) {
|
|
411
|
-
throw new Error("daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url.");
|
|
412
|
-
}
|
|
413
|
-
const activeAgents = new Map();
|
|
414
|
-
const currentCapabilities = () => discoverCapabilities();
|
|
415
|
-
const currentMetadata = () => collectRuntimeMetadataWithProviderLimits();
|
|
416
|
-
const recentLogs = [];
|
|
417
|
-
const pushLog = (message) => {
|
|
418
|
-
recentLogs.push(`${new Date().toISOString()} ${message}`);
|
|
419
|
-
if (recentLogs.length > 200)
|
|
420
|
-
recentLogs.splice(0, recentLogs.length - 200);
|
|
421
|
-
};
|
|
422
|
-
if (stored.localApiPort !== localApiPort || stored.localApiToken !== localApiToken) {
|
|
423
|
-
writeConfig({ ...stored, runtimeId, runtimeToken, wsUrl, localApiPort, localApiToken });
|
|
424
|
-
}
|
|
425
|
-
const socket = io(wsUrl, {
|
|
426
|
-
query: { type: "runtime", runtimeId, hostKind: "daemon", agentVersion: AGENT_VERSION },
|
|
427
|
-
auth: { token: runtimeToken },
|
|
428
|
-
transports: ["websocket"],
|
|
429
|
-
reconnection: true,
|
|
430
|
-
reconnectionAttempts: Infinity,
|
|
431
|
-
reconnectionDelay: 1000,
|
|
432
|
-
reconnectionDelayMax: 5000,
|
|
433
|
-
timeout: 20_000,
|
|
434
|
-
upgrade: false,
|
|
435
|
-
extraHeaders: { "ngrok-skip-browser-warning": "1" },
|
|
436
|
-
});
|
|
437
|
-
const heartbeat = setInterval(() => {
|
|
438
|
-
if (!socket.connected)
|
|
439
|
-
return;
|
|
440
|
-
void currentMetadata().then((metadata) => {
|
|
441
|
-
socket.emit("runtime.heartbeat", { capabilities: currentCapabilities(), metadata });
|
|
442
|
-
});
|
|
443
|
-
}, 15_000);
|
|
444
|
-
socket.on("connect", () => {
|
|
445
|
-
pushLog(`connected runtime=${runtimeId}`);
|
|
446
|
-
console.info("[aiden-agent] Daemon connected", { runtimeId, wsUrl, version: AGENT_VERSION });
|
|
447
|
-
void currentMetadata().then((metadata) => {
|
|
448
|
-
socket.emit("runtime.hello", { capabilities: currentCapabilities(), metadata });
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
let lastConnectErrorLogAt = 0;
|
|
452
|
-
socket.on("connect_error", (error) => {
|
|
453
|
-
pushLog(`connect_error ${error.message}`);
|
|
454
|
-
const now = Date.now();
|
|
455
|
-
if (now - lastConnectErrorLogAt < 5000)
|
|
456
|
-
return;
|
|
457
|
-
lastConnectErrorLogAt = now;
|
|
458
|
-
const wsHint = isLocalEndpoint(wsUrl)
|
|
459
|
-
? ` Nothing is listening at ${wsUrl}. For local dev run setup with --profile local and start the stack (WS on ws://localhost:8401).`
|
|
460
|
-
: ` Verify wsUrl in ${getConfigPath()} and that you can reach ${wsUrl}.`;
|
|
461
|
-
console.warn("[aiden-agent] Daemon connection error:", `${error.message}.${wsHint}`);
|
|
462
|
-
});
|
|
463
|
-
socket.on("disconnect", (reason) => {
|
|
464
|
-
pushLog(`disconnected ${reason}`);
|
|
465
|
-
console.info("[aiden-agent] Daemon disconnected:", reason);
|
|
466
|
-
});
|
|
467
|
-
socket.on("agent.execute", (payload) => {
|
|
468
|
-
if (!payload?.runId || !payload.conversationId || typeof payload.content !== "string") {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
if (activeAgents.has(payload.runId)) {
|
|
472
|
-
socket.emit("agent.rejected", { runId: payload.runId, message: "Run is already active" });
|
|
473
|
-
return;
|
|
474
|
-
}
|
|
475
|
-
const backendKind = normalizeBackendKind(payload.backendKind);
|
|
476
|
-
const runtimeCommand = resolveBackendRuntimeCommand(backendKind ?? payload.backendKind);
|
|
477
|
-
if (!runtimeCommand && backendKind) {
|
|
478
|
-
socket.emit("agent.rejected", {
|
|
479
|
-
runId: payload.runId,
|
|
480
|
-
message: `CLI command not found for ${backendKind}. Install the provider CLI and restart the daemon.`,
|
|
481
|
-
});
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
socket.emit("agent.accepted", { runId: payload.runId });
|
|
485
|
-
pushLog(`accepted run=${payload.runId}`);
|
|
486
|
-
const cwd = payload.projectPath ?? process.cwd();
|
|
487
|
-
const presenter = new RuntimePresenter(socket, payload.conversationId, payload.runId, cwd);
|
|
488
|
-
const agent = new CoreAgent(presenter, {
|
|
489
|
-
backendKind,
|
|
490
|
-
runtimeCommand,
|
|
491
|
-
providerApiKey: payload.providerApiKey,
|
|
492
|
-
});
|
|
493
|
-
activeAgents.set(payload.runId, { agent, presenter });
|
|
494
|
-
void agent.run({
|
|
495
|
-
task: payload.content,
|
|
496
|
-
maxIterations: payload.maxIterations ?? 50,
|
|
497
|
-
teamPath: cwd,
|
|
498
|
-
cwd,
|
|
499
|
-
backendKind,
|
|
500
|
-
runtimeCommand,
|
|
501
|
-
agentId: payload.agentId,
|
|
502
|
-
agentPrompt: payload.agentPrompt,
|
|
503
|
-
mode: (payload.mode ?? "agent"),
|
|
504
|
-
selectedModel: payload.model,
|
|
505
|
-
selectedContextWindow: payload.selectedContextWindow ?? null,
|
|
506
|
-
selectedEffortLevel: payload.selectedEffortLevel ?? null,
|
|
507
|
-
providerSessionId: payload.providerSessionId,
|
|
508
|
-
taskMeta: payload.taskMeta,
|
|
509
|
-
prMeta: payload.prMeta,
|
|
510
|
-
conversationId: payload.conversationId,
|
|
511
|
-
taskId: payload.taskMeta?.id,
|
|
512
|
-
teamId: payload.teamId,
|
|
513
|
-
currentUser: payload.currentUser,
|
|
514
|
-
workflowTools: payload.workflowTools,
|
|
515
|
-
images: payload.images,
|
|
516
|
-
}).catch((error) => {
|
|
517
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
518
|
-
pushLog(`failed run=${payload.runId} ${message}`);
|
|
519
|
-
socket.emit("agent.event", {
|
|
520
|
-
conversationId: payload.conversationId,
|
|
521
|
-
runId: payload.runId,
|
|
522
|
-
event: { type: "session_error", error: message },
|
|
523
|
-
});
|
|
524
|
-
}).finally(() => {
|
|
525
|
-
activeAgents.delete(payload.runId);
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
socket.on("agent.abort", (payload) => {
|
|
529
|
-
if (!payload?.runId)
|
|
530
|
-
return;
|
|
531
|
-
const entry = activeAgents.get(payload.runId);
|
|
532
|
-
if (!entry)
|
|
533
|
-
return;
|
|
534
|
-
abortActiveAgent(entry);
|
|
535
|
-
activeAgents.delete(payload.runId);
|
|
536
|
-
pushLog(`aborted run=${payload.runId}`);
|
|
537
|
-
});
|
|
538
|
-
socket.on("agent.tool_response", (payload) => {
|
|
539
|
-
if (!payload?.toolId || typeof payload.response !== "string")
|
|
540
|
-
return;
|
|
541
|
-
const agents = payload.runId
|
|
542
|
-
? [activeAgents.get(payload.runId)?.agent]
|
|
543
|
-
: [...activeAgents.values()].map((entry) => entry.agent);
|
|
544
|
-
const delivered = agents.some((agent) => agent?.sendToolResponse(payload.toolId, payload.response) === true);
|
|
545
|
-
if (!delivered)
|
|
546
|
-
pushLog(`tool_response missed tool=${payload.toolId}`);
|
|
547
|
-
});
|
|
548
|
-
socket.on("agent.append_message", (payload) => {
|
|
549
|
-
if (!payload?.runId || typeof payload.text !== "string")
|
|
550
|
-
return;
|
|
551
|
-
const agent = activeAgents.get(payload.runId)?.agent;
|
|
552
|
-
const delivered = agent?.sendUserMessage(payload.text) === true;
|
|
553
|
-
socket.emit(delivered ? "agent.accepted" : "agent.rejected", {
|
|
554
|
-
runId: payload.runId,
|
|
555
|
-
...(delivered ? {} : { message: "No active agent for appended message" }),
|
|
556
|
-
});
|
|
557
|
-
});
|
|
558
|
-
const localServer = http.createServer((req, res) => {
|
|
559
|
-
const auth = req.headers.authorization;
|
|
560
|
-
if (auth !== `Bearer ${localApiToken}`) {
|
|
561
|
-
res.writeHead(401, { "content-type": "application/json" }).end(JSON.stringify({ error: "Unauthorized" }));
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (req.method === "GET" && req.url === "/status") {
|
|
565
|
-
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
|
|
566
|
-
configured: true,
|
|
567
|
-
runtimeId,
|
|
568
|
-
wsUrl,
|
|
569
|
-
connected: socket.connected,
|
|
570
|
-
activeRunIds: [...activeAgents.keys()],
|
|
571
|
-
localApiPort,
|
|
572
|
-
version: AGENT_VERSION,
|
|
573
|
-
}));
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
if (req.method === "GET" && req.url === "/capabilities") {
|
|
577
|
-
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify(discoverCapabilities()));
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
if (req.method === "GET" && req.url === "/logs") {
|
|
581
|
-
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ lines: recentLogs }));
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
if (req.method === "POST" && req.url === "/stop") {
|
|
585
|
-
for (const entry of activeAgents.values())
|
|
586
|
-
abortActiveAgent(entry);
|
|
587
|
-
activeAgents.clear();
|
|
588
|
-
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
if (req.method === "POST" && req.url === "/shutdown") {
|
|
592
|
-
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
593
|
-
setImmediate(queueFullShutdown);
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "Not found" }));
|
|
597
|
-
});
|
|
598
|
-
const occupiedProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
599
|
-
if (occupiedProbe.state === "running") {
|
|
600
|
-
clearInterval(heartbeat);
|
|
601
|
-
socket.disconnect();
|
|
602
|
-
throw new Error(`${describeOccupiedLocalDaemon(localApiPort)} (runtime ${occupiedProbe.status.runtimeId})`);
|
|
603
|
-
}
|
|
604
|
-
if (occupiedProbe.state === "listening_unauthorized") {
|
|
605
|
-
clearInterval(heartbeat);
|
|
606
|
-
socket.disconnect();
|
|
607
|
-
throw new Error(`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Aiden desktop or run setup again.`);
|
|
608
|
-
}
|
|
609
|
-
await new Promise((resolve, reject) => {
|
|
610
|
-
const onError = (error) => {
|
|
611
|
-
clearInterval(heartbeat);
|
|
612
|
-
socket.disconnect();
|
|
613
|
-
if (error.code === "EADDRINUSE") {
|
|
614
|
-
void probeLocalDaemonEndpoint(localApiPort).then((probe) => {
|
|
615
|
-
if (probe.state !== "absent") {
|
|
616
|
-
reject(new Error(describeOccupiedLocalDaemon(localApiPort)));
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
reject(new Error(`local daemon API port ${localApiPort} is already in use by another process. `
|
|
620
|
-
+ "Otherwise free the port or pass `--local-api-port` with a different value."));
|
|
621
|
-
});
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
reject(error);
|
|
625
|
-
};
|
|
626
|
-
localServer.once("error", onError);
|
|
627
|
-
localServer.listen(localApiPort, "127.0.0.1", () => {
|
|
628
|
-
localServer.off("error", onError);
|
|
629
|
-
const address = localServer.address();
|
|
630
|
-
if (address && typeof address === "object")
|
|
631
|
-
localApiPort = address.port;
|
|
632
|
-
pushLog(`local_api listening port=${localApiPort}`);
|
|
633
|
-
console.info("[aiden-agent] Local daemon API listening", { port: localApiPort });
|
|
634
|
-
resolve();
|
|
635
|
-
});
|
|
636
|
-
});
|
|
637
|
-
let stopped = false;
|
|
638
|
-
const stop = async () => {
|
|
639
|
-
if (stopped)
|
|
640
|
-
return;
|
|
641
|
-
stopped = true;
|
|
642
|
-
clearInterval(heartbeat);
|
|
643
|
-
for (const entry of activeAgents.values()) {
|
|
644
|
-
abortActiveAgent(entry);
|
|
645
|
-
}
|
|
646
|
-
activeAgents.clear();
|
|
647
|
-
socket.disconnect();
|
|
648
|
-
await new Promise((resolve) => {
|
|
649
|
-
localServer.close(() => resolve());
|
|
650
|
-
});
|
|
651
|
-
};
|
|
652
|
-
const controller = {
|
|
653
|
-
runtimeId,
|
|
654
|
-
wsUrl,
|
|
655
|
-
localApiPort,
|
|
656
|
-
localApiToken,
|
|
657
|
-
stop,
|
|
658
|
-
};
|
|
659
|
-
const queueFullShutdown = () => {
|
|
660
|
-
void stop().then(() => controller.whenExternallyStopped?.());
|
|
661
|
-
};
|
|
662
|
-
return controller;
|
|
663
|
-
}
|
|
664
|
-
export async function stopDaemon(args) {
|
|
665
|
-
const stored = readConfig();
|
|
666
|
-
const localApiPort = getLocalApiPort(args, stored);
|
|
667
|
-
const localApiToken = stored.localApiToken;
|
|
668
|
-
if (!localApiToken) {
|
|
669
|
-
throw new Error("No local API token in config. Run setup or login first.");
|
|
670
|
-
}
|
|
671
|
-
const probe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
672
|
-
if (probe.state === "listening_unauthorized") {
|
|
673
|
-
throw new Error(`Daemon is listening on port ${localApiPort} but rejected this config token. `
|
|
674
|
-
+ "Quit Aiden desktop or rerun setup/login to refresh config.");
|
|
675
|
-
}
|
|
676
|
-
if (probe.state === "absent") {
|
|
677
|
-
console.info("[aiden-agent] No daemon listening", { port: localApiPort });
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
const response = await fetch(`http://127.0.0.1:${localApiPort}/shutdown`, {
|
|
681
|
-
method: "POST",
|
|
682
|
-
headers: { authorization: `Bearer ${localApiToken}` },
|
|
683
|
-
signal: AbortSignal.timeout(5000),
|
|
684
|
-
});
|
|
685
|
-
if (!response.ok) {
|
|
686
|
-
throw new Error(`Failed to stop daemon: HTTP ${response.status}`);
|
|
687
|
-
}
|
|
688
|
-
console.info("[aiden-agent] Daemon stopped", {
|
|
689
|
-
port: localApiPort,
|
|
690
|
-
runtimeId: probe.status.runtimeId,
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
export async function runDaemon(args) {
|
|
694
|
-
const stored = readConfig();
|
|
695
|
-
const localApiPort = getLocalApiPort(args, stored);
|
|
696
|
-
const localApiToken = stored.localApiToken;
|
|
697
|
-
const existingProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
698
|
-
if (existingProbe.state === "running") {
|
|
699
|
-
console.info("[aiden-agent] Daemon already running", {
|
|
700
|
-
port: localApiPort,
|
|
701
|
-
runtimeId: existingProbe.status.runtimeId,
|
|
702
|
-
connected: existingProbe.status.connected,
|
|
703
|
-
hint: "If Aiden desktop is open, it manages the daemon automatically.",
|
|
704
|
-
});
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
if (existingProbe.state === "listening_unauthorized") {
|
|
708
|
-
console.info("[aiden-agent] Daemon already running", {
|
|
709
|
-
port: localApiPort,
|
|
710
|
-
hint: "Aiden desktop is using a different local API token than config.json. Quit the desktop app or rerun setup if you need a fresh daemon.",
|
|
711
|
-
});
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
const controller = await startDaemon(args);
|
|
715
|
-
const shutdown = () => {
|
|
716
|
-
void controller.stop().finally(() => process.exit(0));
|
|
717
|
-
};
|
|
718
|
-
controller.whenExternallyStopped = shutdown;
|
|
719
|
-
process.once("SIGINT", shutdown);
|
|
720
|
-
process.once("SIGTERM", shutdown);
|
|
721
|
-
await new Promise(() => { });
|
|
722
|
-
}
|
|
723
|
-
export async function rotateRuntimeToken(args) {
|
|
724
|
-
const stored = readConfig();
|
|
725
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.AIDEN_API_URL ?? stored.apiUrl;
|
|
726
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.AIDEN_RUNTIME_ID ?? stored.runtimeId;
|
|
727
|
-
const token = argValue(args, "--token") ?? process.env.AIDEN_SETUP_TOKEN;
|
|
728
|
-
if (!apiUrl || !runtimeId || !token) {
|
|
729
|
-
throw new Error("token rotate requires configured api/runtime plus --token <aiden PAT>");
|
|
730
|
-
}
|
|
731
|
-
const response = await fetch(`${apiUrl}/public/runtimes/${runtimeId}/tokens/rotate`, {
|
|
732
|
-
method: "POST",
|
|
733
|
-
headers: { authorization: `Bearer ${token}` },
|
|
734
|
-
});
|
|
735
|
-
if (!response.ok) {
|
|
736
|
-
throw new Error(`runtime token rotation failed: ${response.status} ${await response.text()}`);
|
|
737
|
-
}
|
|
738
|
-
const body = await response.json();
|
|
739
|
-
writeConfig({ ...stored, apiUrl, runtimeId, runtimeToken: body.runtimeToken });
|
|
740
|
-
console.info("[aiden-agent] Runtime token rotated", { runtimeId });
|
|
741
|
-
}
|
|
742
|
-
export async function revokeRuntimeToken(args) {
|
|
743
|
-
const stored = readConfig();
|
|
744
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.AIDEN_API_URL ?? stored.apiUrl;
|
|
745
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.AIDEN_RUNTIME_ID ?? stored.runtimeId;
|
|
746
|
-
const token = argValue(args, "--token") ?? process.env.AIDEN_SETUP_TOKEN;
|
|
747
|
-
if (!apiUrl || !runtimeId || !token) {
|
|
748
|
-
throw new Error("token revoke requires configured api/runtime plus --token <aiden PAT>");
|
|
749
|
-
}
|
|
750
|
-
const response = await fetch(`${apiUrl}/public/runtimes/${runtimeId}/tokens/revoke`, {
|
|
751
|
-
method: "POST",
|
|
752
|
-
headers: { authorization: `Bearer ${token}` },
|
|
753
|
-
});
|
|
754
|
-
if (!response.ok) {
|
|
755
|
-
throw new Error(`runtime token revoke failed: ${response.status} ${await response.text()}`);
|
|
756
|
-
}
|
|
757
|
-
removeConfig();
|
|
758
|
-
console.info("[aiden-agent] Runtime token revoked", { runtimeId });
|
|
759
|
-
}
|
|
760
|
-
export function logoutDaemon() {
|
|
761
|
-
removeConfig();
|
|
762
|
-
console.info("[aiden-agent] Runtime config removed", { configPath: getConfigPath() });
|
|
763
|
-
}
|
|
764
|
-
export async function printStatus() {
|
|
765
|
-
const config = readConfig();
|
|
766
|
-
const configured = Boolean(config.runtimeId && config.runtimeToken && config.wsUrl);
|
|
767
|
-
const warnings = [];
|
|
768
|
-
if ((isLocalEndpoint(config.apiUrl) || isLocalEndpoint(config.wsUrl)) && config.endpointProfile !== "local") {
|
|
769
|
-
warnings.push("Configured endpoint points at localhost. Use --profile local only for local development.");
|
|
770
|
-
}
|
|
771
|
-
const localApiPort = config.localApiPort && config.localApiPort > 0
|
|
772
|
-
? config.localApiPort
|
|
773
|
-
: getLocalApiPort([], config);
|
|
774
|
-
const localDaemonProbe = config.localApiToken
|
|
775
|
-
? await probeLocalDaemonEndpoint(localApiPort, config.localApiToken)
|
|
776
|
-
: { state: "absent" };
|
|
777
|
-
const localDaemonRunning = localDaemonProbe.state === "running";
|
|
778
|
-
console.info(JSON.stringify({
|
|
779
|
-
mode: "runtime",
|
|
780
|
-
configured,
|
|
781
|
-
runtimeId: config.runtimeId ?? null,
|
|
782
|
-
apiUrl: config.apiUrl ?? null,
|
|
783
|
-
wsUrl: config.wsUrl ?? null,
|
|
784
|
-
endpointProfile: config.endpointProfile ?? null,
|
|
785
|
-
localApiPort: config.localApiPort ?? null,
|
|
786
|
-
localDaemonRunning,
|
|
787
|
-
hasLocalApiToken: Boolean(config.localApiToken),
|
|
788
|
-
configPath: getConfigPath(),
|
|
789
|
-
note: configured
|
|
790
|
-
? (localDaemonRunning
|
|
791
|
-
? "Local daemon is running. Stop it with: aiden-agent stop"
|
|
792
|
-
: "Durable runtime config is present. Start it with: aiden-agent daemon")
|
|
793
|
-
: "No durable runtime configured. Cloud sandbox sessions are launched by Aiden with session env vars and do not appear in this local config.",
|
|
794
|
-
warnings,
|
|
795
|
-
}, null, 2));
|
|
796
|
-
}
|
|
797
|
-
export async function printDoctor(args = []) {
|
|
798
|
-
const config = readConfig();
|
|
799
|
-
const capabilities = discoverCapabilities();
|
|
800
|
-
let synced = false;
|
|
801
|
-
let syncError = null;
|
|
802
|
-
if (args.includes("--sync")) {
|
|
803
|
-
if (!config.apiUrl || !config.runtimeId || !config.runtimeToken) {
|
|
804
|
-
throw new Error("doctor --sync requires configured apiUrl, runtimeId, and runtimeToken. Run setup first.");
|
|
805
|
-
}
|
|
806
|
-
const response = await fetch(`${config.apiUrl}/public/runtimes/${config.runtimeId}/capabilities/self`, {
|
|
807
|
-
method: "PATCH",
|
|
808
|
-
headers: {
|
|
809
|
-
authorization: `Bearer ${config.runtimeToken}`,
|
|
810
|
-
"content-type": "application/json",
|
|
811
|
-
},
|
|
812
|
-
body: JSON.stringify({ capabilities, metadata: await collectRuntimeMetadataWithProviderLimits() }),
|
|
813
|
-
});
|
|
814
|
-
if (!response.ok) {
|
|
815
|
-
syncError = `${response.status} ${await response.text()}`;
|
|
816
|
-
throw new Error(`doctor sync failed: ${syncError}`);
|
|
817
|
-
}
|
|
818
|
-
synced = true;
|
|
819
|
-
}
|
|
820
|
-
console.info(JSON.stringify({
|
|
821
|
-
version: AGENT_VERSION,
|
|
822
|
-
configPath: getConfigPath(),
|
|
823
|
-
configured: Boolean(config.runtimeId && config.runtimeToken && config.wsUrl),
|
|
824
|
-
runtimeId: config.runtimeId ?? null,
|
|
825
|
-
apiUrl: config.apiUrl ?? null,
|
|
826
|
-
wsUrl: config.wsUrl ?? null,
|
|
827
|
-
endpointProfile: config.endpointProfile ?? null,
|
|
828
|
-
warnings: (isLocalEndpoint(config.apiUrl) || isLocalEndpoint(config.wsUrl)) && config.endpointProfile !== "local"
|
|
829
|
-
? ["Configured endpoint points at localhost. Use --profile local only for local development."]
|
|
830
|
-
: [],
|
|
831
|
-
synced,
|
|
832
|
-
syncError,
|
|
833
|
-
capabilities,
|
|
834
|
-
}, null, 2));
|
|
835
|
-
}
|
|
836
|
-
//# sourceMappingURL=daemon.js.map
|