@alook/daemon 0.1.30 → 0.1.32
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/cli/index.js +989 -195
- package/dist/index.js +840 -118
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -15979,6 +15979,7 @@ var TERMINAL_MEETING_STATUSES = [
|
|
|
15979
15979
|
MeetingStatus.COMPLETED,
|
|
15980
15980
|
MeetingStatus.FAILED
|
|
15981
15981
|
];
|
|
15982
|
+
var COMMUNITY_BOT_LIMIT_PER_OWNER = 20;
|
|
15982
15983
|
var COMMUNITY_BOT_NAME_MIN = 1;
|
|
15983
15984
|
var COMMUNITY_BOT_NAME_MAX = 32;
|
|
15984
15985
|
var COMMUNITY_BOT_DESCRIPTION_MAX = 1024;
|
|
@@ -16853,6 +16854,13 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
|
16853
16854
|
launchId: exports_external.string().min(1),
|
|
16854
16855
|
unreadNotice: exports_external.unknown()
|
|
16855
16856
|
}),
|
|
16857
|
+
exports_external.object({
|
|
16858
|
+
type: exports_external.literal("agent:event"),
|
|
16859
|
+
agentId: exports_external.string().min(1),
|
|
16860
|
+
config: exports_external.unknown(),
|
|
16861
|
+
launchId: exports_external.string().min(1),
|
|
16862
|
+
prompt: exports_external.string().min(1).max(32768)
|
|
16863
|
+
}),
|
|
16856
16864
|
exports_external.object({
|
|
16857
16865
|
type: exports_external.literal("agent:stop"),
|
|
16858
16866
|
agentId: exports_external.string().min(1)
|
|
@@ -17762,6 +17770,10 @@ var CommunityBotPatchRequestSchema = exports_external.object({
|
|
|
17762
17770
|
var CommunityBotAddToServerRequestSchema = exports_external.object({
|
|
17763
17771
|
botId: exports_external.string().min(1)
|
|
17764
17772
|
});
|
|
17773
|
+
var CommunityServerOnboardRequestSchema = exports_external.strictObject({
|
|
17774
|
+
botIds: exports_external.array(exports_external.string().min(1).max(128)).min(1).max(COMMUNITY_BOT_LIMIT_PER_OWNER).refine((ids) => new Set(ids).size === ids.length, "botIds must be unique"),
|
|
17775
|
+
wakePrompt: exports_external.string().max(32768).refine((value) => value.trim().length > 0, "wakePrompt is required")
|
|
17776
|
+
});
|
|
17765
17777
|
var CommunityAgentMessageContentSchema = exports_external.object({ text: exports_external.string().max(MAX_MESSAGE_CONTENT_LENGTH).default("") }).catchall(exports_external.unknown());
|
|
17766
17778
|
var CommunityAgentSeqSchema = exports_external.number().int().min(0);
|
|
17767
17779
|
var CommunityAgentPositiveSeqSchema = exports_external.number().int().min(1);
|
|
@@ -17856,6 +17868,9 @@ var AuditLogThinkingPayloadSchema = exports_external.object({
|
|
|
17856
17868
|
truncated: exports_external.boolean(),
|
|
17857
17869
|
chars: exports_external.number().int().nonnegative()
|
|
17858
17870
|
});
|
|
17871
|
+
var AuditLogTurnInterruptPayloadSchema = exports_external.object({
|
|
17872
|
+
status: exports_external.literal("accepted")
|
|
17873
|
+
});
|
|
17859
17874
|
var AuditLogWakeTriggerPayloadSchema = exports_external.object({
|
|
17860
17875
|
messageId: exports_external.string().min(1),
|
|
17861
17876
|
channel: exports_external.string().min(1),
|
|
@@ -17888,6 +17903,7 @@ var BotAuditEventSchema = exports_external.discriminatedUnion("kind", [
|
|
|
17888
17903
|
exports_external.object({ kind: exports_external.literal("cli_invocation"), payload: AuditLogCliInvocationPayloadSchema }),
|
|
17889
17904
|
exports_external.object({ kind: exports_external.literal("tool_call"), payload: AuditLogToolCallPayloadSchema }),
|
|
17890
17905
|
exports_external.object({ kind: exports_external.literal("thinking"), payload: AuditLogThinkingPayloadSchema }),
|
|
17906
|
+
exports_external.object({ kind: exports_external.literal("turn_interrupt"), payload: AuditLogTurnInterruptPayloadSchema }),
|
|
17891
17907
|
exports_external.object({ kind: exports_external.literal("wake_trigger"), payload: AuditLogWakeTriggerPayloadSchema }),
|
|
17892
17908
|
exports_external.object({ kind: exports_external.literal("session_reset"), payload: AuditLogSessionResetPayloadSchema }),
|
|
17893
17909
|
exports_external.object({ kind: exports_external.literal("nap"), payload: AuditLogNapPayloadSchema }),
|
|
@@ -17899,6 +17915,7 @@ var BotAuditEventKindSchema = exports_external.enum([
|
|
|
17899
17915
|
"cli_invocation",
|
|
17900
17916
|
"tool_call",
|
|
17901
17917
|
"thinking",
|
|
17918
|
+
"turn_interrupt",
|
|
17902
17919
|
"wake_trigger",
|
|
17903
17920
|
"session_reset",
|
|
17904
17921
|
"nap",
|
|
@@ -18855,7 +18872,7 @@ var communityBotAuditEventSchema = exports_external.strictObject({
|
|
|
18855
18872
|
type: exports_external.literal("community:bot.audit_event"),
|
|
18856
18873
|
botId: string4,
|
|
18857
18874
|
id: string4,
|
|
18858
|
-
kind: exports_external.enum(["cli_invocation", "tool_call", "thinking", "wake_trigger", "session_reset", "nap", "model_changed", "provider_changed", "error"]),
|
|
18875
|
+
kind: exports_external.enum(["cli_invocation", "tool_call", "thinking", "turn_interrupt", "wake_trigger", "session_reset", "nap", "model_changed", "provider_changed", "error"]),
|
|
18859
18876
|
payload: exports_external.unknown(),
|
|
18860
18877
|
sessionId: nullableString.optional(),
|
|
18861
18878
|
launchId: nullableString.optional(),
|
|
@@ -19552,13 +19569,13 @@ function createProxyServerApi(config2) {
|
|
|
19552
19569
|
|
|
19553
19570
|
// src/cli/daemonStart.ts
|
|
19554
19571
|
import * as fs12 from "fs";
|
|
19555
|
-
import * as
|
|
19572
|
+
import * as path17 from "path";
|
|
19556
19573
|
import * as crypto5 from "crypto";
|
|
19557
19574
|
import * as os3 from "os";
|
|
19558
|
-
import { homedir as
|
|
19575
|
+
import { homedir as homedir8 } from "os";
|
|
19559
19576
|
|
|
19560
19577
|
// src/discovery.ts
|
|
19561
|
-
import * as
|
|
19578
|
+
import * as path13 from "path";
|
|
19562
19579
|
import * as fs6 from "fs";
|
|
19563
19580
|
import { fileURLToPath } from "url";
|
|
19564
19581
|
// agent-driver/dist/host/default-host.js
|
|
@@ -20702,6 +20719,12 @@ function tryParseJsonLine(line) {
|
|
|
20702
20719
|
return null;
|
|
20703
20720
|
}
|
|
20704
20721
|
}
|
|
20722
|
+
function asRecord(value) {
|
|
20723
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
20724
|
+
}
|
|
20725
|
+
function tryParseJsonRecord(line) {
|
|
20726
|
+
return asRecord(tryParseJsonLine(line));
|
|
20727
|
+
}
|
|
20705
20728
|
|
|
20706
20729
|
// agent-driver/dist/adapters/claude/normalizer.js
|
|
20707
20730
|
var API_ERROR_RE = /API Error:.*(?:Connection error|\b[45]\d{2}\b)/i;
|
|
@@ -21200,6 +21223,241 @@ class ClaudeTurnProtocol {
|
|
|
21200
21223
|
}
|
|
21201
21224
|
}
|
|
21202
21225
|
|
|
21226
|
+
// agent-driver/dist/adapters/claude/recent-context.js
|
|
21227
|
+
import { createReadStream as createReadStream2 } from "node:fs";
|
|
21228
|
+
import { homedir } from "node:os";
|
|
21229
|
+
import path7 from "node:path";
|
|
21230
|
+
import { createInterface } from "node:readline";
|
|
21231
|
+
|
|
21232
|
+
// agent-driver/dist/internal/recent-context.js
|
|
21233
|
+
import path6 from "node:path";
|
|
21234
|
+
import { createReadStream } from "node:fs";
|
|
21235
|
+
import { readdir, stat } from "node:fs/promises";
|
|
21236
|
+
function normalizeAbsolutePath(value) {
|
|
21237
|
+
if (typeof value !== "string" || !path6.isAbsolute(value))
|
|
21238
|
+
return null;
|
|
21239
|
+
return path6.normalize(value);
|
|
21240
|
+
}
|
|
21241
|
+
function normalizeModifiedAt(value) {
|
|
21242
|
+
const date5 = value instanceof Date ? value : typeof value === "string" || typeof value === "number" ? new Date(value) : null;
|
|
21243
|
+
if (!date5 || !Number.isFinite(date5.getTime()))
|
|
21244
|
+
return null;
|
|
21245
|
+
return date5.toISOString();
|
|
21246
|
+
}
|
|
21247
|
+
function isValidRecentContextTopK(value) {
|
|
21248
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
21249
|
+
}
|
|
21250
|
+
function isMissingPathError(error51) {
|
|
21251
|
+
return error51?.code === "ENOENT";
|
|
21252
|
+
}
|
|
21253
|
+
async function readFirstLine(filePath, maxBytes = 65536) {
|
|
21254
|
+
const stream = createReadStream(filePath, { highWaterMark: 1 });
|
|
21255
|
+
const bytes = [];
|
|
21256
|
+
let length = 0;
|
|
21257
|
+
try {
|
|
21258
|
+
for await (const chunk2 of stream) {
|
|
21259
|
+
const byte = Buffer.isBuffer(chunk2) ? chunk2 : Buffer.from(chunk2);
|
|
21260
|
+
if (byte[0] === 10)
|
|
21261
|
+
break;
|
|
21262
|
+
if (length >= maxBytes)
|
|
21263
|
+
return null;
|
|
21264
|
+
bytes.push(byte);
|
|
21265
|
+
length += byte.length;
|
|
21266
|
+
}
|
|
21267
|
+
return length === 0 ? null : Buffer.concat(bytes, length).toString("utf8").replace(/\r$/, "");
|
|
21268
|
+
} catch (error51) {
|
|
21269
|
+
if (isMissingPathError(error51))
|
|
21270
|
+
return null;
|
|
21271
|
+
throw error51;
|
|
21272
|
+
} finally {
|
|
21273
|
+
stream.destroy();
|
|
21274
|
+
}
|
|
21275
|
+
}
|
|
21276
|
+
async function collectJsonlFileCandidates(root, options = {}) {
|
|
21277
|
+
const candidates = [];
|
|
21278
|
+
const readDirectory = options.readDirectory ?? ((directory) => readdir(directory, { withFileTypes: true }));
|
|
21279
|
+
const statPath = options.statPath ?? stat;
|
|
21280
|
+
const visit = async (directory, remainingDepth, includeFiles) => {
|
|
21281
|
+
let entries;
|
|
21282
|
+
try {
|
|
21283
|
+
entries = await readDirectory(directory);
|
|
21284
|
+
} catch (error51) {
|
|
21285
|
+
if (isMissingPathError(error51))
|
|
21286
|
+
return;
|
|
21287
|
+
throw error51;
|
|
21288
|
+
}
|
|
21289
|
+
await Promise.all(entries.map(async (entry) => {
|
|
21290
|
+
const entryPath = path6.join(directory, entry.name);
|
|
21291
|
+
if (entry.isDirectory() && remainingDepth > 0) {
|
|
21292
|
+
await visit(entryPath, remainingDepth - 1, true);
|
|
21293
|
+
} else if (includeFiles && entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
21294
|
+
try {
|
|
21295
|
+
const metadata = await statPath(entryPath);
|
|
21296
|
+
if (metadata.isFile())
|
|
21297
|
+
candidates.push({ filePath: entryPath, modifiedAt: metadata.mtime });
|
|
21298
|
+
} catch (error51) {
|
|
21299
|
+
if (!isMissingPathError(error51))
|
|
21300
|
+
throw error51;
|
|
21301
|
+
}
|
|
21302
|
+
}
|
|
21303
|
+
}));
|
|
21304
|
+
};
|
|
21305
|
+
await visit(root, options.maxDepth ?? Number.POSITIVE_INFINITY, options.includeRootFiles ?? true);
|
|
21306
|
+
return candidates;
|
|
21307
|
+
}
|
|
21308
|
+
function retainNewest(values, key, value, topK) {
|
|
21309
|
+
const existing = values.get(key);
|
|
21310
|
+
if (existing) {
|
|
21311
|
+
if (value.modifiedAt > existing.modifiedAt)
|
|
21312
|
+
values.set(key, value);
|
|
21313
|
+
return;
|
|
21314
|
+
}
|
|
21315
|
+
if (values.size < topK) {
|
|
21316
|
+
values.set(key, value);
|
|
21317
|
+
return;
|
|
21318
|
+
}
|
|
21319
|
+
let oldest;
|
|
21320
|
+
for (const entry of values) {
|
|
21321
|
+
if (!oldest || entry[1].modifiedAt < oldest[1].modifiedAt)
|
|
21322
|
+
oldest = entry;
|
|
21323
|
+
}
|
|
21324
|
+
if (oldest && value.modifiedAt > oldest[1].modifiedAt) {
|
|
21325
|
+
values.delete(oldest[0]);
|
|
21326
|
+
values.set(key, value);
|
|
21327
|
+
}
|
|
21328
|
+
}
|
|
21329
|
+
|
|
21330
|
+
class RecentContextCollector {
|
|
21331
|
+
request;
|
|
21332
|
+
sessionFileCapability;
|
|
21333
|
+
sessions = new Map;
|
|
21334
|
+
projects = new Map;
|
|
21335
|
+
constructor(request, sessionFileCapability) {
|
|
21336
|
+
this.request = request;
|
|
21337
|
+
this.sessionFileCapability = sessionFileCapability;
|
|
21338
|
+
}
|
|
21339
|
+
add(candidate, target2 = "both") {
|
|
21340
|
+
const projectPath = normalizeAbsolutePath(candidate.projectPath);
|
|
21341
|
+
const modifiedAt = normalizeModifiedAt(candidate.modifiedAt);
|
|
21342
|
+
if (!projectPath || !modifiedAt)
|
|
21343
|
+
return;
|
|
21344
|
+
if (target2 !== "session") {
|
|
21345
|
+
retainNewest(this.projects, projectPath, { projectPath, modifiedAt }, this.request.recentProjectsTopK);
|
|
21346
|
+
}
|
|
21347
|
+
if (target2 === "project" || this.sessionFileCapability !== "supported")
|
|
21348
|
+
return;
|
|
21349
|
+
const sessionFilePath = normalizeAbsolutePath(candidate.sessionFilePath);
|
|
21350
|
+
if (!sessionFilePath)
|
|
21351
|
+
return;
|
|
21352
|
+
retainNewest(this.sessions, sessionFilePath, { sessionFilePath, projectPath, modifiedAt }, this.request.recentSessionFilesTopK);
|
|
21353
|
+
}
|
|
21354
|
+
get satisfied() {
|
|
21355
|
+
const sessionsSatisfied = this.sessionFileCapability === "unavailable" || this.sessions.size >= this.request.recentSessionFilesTopK;
|
|
21356
|
+
return sessionsSatisfied && this.projects.size >= this.request.recentProjectsTopK;
|
|
21357
|
+
}
|
|
21358
|
+
result() {
|
|
21359
|
+
const newestFirst = (left, right) => right.modifiedAt.localeCompare(left.modifiedAt);
|
|
21360
|
+
return {
|
|
21361
|
+
sessionFiles: {
|
|
21362
|
+
capability: this.sessionFileCapability,
|
|
21363
|
+
items: [...this.sessions.values()].sort(newestFirst).slice(0, this.request.recentSessionFilesTopK)
|
|
21364
|
+
},
|
|
21365
|
+
recentProjects: [...this.projects.values()].sort(newestFirst).slice(0, this.request.recentProjectsTopK)
|
|
21366
|
+
};
|
|
21367
|
+
}
|
|
21368
|
+
}
|
|
21369
|
+
function sanitizeRecentContextData(value, request) {
|
|
21370
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
21371
|
+
return null;
|
|
21372
|
+
const candidate = value;
|
|
21373
|
+
if (!candidate.sessionFiles || typeof candidate.sessionFiles !== "object" || Array.isArray(candidate.sessionFiles)) {
|
|
21374
|
+
return null;
|
|
21375
|
+
}
|
|
21376
|
+
const sessionFiles = candidate.sessionFiles;
|
|
21377
|
+
const capability = sessionFiles.capability;
|
|
21378
|
+
if (capability !== "supported" && capability !== "unavailable" || !Array.isArray(sessionFiles.items)) {
|
|
21379
|
+
return null;
|
|
21380
|
+
}
|
|
21381
|
+
if (!Array.isArray(candidate.recentProjects))
|
|
21382
|
+
return null;
|
|
21383
|
+
const collector = new RecentContextCollector(request, capability);
|
|
21384
|
+
if (capability === "supported") {
|
|
21385
|
+
for (const item of sessionFiles.items) {
|
|
21386
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
21387
|
+
continue;
|
|
21388
|
+
const record2 = item;
|
|
21389
|
+
collector.add({
|
|
21390
|
+
sessionFilePath: record2.sessionFilePath,
|
|
21391
|
+
projectPath: record2.projectPath,
|
|
21392
|
+
modifiedAt: record2.modifiedAt
|
|
21393
|
+
}, "session");
|
|
21394
|
+
}
|
|
21395
|
+
}
|
|
21396
|
+
for (const item of candidate.recentProjects) {
|
|
21397
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
21398
|
+
continue;
|
|
21399
|
+
const record2 = item;
|
|
21400
|
+
collector.add({ projectPath: record2.projectPath, modifiedAt: record2.modifiedAt }, "project");
|
|
21401
|
+
}
|
|
21402
|
+
return collector.result();
|
|
21403
|
+
}
|
|
21404
|
+
|
|
21405
|
+
// agent-driver/dist/adapters/claude/recent-context.js
|
|
21406
|
+
async function readClaudeProjectPath(filePath) {
|
|
21407
|
+
const input = createReadStream2(filePath);
|
|
21408
|
+
const lines = createInterface({ input, crlfDelay: Infinity });
|
|
21409
|
+
try {
|
|
21410
|
+
for await (const line of lines) {
|
|
21411
|
+
let record2;
|
|
21412
|
+
try {
|
|
21413
|
+
record2 = JSON.parse(line);
|
|
21414
|
+
} catch {
|
|
21415
|
+
continue;
|
|
21416
|
+
}
|
|
21417
|
+
if (!record2 || typeof record2 !== "object")
|
|
21418
|
+
continue;
|
|
21419
|
+
const cwd = record2.cwd;
|
|
21420
|
+
if (typeof cwd === "string" && path7.isAbsolute(cwd))
|
|
21421
|
+
return cwd;
|
|
21422
|
+
}
|
|
21423
|
+
return null;
|
|
21424
|
+
} catch (error51) {
|
|
21425
|
+
if (isMissingPathError(error51))
|
|
21426
|
+
return null;
|
|
21427
|
+
throw error51;
|
|
21428
|
+
} finally {
|
|
21429
|
+
lines.close();
|
|
21430
|
+
input.destroy();
|
|
21431
|
+
}
|
|
21432
|
+
}
|
|
21433
|
+
async function discoverClaudeRecentContext(request, dependencies = {}) {
|
|
21434
|
+
const collector = new RecentContextCollector(request, "supported");
|
|
21435
|
+
if (collector.satisfied)
|
|
21436
|
+
return collector.result();
|
|
21437
|
+
const projectsRoot = path7.resolve(dependencies.projectsRoot ?? path7.join(process.env.CLAUDE_CONFIG_DIR || path7.join(homedir(), ".claude"), "projects"));
|
|
21438
|
+
const readProjectPath = dependencies.readProjectPath ?? readClaudeProjectPath;
|
|
21439
|
+
const candidates = await collectJsonlFileCandidates(projectsRoot, {
|
|
21440
|
+
includeRootFiles: false,
|
|
21441
|
+
maxDepth: 1,
|
|
21442
|
+
readDirectory: dependencies.readDirectory,
|
|
21443
|
+
statPath: dependencies.statPath
|
|
21444
|
+
});
|
|
21445
|
+
candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
|
|
21446
|
+
for (const candidate of candidates) {
|
|
21447
|
+
const projectPath = await readProjectPath(candidate.filePath);
|
|
21448
|
+
if (!projectPath)
|
|
21449
|
+
continue;
|
|
21450
|
+
collector.add({
|
|
21451
|
+
sessionFilePath: candidate.filePath,
|
|
21452
|
+
projectPath,
|
|
21453
|
+
modifiedAt: candidate.modifiedAt
|
|
21454
|
+
});
|
|
21455
|
+
if (collector.satisfied)
|
|
21456
|
+
break;
|
|
21457
|
+
}
|
|
21458
|
+
return collector.result();
|
|
21459
|
+
}
|
|
21460
|
+
|
|
21203
21461
|
// agent-driver/dist/adapters/claude/index.js
|
|
21204
21462
|
var CLAUDE_MODEL_CATALOG = {
|
|
21205
21463
|
updateMode: "unsupported",
|
|
@@ -21221,6 +21479,9 @@ class ClaudeDriver {
|
|
|
21221
21479
|
turnProtocol = new ClaudeTurnProtocol;
|
|
21222
21480
|
eventNormalizer = new ClaudeEventNormalizer(this.turnProtocol);
|
|
21223
21481
|
pendingInterrupt;
|
|
21482
|
+
discoverRecentContext(request) {
|
|
21483
|
+
return discoverClaudeRecentContext(request);
|
|
21484
|
+
}
|
|
21224
21485
|
beginTurn() {
|
|
21225
21486
|
this.pendingInterrupt = undefined;
|
|
21226
21487
|
const receipt = this.turnProtocol.beginTurn();
|
|
@@ -21484,16 +21745,16 @@ function normalizeFileChangeInput(item) {
|
|
|
21484
21745
|
for (const candidate of changePaths) {
|
|
21485
21746
|
if (typeof candidate !== "string")
|
|
21486
21747
|
continue;
|
|
21487
|
-
const
|
|
21488
|
-
if (!
|
|
21748
|
+
const path8 = candidate.trim();
|
|
21749
|
+
if (!path8 || seen.has(path8))
|
|
21489
21750
|
continue;
|
|
21490
|
-
seen.add(
|
|
21491
|
-
paths.push(
|
|
21751
|
+
seen.add(path8);
|
|
21752
|
+
paths.push(path8);
|
|
21492
21753
|
}
|
|
21493
21754
|
if (paths.length === 0 && typeof item?.path === "string") {
|
|
21494
|
-
const
|
|
21495
|
-
if (
|
|
21496
|
-
paths.push(
|
|
21755
|
+
const path8 = item.path.trim();
|
|
21756
|
+
if (path8)
|
|
21757
|
+
paths.push(path8);
|
|
21497
21758
|
}
|
|
21498
21759
|
return paths.length > 0 ? { path: paths.join(", ") } : {};
|
|
21499
21760
|
}
|
|
@@ -21862,7 +22123,7 @@ class CodexEventNormalizer {
|
|
|
21862
22123
|
|
|
21863
22124
|
// agent-driver/dist/adapters/codex/home.js
|
|
21864
22125
|
import * as os from "os";
|
|
21865
|
-
import * as
|
|
22126
|
+
import * as path8 from "path";
|
|
21866
22127
|
function readConfiguredCodexHome(env) {
|
|
21867
22128
|
const raw = env.CODEX_HOME;
|
|
21868
22129
|
return typeof raw === "string" && raw.trim().length > 0 ? raw : null;
|
|
@@ -21870,8 +22131,8 @@ function readConfiguredCodexHome(env) {
|
|
|
21870
22131
|
function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
|
|
21871
22132
|
const raw = readConfiguredCodexHome(env);
|
|
21872
22133
|
if (raw)
|
|
21873
|
-
return
|
|
21874
|
-
return
|
|
22134
|
+
return path8.resolve(opts.cwd ?? process.cwd(), raw);
|
|
22135
|
+
return path8.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
|
|
21875
22136
|
}
|
|
21876
22137
|
|
|
21877
22138
|
// agent-driver/dist/internal/errors.js
|
|
@@ -21964,6 +22225,49 @@ function parsePiModelCatalog(values) {
|
|
|
21964
22225
|
return catalogFromIds(ids);
|
|
21965
22226
|
}
|
|
21966
22227
|
|
|
22228
|
+
// agent-driver/dist/adapters/codex/recent-context.js
|
|
22229
|
+
import path9 from "node:path";
|
|
22230
|
+
function isChildSession(payload) {
|
|
22231
|
+
if (payload.parentThreadId != null || payload.parent_thread_id != null)
|
|
22232
|
+
return true;
|
|
22233
|
+
const source = payload.source;
|
|
22234
|
+
if (typeof source === "string")
|
|
22235
|
+
return source.toLowerCase().startsWith("subagent");
|
|
22236
|
+
const sourceRecord = asRecord(source);
|
|
22237
|
+
return sourceRecord != null && (("subagent" in sourceRecord) || ("subAgent" in sourceRecord));
|
|
22238
|
+
}
|
|
22239
|
+
async function discoverCodexRecentContext(request, dependencies = {}) {
|
|
22240
|
+
const collector = new RecentContextCollector(request, "supported");
|
|
22241
|
+
if (collector.satisfied)
|
|
22242
|
+
return collector.result();
|
|
22243
|
+
const sessionsRoot = path9.resolve(dependencies.sessionsRoot ?? path9.join(resolveCodexHomeRootFromEnv(), "sessions"));
|
|
22244
|
+
const readHeader = dependencies.readHeader ?? readFirstLine;
|
|
22245
|
+
const candidates = await collectJsonlFileCandidates(sessionsRoot, {
|
|
22246
|
+
readDirectory: dependencies.readDirectory,
|
|
22247
|
+
statPath: dependencies.statPath
|
|
22248
|
+
});
|
|
22249
|
+
candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
|
|
22250
|
+
for (const candidate of candidates) {
|
|
22251
|
+
const line = await readHeader(candidate.filePath);
|
|
22252
|
+
if (!line)
|
|
22253
|
+
continue;
|
|
22254
|
+
const header = tryParseJsonRecord(line);
|
|
22255
|
+
if (!header)
|
|
22256
|
+
continue;
|
|
22257
|
+
const payload = asRecord(header.payload);
|
|
22258
|
+
if (header.type !== "session_meta" || !payload || isChildSession(payload))
|
|
22259
|
+
continue;
|
|
22260
|
+
collector.add({
|
|
22261
|
+
sessionFilePath: candidate.filePath,
|
|
22262
|
+
projectPath: payload.cwd,
|
|
22263
|
+
modifiedAt: candidate.modifiedAt
|
|
22264
|
+
});
|
|
22265
|
+
if (collector.satisfied)
|
|
22266
|
+
break;
|
|
22267
|
+
}
|
|
22268
|
+
return collector.result();
|
|
22269
|
+
}
|
|
22270
|
+
|
|
21967
22271
|
// agent-driver/dist/adapters/codex/index.js
|
|
21968
22272
|
var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
|
|
21969
22273
|
var MODEL_LIST_TIMEOUT_MS = 5000;
|
|
@@ -21992,6 +22296,9 @@ class CodexDriver {
|
|
|
21992
22296
|
eventNormalizer = new CodexEventNormalizer;
|
|
21993
22297
|
pendingAccountReadRequestIds = new Set;
|
|
21994
22298
|
requestId = 0;
|
|
22299
|
+
discoverRecentContext(request) {
|
|
22300
|
+
return discoverCodexRecentContext(request);
|
|
22301
|
+
}
|
|
21995
22302
|
codexHomeRoot = null;
|
|
21996
22303
|
proc = null;
|
|
21997
22304
|
pendingInitialPrompt = null;
|
|
@@ -23168,6 +23475,196 @@ class CursorAcpLane {
|
|
|
23168
23475
|
}
|
|
23169
23476
|
}
|
|
23170
23477
|
|
|
23478
|
+
// agent-driver/dist/internal/discovery-process.js
|
|
23479
|
+
import { StringDecoder } from "node:string_decoder";
|
|
23480
|
+
function toBuffer(chunk2) {
|
|
23481
|
+
if (Buffer.isBuffer(chunk2))
|
|
23482
|
+
return chunk2;
|
|
23483
|
+
if (chunk2 instanceof Uint8Array)
|
|
23484
|
+
return Buffer.from(chunk2.buffer, chunk2.byteOffset, chunk2.byteLength);
|
|
23485
|
+
return Buffer.from(String(chunk2));
|
|
23486
|
+
}
|
|
23487
|
+
async function cleanupProcess(process3, label) {
|
|
23488
|
+
if (process3.pid)
|
|
23489
|
+
await killProcessTree(process3.pid, { graceMs: 250 });
|
|
23490
|
+
else if (process3.exitCode === null && process3.signalCode === null && !process3.kill("SIGTERM")) {
|
|
23491
|
+
throw new Error(`${label} discovery cleanup failed`);
|
|
23492
|
+
}
|
|
23493
|
+
}
|
|
23494
|
+
function onStream(stream, event, listener) {
|
|
23495
|
+
stream?.on?.(event, listener);
|
|
23496
|
+
}
|
|
23497
|
+
function runBoundedDiscoveryProcess(options) {
|
|
23498
|
+
return new Promise((resolve2, reject) => {
|
|
23499
|
+
const decoder = new StringDecoder("utf8");
|
|
23500
|
+
let settled = false;
|
|
23501
|
+
let decoderEnded = false;
|
|
23502
|
+
let outputBytes = 0;
|
|
23503
|
+
const settle = (result, error51) => {
|
|
23504
|
+
if (settled)
|
|
23505
|
+
return;
|
|
23506
|
+
settled = true;
|
|
23507
|
+
clearTimeout(timer);
|
|
23508
|
+
if (!decoderEnded)
|
|
23509
|
+
decoder.end();
|
|
23510
|
+
Promise.resolve().then(() => options.cleanup?.(options.process) ?? cleanupProcess(options.process, options.label)).then(() => error51 ? reject(error51) : resolve2(result), () => reject(error51 ?? new Error(`${options.label} discovery cleanup failed`)));
|
|
23511
|
+
};
|
|
23512
|
+
const control = {
|
|
23513
|
+
get outputBytes() {
|
|
23514
|
+
return outputBytes;
|
|
23515
|
+
},
|
|
23516
|
+
finish: (result) => settle(result),
|
|
23517
|
+
fail: (message2) => settle(undefined, new Error(message2)),
|
|
23518
|
+
flushStdout: () => {
|
|
23519
|
+
if (settled || decoderEnded)
|
|
23520
|
+
return;
|
|
23521
|
+
decoderEnded = true;
|
|
23522
|
+
options.onStdout(decoder.end(), true, control);
|
|
23523
|
+
},
|
|
23524
|
+
write: (line) => {
|
|
23525
|
+
const stdin = options.process.stdin;
|
|
23526
|
+
if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false) {
|
|
23527
|
+
return control.fail(`${options.label} discovery transport is unavailable`);
|
|
23528
|
+
}
|
|
23529
|
+
try {
|
|
23530
|
+
stdin.write(line);
|
|
23531
|
+
} catch {
|
|
23532
|
+
control.fail(`${options.label} discovery transport write failed`);
|
|
23533
|
+
}
|
|
23534
|
+
}
|
|
23535
|
+
};
|
|
23536
|
+
const addBytes = (chunk2, decode3) => {
|
|
23537
|
+
if (settled)
|
|
23538
|
+
return;
|
|
23539
|
+
const bytes = toBuffer(chunk2);
|
|
23540
|
+
outputBytes += bytes.byteLength;
|
|
23541
|
+
if (outputBytes > options.outputMaxBytes) {
|
|
23542
|
+
control.fail(`${options.label} discovery output exceeded its bound`);
|
|
23543
|
+
} else if (decode3)
|
|
23544
|
+
options.onStdout(decoder.write(bytes), false, control);
|
|
23545
|
+
};
|
|
23546
|
+
const timer = setTimeout(() => control.fail(`${options.label} discovery timed out`), Math.max(1, options.timeoutMs));
|
|
23547
|
+
timer.unref?.();
|
|
23548
|
+
options.process.stdout?.on("data", (chunk2) => addBytes(chunk2, true));
|
|
23549
|
+
options.process.stderr?.on("data", (chunk2) => addBytes(chunk2, false));
|
|
23550
|
+
onStream(options.process.stdout, "end", control.flushStdout);
|
|
23551
|
+
for (const stream of [options.process.stdin, options.process.stdout, options.process.stderr]) {
|
|
23552
|
+
onStream(stream, "error", () => control.fail(`${options.label} discovery transport failed`));
|
|
23553
|
+
}
|
|
23554
|
+
options.process.on("error", () => control.fail(`${options.label} discovery process failed`));
|
|
23555
|
+
options.process.on(options.exitEvent, (code, signal) => options.onExit(code, signal, control));
|
|
23556
|
+
options.onStart?.(control);
|
|
23557
|
+
});
|
|
23558
|
+
}
|
|
23559
|
+
|
|
23560
|
+
// agent-driver/dist/adapters/cursor/recent-context.js
|
|
23561
|
+
var ACP_PROTOCOL_VERSION3 = 1;
|
|
23562
|
+
var AUTH_METHOD_ID3 = "cursor_login";
|
|
23563
|
+
var CURSOR_DISCOVERY_TIMEOUT_MS = 30000;
|
|
23564
|
+
var CURSOR_DISCOVERY_OUTPUT_MAX_BYTES = 4 * 1024 * 1024;
|
|
23565
|
+
async function discoverCursorRecentContext(request, dependencies = {}) {
|
|
23566
|
+
const collector = new RecentContextCollector(request, "unavailable");
|
|
23567
|
+
if (collector.satisfied)
|
|
23568
|
+
return collector.result();
|
|
23569
|
+
const spec = resolveSpawnSpec("cursor-agent", ["acp"], request.command);
|
|
23570
|
+
const processHandle = (dependencies.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
|
|
23571
|
+
cwd: dependencies.cwd ?? process.cwd(),
|
|
23572
|
+
env: { ...process.env, CI: "1" },
|
|
23573
|
+
shell: spec.shell
|
|
23574
|
+
});
|
|
23575
|
+
let buffer = "";
|
|
23576
|
+
let requestId = 0;
|
|
23577
|
+
let expectedId = 0;
|
|
23578
|
+
let expectedMethod = "";
|
|
23579
|
+
const seenCursors = new Set;
|
|
23580
|
+
const requestRpc = (method, params, control) => {
|
|
23581
|
+
expectedId = ++requestId;
|
|
23582
|
+
expectedMethod = method;
|
|
23583
|
+
control.write(`${jsonRpcRequest(method, params, expectedId)}
|
|
23584
|
+
`);
|
|
23585
|
+
};
|
|
23586
|
+
const requestPage = (control, cursor) => requestRpc("session/list", cursor ? { cursor } : {}, control);
|
|
23587
|
+
const onLine = (line, control) => {
|
|
23588
|
+
const message2 = asRecord(tryParseJsonLine(line));
|
|
23589
|
+
if (!message2 || message2.id !== expectedId)
|
|
23590
|
+
return;
|
|
23591
|
+
if (message2.error !== undefined)
|
|
23592
|
+
return control.fail("Cursor discovery request failed");
|
|
23593
|
+
const result = asRecord(message2.result);
|
|
23594
|
+
if (!result)
|
|
23595
|
+
return control.fail("Cursor discovery returned an invalid response");
|
|
23596
|
+
if (expectedMethod === "initialize") {
|
|
23597
|
+
if (result.protocolVersion !== ACP_PROTOCOL_VERSION3) {
|
|
23598
|
+
return control.fail("Cursor discovery protocol is incompatible");
|
|
23599
|
+
}
|
|
23600
|
+
const capabilities = asRecord(result.agentCapabilities);
|
|
23601
|
+
const sessionCapabilities = asRecord(capabilities?.sessionCapabilities);
|
|
23602
|
+
if (sessionCapabilities?.list !== true && !asRecord(sessionCapabilities?.list)) {
|
|
23603
|
+
return control.fail("Cursor session listing is unavailable");
|
|
23604
|
+
}
|
|
23605
|
+
const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
|
|
23606
|
+
if (!authMethods.some((method) => asRecord(method)?.id === AUTH_METHOD_ID3)) {
|
|
23607
|
+
return control.fail("Cursor discovery authentication is unavailable");
|
|
23608
|
+
}
|
|
23609
|
+
requestRpc("authenticate", { methodId: AUTH_METHOD_ID3 }, control);
|
|
23610
|
+
return;
|
|
23611
|
+
}
|
|
23612
|
+
if (expectedMethod === "authenticate") {
|
|
23613
|
+
requestPage(control);
|
|
23614
|
+
return;
|
|
23615
|
+
}
|
|
23616
|
+
if (expectedMethod !== "session/list")
|
|
23617
|
+
return control.fail("Cursor discovery state is invalid");
|
|
23618
|
+
const sessions = Array.isArray(result.sessions) ? result.sessions : Array.isArray(result.data) ? result.data : [];
|
|
23619
|
+
for (const value of sessions) {
|
|
23620
|
+
const session2 = asRecord(value);
|
|
23621
|
+
if (!session2)
|
|
23622
|
+
continue;
|
|
23623
|
+
collector.add({ projectPath: session2.cwd, modifiedAt: session2.updatedAt });
|
|
23624
|
+
if (collector.satisfied)
|
|
23625
|
+
return control.finish(collector.result());
|
|
23626
|
+
}
|
|
23627
|
+
const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : undefined;
|
|
23628
|
+
if (!nextCursor)
|
|
23629
|
+
return control.finish(collector.result());
|
|
23630
|
+
if (seenCursors.has(nextCursor))
|
|
23631
|
+
return control.fail("Cursor discovery cursor repeated");
|
|
23632
|
+
seenCursors.add(nextCursor);
|
|
23633
|
+
requestPage(control, nextCursor);
|
|
23634
|
+
};
|
|
23635
|
+
return runBoundedDiscoveryProcess({
|
|
23636
|
+
process: processHandle,
|
|
23637
|
+
label: "Cursor",
|
|
23638
|
+
timeoutMs: dependencies.timeoutMs ?? CURSOR_DISCOVERY_TIMEOUT_MS,
|
|
23639
|
+
outputMaxBytes: dependencies.outputMaxBytes ?? CURSOR_DISCOVERY_OUTPUT_MAX_BYTES,
|
|
23640
|
+
cleanup: dependencies.cleanup,
|
|
23641
|
+
exitEvent: "exit",
|
|
23642
|
+
onStdout: (text2, final, control) => {
|
|
23643
|
+
buffer += text2;
|
|
23644
|
+
const lines = buffer.split(`
|
|
23645
|
+
`);
|
|
23646
|
+
buffer = lines.pop() ?? "";
|
|
23647
|
+
for (const line of lines)
|
|
23648
|
+
if (line.trim())
|
|
23649
|
+
onLine(line, control);
|
|
23650
|
+
if (final && buffer.trim()) {
|
|
23651
|
+
const line = buffer;
|
|
23652
|
+
buffer = "";
|
|
23653
|
+
onLine(line, control);
|
|
23654
|
+
}
|
|
23655
|
+
},
|
|
23656
|
+
onExit: (_code, _signal, control) => control.fail("Cursor discovery process exited early"),
|
|
23657
|
+
onStart: (control) => requestRpc("initialize", {
|
|
23658
|
+
protocolVersion: ACP_PROTOCOL_VERSION3,
|
|
23659
|
+
clientCapabilities: {
|
|
23660
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
23661
|
+
terminal: false
|
|
23662
|
+
},
|
|
23663
|
+
clientInfo: { name: "alook-agent-driver-discovery", version: "0.1.31" }
|
|
23664
|
+
}, control)
|
|
23665
|
+
});
|
|
23666
|
+
}
|
|
23667
|
+
|
|
23171
23668
|
// agent-driver/dist/adapters/cursor/index.js
|
|
23172
23669
|
class CursorDriver {
|
|
23173
23670
|
catalogProbe;
|
|
@@ -23179,6 +23676,9 @@ class CursorDriver {
|
|
|
23179
23676
|
wakeStart: "immediate",
|
|
23180
23677
|
terminalOwnership: "transport_request"
|
|
23181
23678
|
};
|
|
23679
|
+
discoverRecentContext(request) {
|
|
23680
|
+
return discoverCursorRecentContext(request);
|
|
23681
|
+
}
|
|
23182
23682
|
constructor(catalogProbe = probeCursorAcpCatalog) {
|
|
23183
23683
|
this.catalogProbe = catalogProbe;
|
|
23184
23684
|
}
|
|
@@ -23673,7 +24173,7 @@ class OpenCodeServiceLane {
|
|
|
23673
24173
|
"/api/session/{sessionID}/permission/{requestID}/reply",
|
|
23674
24174
|
"/api/event"
|
|
23675
24175
|
];
|
|
23676
|
-
if (!paths || required2.some((
|
|
24176
|
+
if (!paths || required2.some((path10) => !record4(paths[path10]))) {
|
|
23677
24177
|
throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
|
|
23678
24178
|
}
|
|
23679
24179
|
}
|
|
@@ -23731,8 +24231,8 @@ class OpenCodeServiceLane {
|
|
|
23731
24231
|
const controller = new AbortController;
|
|
23732
24232
|
this.durableAbort = controller;
|
|
23733
24233
|
try {
|
|
23734
|
-
const
|
|
23735
|
-
const response = await this.openStream(
|
|
24234
|
+
const path10 = `/api/session/${encodeURIComponent(this.sessionId)}/event?after=${this.lastDurableSeq}`;
|
|
24235
|
+
const response = await this.openStream(path10, controller, "durable event stream");
|
|
23736
24236
|
await this.catchUpHistory(true);
|
|
23737
24237
|
if (!this.isIdentity(identity))
|
|
23738
24238
|
return;
|
|
@@ -23806,12 +24306,12 @@ class OpenCodeServiceLane {
|
|
|
23806
24306
|
return;
|
|
23807
24307
|
this.protocolFailure(`OpenCode ${kind} stream stopped unexpectedly`, error51);
|
|
23808
24308
|
}
|
|
23809
|
-
async openStream(
|
|
24309
|
+
async openStream(path10, lifecycle, operation) {
|
|
23810
24310
|
const deadline = new AbortController;
|
|
23811
24311
|
const timeout = setTimeout(() => deadline.abort(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS);
|
|
23812
24312
|
timeout.unref?.();
|
|
23813
24313
|
try {
|
|
23814
|
-
const response = await this.fetchFn(`${this.baseUrl}${
|
|
24314
|
+
const response = await this.fetchFn(`${this.baseUrl}${path10}`, {
|
|
23815
24315
|
method: "GET",
|
|
23816
24316
|
headers: this.headers({ accept: "text/event-stream" }),
|
|
23817
24317
|
signal: AbortSignal.any([lifecycle.signal, deadline.signal])
|
|
@@ -24238,8 +24738,8 @@ class OpenCodeServiceLane {
|
|
|
24238
24738
|
turnOwner: root.receipt
|
|
24239
24739
|
});
|
|
24240
24740
|
}
|
|
24241
|
-
async requestNoContent(
|
|
24242
|
-
const response = await this.fetchWithTimeout(
|
|
24741
|
+
async requestNoContent(path10, method, body, operation) {
|
|
24742
|
+
const response = await this.fetchWithTimeout(path10, {
|
|
24243
24743
|
method,
|
|
24244
24744
|
...body ? {
|
|
24245
24745
|
headers: { "content-type": "application/json" },
|
|
@@ -24249,7 +24749,7 @@ class OpenCodeServiceLane {
|
|
|
24249
24749
|
if (!response.ok)
|
|
24250
24750
|
throw new OpenCodeHttpError(response.status, operation);
|
|
24251
24751
|
}
|
|
24252
|
-
async fetchWithTimeout(
|
|
24752
|
+
async fetchWithTimeout(path10, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
|
|
24253
24753
|
if (this.stopping)
|
|
24254
24754
|
throw new Error("OpenCode service lane is stopping");
|
|
24255
24755
|
const controller = new AbortController;
|
|
@@ -24257,7 +24757,7 @@ class OpenCodeServiceLane {
|
|
|
24257
24757
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
24258
24758
|
timer.unref?.();
|
|
24259
24759
|
try {
|
|
24260
|
-
return await this.fetchFn(`${this.baseUrl}${
|
|
24760
|
+
return await this.fetchFn(`${this.baseUrl}${path10}`, {
|
|
24261
24761
|
...init,
|
|
24262
24762
|
headers: this.headers(init.headers),
|
|
24263
24763
|
signal: controller.signal
|
|
@@ -24269,7 +24769,7 @@ class OpenCodeServiceLane {
|
|
|
24269
24769
|
this.requestControllers.delete(controller);
|
|
24270
24770
|
}
|
|
24271
24771
|
}
|
|
24272
|
-
async fetchJsonWithTimeout(
|
|
24772
|
+
async fetchJsonWithTimeout(path10, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
|
|
24273
24773
|
if (this.stopping)
|
|
24274
24774
|
throw new OpenCodeStoppedError("OpenCode service lane is stopping");
|
|
24275
24775
|
const controller = new AbortController;
|
|
@@ -24281,7 +24781,7 @@ class OpenCodeServiceLane {
|
|
|
24281
24781
|
}, timeoutMs);
|
|
24282
24782
|
timer.unref?.();
|
|
24283
24783
|
try {
|
|
24284
|
-
const response = await this.fetchFn(`${this.baseUrl}${
|
|
24784
|
+
const response = await this.fetchFn(`${this.baseUrl}${path10}`, {
|
|
24285
24785
|
...init,
|
|
24286
24786
|
headers: this.headers(init.headers),
|
|
24287
24787
|
signal: controller.signal
|
|
@@ -24374,6 +24874,75 @@ class OpenCodeServiceLane {
|
|
|
24374
24874
|
}
|
|
24375
24875
|
}
|
|
24376
24876
|
|
|
24877
|
+
// agent-driver/dist/adapters/opencode/recent-context.js
|
|
24878
|
+
var OPENCODE_DISCOVERY_TIMEOUT_MS = 15000;
|
|
24879
|
+
var OPENCODE_DISCOVERY_OUTPUT_MAX_BYTES = 4 * 1024 * 1024;
|
|
24880
|
+
function collectCommandOutput(command, args, shell, dependencies, timeoutMs, outputMaxBytes) {
|
|
24881
|
+
const processHandle = (dependencies.spawn ?? spawnAgentProcess)(command, args, {
|
|
24882
|
+
cwd: dependencies.cwd ?? process.cwd(),
|
|
24883
|
+
env: { ...process.env, CI: "1" },
|
|
24884
|
+
shell,
|
|
24885
|
+
stdin: "ignore"
|
|
24886
|
+
});
|
|
24887
|
+
let output = "";
|
|
24888
|
+
return runBoundedDiscoveryProcess({
|
|
24889
|
+
process: processHandle,
|
|
24890
|
+
label: "OpenCode",
|
|
24891
|
+
timeoutMs,
|
|
24892
|
+
outputMaxBytes,
|
|
24893
|
+
cleanup: dependencies.cleanup,
|
|
24894
|
+
exitEvent: "close",
|
|
24895
|
+
onStdout: (text2) => {
|
|
24896
|
+
output += text2;
|
|
24897
|
+
},
|
|
24898
|
+
onExit: (code, _signal, control) => {
|
|
24899
|
+
if (code !== 0)
|
|
24900
|
+
return control.fail("OpenCode session listing failed");
|
|
24901
|
+
control.flushStdout();
|
|
24902
|
+
control.finish({ output, outputBytes: control.outputBytes });
|
|
24903
|
+
}
|
|
24904
|
+
});
|
|
24905
|
+
}
|
|
24906
|
+
async function discoverOpenCodeRecentContext(request, dependencies = {}) {
|
|
24907
|
+
const collector = new RecentContextCollector(request, "unavailable");
|
|
24908
|
+
if (collector.satisfied)
|
|
24909
|
+
return collector.result();
|
|
24910
|
+
const startedAt = Date.now();
|
|
24911
|
+
const timeoutMs = dependencies.timeoutMs ?? OPENCODE_DISCOVERY_TIMEOUT_MS;
|
|
24912
|
+
let remainingOutputBytes = dependencies.outputMaxBytes ?? OPENCODE_DISCOVERY_OUTPUT_MAX_BYTES;
|
|
24913
|
+
let maxCount = Math.max(1, Math.min(Number.MAX_SAFE_INTEGER, request.recentProjectsTopK * 10));
|
|
24914
|
+
while (!collector.satisfied) {
|
|
24915
|
+
const remainingTimeoutMs = timeoutMs - (Date.now() - startedAt);
|
|
24916
|
+
if (remainingTimeoutMs <= 0)
|
|
24917
|
+
throw new Error("OpenCode discovery timed out");
|
|
24918
|
+
const spec = resolveSpawnSpec("opencode", ["session", "list", "--format", "json", "--max-count", String(maxCount), "--pure"], request.command);
|
|
24919
|
+
const commandOutput = await collectCommandOutput(spec.command, spec.args, spec.shell, dependencies, remainingTimeoutMs, remainingOutputBytes);
|
|
24920
|
+
remainingOutputBytes -= commandOutput.outputBytes;
|
|
24921
|
+
let parsed;
|
|
24922
|
+
try {
|
|
24923
|
+
parsed = JSON.parse(commandOutput.output);
|
|
24924
|
+
} catch {
|
|
24925
|
+
throw new Error("OpenCode session listing returned invalid JSON");
|
|
24926
|
+
}
|
|
24927
|
+
if (!Array.isArray(parsed))
|
|
24928
|
+
throw new Error("OpenCode session listing returned an invalid response");
|
|
24929
|
+
for (const value of parsed) {
|
|
24930
|
+
const session2 = asRecord(value);
|
|
24931
|
+
if (!session2)
|
|
24932
|
+
continue;
|
|
24933
|
+
const modifiedAt = session2.updated ?? session2.updatedAt;
|
|
24934
|
+
collector.add({ projectPath: session2.directory, modifiedAt });
|
|
24935
|
+
}
|
|
24936
|
+
if (collector.satisfied || parsed.length < maxCount)
|
|
24937
|
+
break;
|
|
24938
|
+
const nextMaxCount = Math.min(Number.MAX_SAFE_INTEGER, maxCount * 2);
|
|
24939
|
+
if (nextMaxCount === maxCount)
|
|
24940
|
+
throw new Error("OpenCode session listing could not prove the requested Top-K");
|
|
24941
|
+
maxCount = nextMaxCount;
|
|
24942
|
+
}
|
|
24943
|
+
return collector.result();
|
|
24944
|
+
}
|
|
24945
|
+
|
|
24377
24946
|
// agent-driver/dist/adapters/opencode/index.js
|
|
24378
24947
|
function createOpenCodeMessageId() {
|
|
24379
24948
|
return `msg_${randomBytes3(16).toString("hex")}`;
|
|
@@ -24392,6 +24961,9 @@ class OpenCodeDriver {
|
|
|
24392
24961
|
constructor(outputProbe = probeCommandOutput) {
|
|
24393
24962
|
this.outputProbe = outputProbe;
|
|
24394
24963
|
}
|
|
24964
|
+
discoverRecentContext(request) {
|
|
24965
|
+
return discoverOpenCodeRecentContext(request);
|
|
24966
|
+
}
|
|
24395
24967
|
probe(command) {
|
|
24396
24968
|
const result = probeCliRuntime("opencode", {}, command);
|
|
24397
24969
|
if (result.status !== "healthy")
|
|
@@ -24438,8 +25010,8 @@ class OpenCodeDriver {
|
|
|
24438
25010
|
// agent-driver/dist/adapters/pi/index.js
|
|
24439
25011
|
import { createRequire as createRequire2 } from "module";
|
|
24440
25012
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, realpathSync } from "fs";
|
|
24441
|
-
import { homedir as
|
|
24442
|
-
import * as
|
|
25013
|
+
import { homedir as homedir4 } from "os";
|
|
25014
|
+
import * as path12 from "path";
|
|
24443
25015
|
|
|
24444
25016
|
// agent-driver/dist/controller/sdk-host.js
|
|
24445
25017
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
@@ -24554,7 +25126,7 @@ class SdkLane {
|
|
|
24554
25126
|
|
|
24555
25127
|
// agent-driver/dist/adapters/pi/sessionDeps.js
|
|
24556
25128
|
import { readFileSync as readFileSync2 } from "fs";
|
|
24557
|
-
import * as
|
|
25129
|
+
import * as path10 from "path";
|
|
24558
25130
|
import { pathToFileURL } from "url";
|
|
24559
25131
|
var PI_SDK_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
|
|
24560
25132
|
var cachedSdkPromise = null;
|
|
@@ -24563,9 +25135,9 @@ async function importPiSdkFromGlobalInstall() {
|
|
|
24563
25135
|
if (!dir) {
|
|
24564
25136
|
throw new Error(`${PI_SDK_PACKAGE_NAME} not found — install it (e.g. \`npm install -g ${PI_SDK_PACKAGE_NAME}\`) before launching a pi agent`);
|
|
24565
25137
|
}
|
|
24566
|
-
const pkg = JSON.parse(readFileSync2(
|
|
25138
|
+
const pkg = JSON.parse(readFileSync2(path10.join(dir, "package.json"), "utf-8"));
|
|
24567
25139
|
const entry = pkg.exports?.["."]?.import ?? pkg.main ?? "./dist/index.js";
|
|
24568
|
-
const entryPath =
|
|
25140
|
+
const entryPath = path10.join(dir, entry);
|
|
24569
25141
|
const barrel = await import(pathToFileURL(entryPath).href);
|
|
24570
25142
|
return withSessionDirHelper(barrel, entryPath);
|
|
24571
25143
|
}
|
|
@@ -24573,7 +25145,7 @@ async function withSessionDirHelper(barrel, entryPath) {
|
|
|
24573
25145
|
if (typeof barrel.getDefaultSessionDir === "function")
|
|
24574
25146
|
return barrel;
|
|
24575
25147
|
try {
|
|
24576
|
-
const deepPath =
|
|
25148
|
+
const deepPath = path10.join(path10.dirname(entryPath), "core", "session-manager.js");
|
|
24577
25149
|
const deep = await import(pathToFileURL(deepPath).href);
|
|
24578
25150
|
if (typeof deep.getDefaultSessionDir !== "function")
|
|
24579
25151
|
return barrel;
|
|
@@ -24653,6 +25225,51 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
|
|
|
24653
25225
|
};
|
|
24654
25226
|
}
|
|
24655
25227
|
|
|
25228
|
+
// agent-driver/dist/adapters/pi/recent-context.js
|
|
25229
|
+
import { homedir as homedir3 } from "node:os";
|
|
25230
|
+
import path11 from "node:path";
|
|
25231
|
+
function expandTildePath(value) {
|
|
25232
|
+
if (value === "~")
|
|
25233
|
+
return homedir3();
|
|
25234
|
+
if (value.startsWith("~/"))
|
|
25235
|
+
return path11.join(homedir3(), value.slice(2));
|
|
25236
|
+
return value;
|
|
25237
|
+
}
|
|
25238
|
+
async function discoverPiRecentContext(request, dependencies = {}) {
|
|
25239
|
+
const collector = new RecentContextCollector(request, "supported");
|
|
25240
|
+
if (collector.satisfied)
|
|
25241
|
+
return collector.result();
|
|
25242
|
+
const customSessionDirectory = process.env.PI_CODING_AGENT_SESSION_DIR;
|
|
25243
|
+
const sessionsRoot = path11.resolve(expandTildePath(dependencies.sessionsRoot ?? customSessionDirectory ?? path11.join(process.env.PI_CODING_AGENT_DIR || path11.join(homedir3(), ".pi", "agent"), "sessions")));
|
|
25244
|
+
const scanDirectSessions = dependencies.sessionsRoot == null && customSessionDirectory != null;
|
|
25245
|
+
const readHeader = dependencies.readHeader ?? readFirstLine;
|
|
25246
|
+
const candidates = await collectJsonlFileCandidates(sessionsRoot, {
|
|
25247
|
+
includeRootFiles: scanDirectSessions,
|
|
25248
|
+
maxDepth: 1,
|
|
25249
|
+
readDirectory: dependencies.readDirectory,
|
|
25250
|
+
statPath: dependencies.statPath
|
|
25251
|
+
});
|
|
25252
|
+
candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
|
|
25253
|
+
for (const candidate of candidates) {
|
|
25254
|
+
const line = await readHeader(candidate.filePath);
|
|
25255
|
+
if (!line)
|
|
25256
|
+
continue;
|
|
25257
|
+
const header = tryParseJsonRecord(line);
|
|
25258
|
+
if (!header)
|
|
25259
|
+
continue;
|
|
25260
|
+
if (header.type !== "session" || header.parentSession != null)
|
|
25261
|
+
continue;
|
|
25262
|
+
collector.add({
|
|
25263
|
+
sessionFilePath: candidate.filePath,
|
|
25264
|
+
projectPath: header.cwd,
|
|
25265
|
+
modifiedAt: candidate.modifiedAt
|
|
25266
|
+
});
|
|
25267
|
+
if (collector.satisfied)
|
|
25268
|
+
break;
|
|
25269
|
+
}
|
|
25270
|
+
return collector.result();
|
|
25271
|
+
}
|
|
25272
|
+
|
|
24656
25273
|
// agent-driver/dist/adapters/pi/index.js
|
|
24657
25274
|
var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
|
|
24658
25275
|
var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
|
|
@@ -24671,15 +25288,15 @@ function resolvePiSdkPackageDir(deps = {}) {
|
|
|
24671
25288
|
if (!binPath)
|
|
24672
25289
|
return;
|
|
24673
25290
|
try {
|
|
24674
|
-
let dir =
|
|
25291
|
+
let dir = path12.dirname(realpathSync(binPath));
|
|
24675
25292
|
const MAX_DEPTH = 8;
|
|
24676
25293
|
for (let i = 0;i < MAX_DEPTH; i++) {
|
|
24677
|
-
if (isPiSdkPackageJson(
|
|
25294
|
+
if (isPiSdkPackageJson(path12.join(dir, "package.json")))
|
|
24678
25295
|
return dir;
|
|
24679
|
-
const siblingDir =
|
|
24680
|
-
if (isPiSdkPackageJson(
|
|
25296
|
+
const siblingDir = path12.join(dir, "node_modules", PI_SDK_PACKAGE_NAME2);
|
|
25297
|
+
if (isPiSdkPackageJson(path12.join(siblingDir, "package.json")))
|
|
24681
25298
|
return siblingDir;
|
|
24682
|
-
const parent =
|
|
25299
|
+
const parent = path12.dirname(dir);
|
|
24683
25300
|
if (parent === dir)
|
|
24684
25301
|
break;
|
|
24685
25302
|
dir = parent;
|
|
@@ -24696,21 +25313,21 @@ function findPiSessionFile(sessionDir, sessionId) {
|
|
|
24696
25313
|
}
|
|
24697
25314
|
const suffix = `_${sessionId}.jsonl`;
|
|
24698
25315
|
const match = entries.find((entry) => entry.endsWith(suffix));
|
|
24699
|
-
return match ?
|
|
25316
|
+
return match ? path12.join(sessionDir, match) : null;
|
|
24700
25317
|
}
|
|
24701
25318
|
function resolvePiSessionDir(sdk, cwd) {
|
|
24702
25319
|
if (typeof sdk.getDefaultSessionDir === "function")
|
|
24703
25320
|
return sdk.getDefaultSessionDir(cwd);
|
|
24704
|
-
const agentDir = typeof sdk.getAgentDir === "function" ? sdk.getAgentDir() :
|
|
24705
|
-
const encoded = `--${
|
|
24706
|
-
return
|
|
25321
|
+
const agentDir = typeof sdk.getAgentDir === "function" ? sdk.getAgentDir() : path12.join(homedir4(), ".pi", "agent");
|
|
25322
|
+
const encoded = `--${path12.resolve(cwd).replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
25323
|
+
return path12.join(agentDir, "sessions", encoded);
|
|
24707
25324
|
}
|
|
24708
25325
|
function resolvePiSdkVersionFromPath(deps = {}) {
|
|
24709
25326
|
const dir = resolvePiSdkPackageDir(deps);
|
|
24710
25327
|
if (!dir)
|
|
24711
25328
|
return;
|
|
24712
25329
|
try {
|
|
24713
|
-
const pkg = JSON.parse(readFileSync3(
|
|
25330
|
+
const pkg = JSON.parse(readFileSync3(path12.join(dir, "package.json"), "utf-8"));
|
|
24714
25331
|
return pkg.version;
|
|
24715
25332
|
} catch {
|
|
24716
25333
|
return;
|
|
@@ -24814,6 +25431,9 @@ class PiDriver {
|
|
|
24814
25431
|
};
|
|
24815
25432
|
sessionId = null;
|
|
24816
25433
|
terminalSequence = 0;
|
|
25434
|
+
discoverRecentContext(request) {
|
|
25435
|
+
return discoverPiRecentContext(request);
|
|
25436
|
+
}
|
|
24817
25437
|
constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
|
|
24818
25438
|
this.dependenciesFor = dependenciesFor;
|
|
24819
25439
|
this.loadSdk = loadSdk;
|
|
@@ -24959,6 +25579,9 @@ function assertAdapterCompatibility(registrationId, registeredCapabilities, adap
|
|
|
24959
25579
|
if (typeof candidate[method] !== "function")
|
|
24960
25580
|
throw new Error(`Adapter ${registrationId} is missing ${method}()`);
|
|
24961
25581
|
}
|
|
25582
|
+
if (candidate.discoverRecentContext !== undefined && typeof candidate.discoverRecentContext !== "function") {
|
|
25583
|
+
throw new Error(`Adapter ${registrationId} has an invalid discoverRecentContext()`);
|
|
25584
|
+
}
|
|
24962
25585
|
const instruction = candidate.instructionDelivery;
|
|
24963
25586
|
if (!instruction || instruction.kind !== "native" && instruction.kind !== "workspace_file") {
|
|
24964
25587
|
throw new Error(`Adapter ${registrationId} has an invalid instructionDelivery declaration`);
|
|
@@ -25180,8 +25803,8 @@ function ensureSymlinks(workDir, canonical = CANONICAL_FILE, aliases = SYMLINK_A
|
|
|
25180
25803
|
continue;
|
|
25181
25804
|
const aliasPath = join8(workDir, alias2);
|
|
25182
25805
|
try {
|
|
25183
|
-
const
|
|
25184
|
-
if (
|
|
25806
|
+
const stat2 = lstatSync(aliasPath);
|
|
25807
|
+
if (stat2.isSymbolicLink()) {
|
|
25185
25808
|
const target2 = readlinkSync(aliasPath);
|
|
25186
25809
|
if (target2 === canonical)
|
|
25187
25810
|
continue;
|
|
@@ -25344,6 +25967,7 @@ class LogicalAgentSession {
|
|
|
25344
25967
|
steeringDeliveries = new Set;
|
|
25345
25968
|
laneAdmissionTail = Promise.resolve();
|
|
25346
25969
|
laneAdmissionPending = 0;
|
|
25970
|
+
toolBoundaryOpen = true;
|
|
25347
25971
|
toolBoundaryFlushDisabled = false;
|
|
25348
25972
|
safeBoundaryFlush;
|
|
25349
25973
|
safeBoundaryDelivery;
|
|
@@ -25624,6 +26248,8 @@ class LogicalAgentSession {
|
|
|
25624
26248
|
this.turnError = undefined;
|
|
25625
26249
|
this.interruptedTurnId = undefined;
|
|
25626
26250
|
this.processTurnEnded = false;
|
|
26251
|
+
this.toolBoundaryOpen = true;
|
|
26252
|
+
this.toolBoundaryFlushDisabled = false;
|
|
25627
26253
|
this.state = "starting";
|
|
25628
26254
|
const generation = this.lifecycleGeneration;
|
|
25629
26255
|
const text2 = messages.map((message2) => message2.text).join(`
|
|
@@ -25856,6 +26482,7 @@ class LogicalAgentSession {
|
|
|
25856
26482
|
}
|
|
25857
26483
|
if (!this.startToolUse(event.callId))
|
|
25858
26484
|
return;
|
|
26485
|
+
this.toolBoundaryOpen = false;
|
|
25859
26486
|
const startedCallId = this.toolCallId(event.callId);
|
|
25860
26487
|
this.emit({
|
|
25861
26488
|
type: "tool_started",
|
|
@@ -25872,6 +26499,7 @@ class LogicalAgentSession {
|
|
|
25872
26499
|
}
|
|
25873
26500
|
if (!this.finishToolUse(event.callId))
|
|
25874
26501
|
return;
|
|
26502
|
+
this.toolBoundaryOpen = true;
|
|
25875
26503
|
const finishedCallId = this.toolCallId(event.callId);
|
|
25876
26504
|
this.emit({
|
|
25877
26505
|
type: "tool_finished",
|
|
@@ -25879,7 +26507,7 @@ class LogicalAgentSession {
|
|
|
25879
26507
|
...finishedCallId ? { callId: finishedCallId } : {},
|
|
25880
26508
|
name: event.name
|
|
25881
26509
|
});
|
|
25882
|
-
if (
|
|
26510
|
+
if (!this.toolBoundaryFlushDisabled)
|
|
25883
26511
|
this.flushSafeBoundaryQueue();
|
|
25884
26512
|
return;
|
|
25885
26513
|
case "compaction_started":
|
|
@@ -26025,6 +26653,7 @@ class LogicalAgentSession {
|
|
|
26025
26653
|
this.clearOutstandingToolUses();
|
|
26026
26654
|
this.compacting = false;
|
|
26027
26655
|
this.reviewing = false;
|
|
26656
|
+
this.toolBoundaryOpen = true;
|
|
26028
26657
|
this.toolBoundaryFlushDisabled = false;
|
|
26029
26658
|
this.state = "idle";
|
|
26030
26659
|
if (this.adapter.execution.lifetime === "turn") {
|
|
@@ -26046,7 +26675,7 @@ class LogicalAgentSession {
|
|
|
26046
26675
|
return this.safeBoundaryFlush;
|
|
26047
26676
|
}
|
|
26048
26677
|
canFlushSafeBoundaryQueue() {
|
|
26049
|
-
return this.state === "working" && this.behavior.midTurnDelivery === "safe_boundary_queue" && this.
|
|
26678
|
+
return this.state === "working" && this.behavior.midTurnDelivery === "safe_boundary_queue" && this.toolBoundaryOpen && !this.toolBoundaryFlushDisabled && !this.compacting && !this.reviewing && this.activeTurn !== undefined && this.lane !== null && this.queued.length > 0;
|
|
26050
26679
|
}
|
|
26051
26680
|
async drainSafeBoundaryQueue() {
|
|
26052
26681
|
while (this.canFlushSafeBoundaryQueue()) {
|
|
@@ -26561,6 +27190,57 @@ function createAgentDriverSdkWithRegistry(options) {
|
|
|
26561
27190
|
const registry2 = options.registry;
|
|
26562
27191
|
return {
|
|
26563
27192
|
backendIds: registry2.backendIds,
|
|
27193
|
+
async discoverRecentContext(input) {
|
|
27194
|
+
if (!isValidRecentContextTopK(input.recentSessionFilesTopK) || !isValidRecentContextTopK(input.recentProjectsTopK)) {
|
|
27195
|
+
return {
|
|
27196
|
+
ok: false,
|
|
27197
|
+
error: {
|
|
27198
|
+
category: "configuration",
|
|
27199
|
+
code: "invalid_recent_context_top_k",
|
|
27200
|
+
message: "Recent-context Top-K values must be non-negative safe integers",
|
|
27201
|
+
retryable: false
|
|
27202
|
+
}
|
|
27203
|
+
};
|
|
27204
|
+
}
|
|
27205
|
+
const registration = registry2.get(input.backend);
|
|
27206
|
+
try {
|
|
27207
|
+
assertRegistrationShape(registration);
|
|
27208
|
+
const adapter = registration.createAdapter();
|
|
27209
|
+
assertAdapterCompatibility(String(registration.id), registration.capabilities, adapter);
|
|
27210
|
+
if (!adapter.discoverRecentContext) {
|
|
27211
|
+
return {
|
|
27212
|
+
ok: false,
|
|
27213
|
+
error: {
|
|
27214
|
+
category: "configuration",
|
|
27215
|
+
code: "recent_context_discovery_unsupported",
|
|
27216
|
+
message: `Backend ${input.backend} does not support recent-context discovery`,
|
|
27217
|
+
retryable: false
|
|
27218
|
+
}
|
|
27219
|
+
};
|
|
27220
|
+
}
|
|
27221
|
+
const command = registration.capabilities.commandOverride ? input.command : undefined;
|
|
27222
|
+
const discovered = await adapter.discoverRecentContext({
|
|
27223
|
+
recentSessionFilesTopK: input.recentSessionFilesTopK,
|
|
27224
|
+
recentProjectsTopK: input.recentProjectsTopK,
|
|
27225
|
+
...command ? { command } : {}
|
|
27226
|
+
});
|
|
27227
|
+
const sanitized = sanitizeRecentContextData(discovered, input);
|
|
27228
|
+
if (!sanitized)
|
|
27229
|
+
throw new Error(`Adapter ${input.backend} returned invalid recent-context data`);
|
|
27230
|
+
return { ok: true, ...sanitized };
|
|
27231
|
+
} catch (error51) {
|
|
27232
|
+
const contractInvalid = error51 instanceof Error && (error51.message.startsWith("Adapter ") || error51.message.startsWith("Agent backend registration "));
|
|
27233
|
+
return {
|
|
27234
|
+
ok: false,
|
|
27235
|
+
error: {
|
|
27236
|
+
category: contractInvalid ? "internal" : "runtime_unavailable",
|
|
27237
|
+
code: contractInvalid ? "adapter_contract_invalid" : "recent_context_discovery_failed",
|
|
27238
|
+
message: contractInvalid ? `Backend ${input.backend} adapter contract is invalid` : `Backend ${input.backend} recent-context discovery failed`,
|
|
27239
|
+
retryable: !contractInvalid
|
|
27240
|
+
}
|
|
27241
|
+
};
|
|
27242
|
+
}
|
|
27243
|
+
},
|
|
26564
27244
|
async probe(input) {
|
|
26565
27245
|
const registration = registry2.get(input.backend);
|
|
26566
27246
|
const capabilities2 = registration.capabilities;
|
|
@@ -26699,22 +27379,22 @@ function listRuntimeIds() {
|
|
|
26699
27379
|
|
|
26700
27380
|
// src/discovery.ts
|
|
26701
27381
|
function resolveAlookCliPath(moduleDir) {
|
|
26702
|
-
const thisDir = moduleDir ??
|
|
26703
|
-
const target2 =
|
|
27382
|
+
const thisDir = moduleDir ?? path13.dirname(fileURLToPath(import.meta.url));
|
|
27383
|
+
const target2 = path13.basename(thisDir) === "dist" ? path13.resolve(thisDir, "cli", "index.js") : path13.resolve(thisDir, "..", "scripts", "alook-shim.mjs");
|
|
26704
27384
|
return fs6.existsSync(target2) ? target2 : null;
|
|
26705
27385
|
}
|
|
26706
27386
|
function deriveCliFallbackCandidates(cliPath) {
|
|
26707
27387
|
if (!cliPath)
|
|
26708
27388
|
return [];
|
|
26709
|
-
const normalized = cliPath.split(
|
|
27389
|
+
const normalized = cliPath.split(path13.sep).join("/");
|
|
26710
27390
|
const marker = "/node_modules/";
|
|
26711
27391
|
const idx = normalized.indexOf(marker);
|
|
26712
27392
|
if (idx === -1)
|
|
26713
27393
|
return [];
|
|
26714
27394
|
const globalRoot = cliPath.slice(0, idx + marker.length - 1);
|
|
26715
|
-
const tail =
|
|
27395
|
+
const tail = path13.join("dist", "cli", "index.js");
|
|
26716
27396
|
return [
|
|
26717
|
-
|
|
27397
|
+
path13.join(globalRoot, "@alook", "daemon", tail)
|
|
26718
27398
|
].filter((candidate) => candidate !== cliPath);
|
|
26719
27399
|
}
|
|
26720
27400
|
function resolveAlookCliPathWithFallback(primary) {
|
|
@@ -26856,11 +27536,11 @@ function readDaemonVersion() {
|
|
|
26856
27536
|
|
|
26857
27537
|
// src/cli/daemonRunner.ts
|
|
26858
27538
|
import * as fs11 from "node:fs";
|
|
26859
|
-
import * as
|
|
27539
|
+
import * as path16 from "node:path";
|
|
26860
27540
|
import { WebSocket } from "ws";
|
|
26861
27541
|
|
|
26862
27542
|
// src/daemon/createDaemon.ts
|
|
26863
|
-
import { homedir as
|
|
27543
|
+
import { homedir as homedir7 } from "os";
|
|
26864
27544
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
|
|
26865
27545
|
|
|
26866
27546
|
// src/util/rotatingFileSink.ts
|
|
@@ -26877,7 +27557,7 @@ import {
|
|
|
26877
27557
|
statSync,
|
|
26878
27558
|
unlinkSync as unlinkSync3
|
|
26879
27559
|
} from "node:fs";
|
|
26880
|
-
function createRotatingFileSink(
|
|
27560
|
+
function createRotatingFileSink(path14, maxBytes, opts = {}) {
|
|
26881
27561
|
const report = (operation, error51) => {
|
|
26882
27562
|
try {
|
|
26883
27563
|
opts.onError?.({ operation, error: error51 });
|
|
@@ -26887,8 +27567,8 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26887
27567
|
if (!existsSync6(filePath))
|
|
26888
27568
|
return true;
|
|
26889
27569
|
try {
|
|
26890
|
-
const
|
|
26891
|
-
if (!
|
|
27570
|
+
const stat2 = lstatSync2(filePath);
|
|
27571
|
+
if (!stat2.isFile()) {
|
|
26892
27572
|
if (opts.mode === undefined && !opts.hardMaxBytes)
|
|
26893
27573
|
return true;
|
|
26894
27574
|
report("unsafe_generation", new Error("log generation is not a regular file"));
|
|
@@ -26896,7 +27576,7 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26896
27576
|
}
|
|
26897
27577
|
if (opts.mode !== undefined)
|
|
26898
27578
|
chmodSync(filePath, opts.mode);
|
|
26899
|
-
if (opts.hardMaxBytes && maxBytes > 0 &&
|
|
27579
|
+
if (opts.hardMaxBytes && maxBytes > 0 && stat2.size > maxBytes) {
|
|
26900
27580
|
try {
|
|
26901
27581
|
unlinkSync3(filePath);
|
|
26902
27582
|
} catch (error51) {
|
|
@@ -26913,9 +27593,9 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26913
27593
|
};
|
|
26914
27594
|
const rotate = () => {
|
|
26915
27595
|
try {
|
|
26916
|
-
renameSync2(
|
|
27596
|
+
renameSync2(path14, `${path14}.1`);
|
|
26917
27597
|
if (opts.mode !== undefined)
|
|
26918
|
-
chmodSync(`${
|
|
27598
|
+
chmodSync(`${path14}.1`, opts.mode);
|
|
26919
27599
|
return true;
|
|
26920
27600
|
} catch (error51) {
|
|
26921
27601
|
report("rotate", error51);
|
|
@@ -26924,7 +27604,7 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26924
27604
|
};
|
|
26925
27605
|
const currentSize = () => {
|
|
26926
27606
|
try {
|
|
26927
|
-
return existsSync6(
|
|
27607
|
+
return existsSync6(path14) ? statSync(path14).size : 0;
|
|
26928
27608
|
} catch (error51) {
|
|
26929
27609
|
report("stat", error51);
|
|
26930
27610
|
return null;
|
|
@@ -26932,7 +27612,7 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26932
27612
|
};
|
|
26933
27613
|
const sink = {
|
|
26934
27614
|
secure() {
|
|
26935
|
-
return secureGeneration(`${
|
|
27615
|
+
return secureGeneration(`${path14}.1`) && secureGeneration(path14);
|
|
26936
27616
|
},
|
|
26937
27617
|
write(line) {
|
|
26938
27618
|
try {
|
|
@@ -26952,7 +27632,7 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26952
27632
|
const shouldRotate = maxBytes > 0 && (opts.hardMaxBytes ? currentBytes > 0 && currentBytes + serializedBytes > maxBytes : currentBytes >= maxBytes);
|
|
26953
27633
|
if (shouldRotate && !rotate() && opts.hardMaxBytes)
|
|
26954
27634
|
return;
|
|
26955
|
-
appendFileSync(
|
|
27635
|
+
appendFileSync(path14, serialized, opts.mode === undefined ? undefined : { mode: opts.mode });
|
|
26956
27636
|
} catch (error51) {
|
|
26957
27637
|
report("append", error51);
|
|
26958
27638
|
}
|
|
@@ -26960,7 +27640,7 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26960
27640
|
openSnapshot() {
|
|
26961
27641
|
const files = [];
|
|
26962
27642
|
try {
|
|
26963
|
-
for (const candidate of [`${
|
|
27643
|
+
for (const candidate of [`${path14}.1`, path14]) {
|
|
26964
27644
|
if (!existsSync6(candidate))
|
|
26965
27645
|
continue;
|
|
26966
27646
|
if (!secureGeneration(candidate))
|
|
@@ -26968,10 +27648,10 @@ function createRotatingFileSink(path10, maxBytes, opts = {}) {
|
|
|
26968
27648
|
const noFollow = "O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0;
|
|
26969
27649
|
const fd = openSync(candidate, constants.O_RDONLY | noFollow);
|
|
26970
27650
|
try {
|
|
26971
|
-
const
|
|
26972
|
-
if (!
|
|
27651
|
+
const stat2 = fstatSync(fd);
|
|
27652
|
+
if (!stat2.isFile())
|
|
26973
27653
|
throw new Error("snapshot source is not a regular file");
|
|
26974
|
-
files.push({ path: candidate, fd, size:
|
|
27654
|
+
files.push({ path: candidate, fd, size: stat2.size });
|
|
26975
27655
|
} catch (error51) {
|
|
26976
27656
|
closeSync(fd);
|
|
26977
27657
|
throw error51;
|
|
@@ -27121,11 +27801,11 @@ function createTraceSampler(emit, throttleMs = DEFAULT_TRACE_SAMPLE_MS) {
|
|
|
27121
27801
|
|
|
27122
27802
|
// src/util/statusFile.ts
|
|
27123
27803
|
import { writeFileSync as writeFileSync4, renameSync as renameSync3 } from "node:fs";
|
|
27124
|
-
function writeStatusFile(
|
|
27804
|
+
function writeStatusFile(path14, snapshot) {
|
|
27125
27805
|
try {
|
|
27126
|
-
const tmp = `${
|
|
27806
|
+
const tmp = `${path14}.tmp`;
|
|
27127
27807
|
writeFileSync4(tmp, JSON.stringify(snapshot));
|
|
27128
|
-
renameSync3(tmp,
|
|
27808
|
+
renameSync3(tmp, path14);
|
|
27129
27809
|
} catch {}
|
|
27130
27810
|
}
|
|
27131
27811
|
|
|
@@ -27807,7 +28487,7 @@ import * as fs7 from "fs";
|
|
|
27807
28487
|
import * as http from "http";
|
|
27808
28488
|
import * as https from "https";
|
|
27809
28489
|
import * as os2 from "os";
|
|
27810
|
-
import * as
|
|
28490
|
+
import * as path14 from "path";
|
|
27811
28491
|
import { URL as URL2 } from "url";
|
|
27812
28492
|
var LOCAL_MESSAGE_REMINDER_PATH = "/__alook/local/message-reminder";
|
|
27813
28493
|
var LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES = 4 * 1024;
|
|
@@ -27839,15 +28519,15 @@ class CredentialBroker {
|
|
|
27839
28519
|
this.voucherPrefix = config2.voucherPrefix ?? "vch_";
|
|
27840
28520
|
this.clientLabel = config2.clientLabel ?? "cli";
|
|
27841
28521
|
this.headerNames = config2.headerNames ?? DEFAULT_HEADER_NAMES;
|
|
27842
|
-
this.voucherDir = config2.voucherDir ??
|
|
28522
|
+
this.voucherDir = config2.voucherDir ?? path14.join(os2.tmpdir(), "agent-vouchers");
|
|
27843
28523
|
}
|
|
27844
28524
|
mint(agentId, launchId, capabilities2, runnerKey) {
|
|
27845
28525
|
if (!runnerKey)
|
|
27846
28526
|
throw new Error("CredentialBroker.mint: runnerKey is required (per-agent tier-2 credential)");
|
|
27847
28527
|
const voucher = randomVoucher(this.voucherPrefix);
|
|
27848
|
-
const dir =
|
|
28528
|
+
const dir = path14.join(this.voucherDir, sanitizeIdSegment(agentId));
|
|
27849
28529
|
fs7.mkdirSync(dir, { recursive: true });
|
|
27850
|
-
const voucherFile =
|
|
28530
|
+
const voucherFile = path14.join(dir, `${sanitizeIdSegment(launchId)}.token`);
|
|
27851
28531
|
fs7.writeFileSync(voucherFile, voucher, { mode: 384 });
|
|
27852
28532
|
this.registrations.set(voucher, {
|
|
27853
28533
|
agentId,
|
|
@@ -28915,7 +29595,7 @@ function runtimeModelName(config2) {
|
|
|
28915
29595
|
// agent-driver/dist/provider-quota.js
|
|
28916
29596
|
import { execFile } from "node:child_process";
|
|
28917
29597
|
import { readFile } from "node:fs/promises";
|
|
28918
|
-
import { homedir as
|
|
29598
|
+
import { homedir as homedir5 } from "node:os";
|
|
28919
29599
|
import { join as join11 } from "node:path";
|
|
28920
29600
|
import { promisify } from "node:util";
|
|
28921
29601
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
@@ -28944,7 +29624,7 @@ async function claudeCredentials(options) {
|
|
|
28944
29624
|
} catch {}
|
|
28945
29625
|
}
|
|
28946
29626
|
const env = options.env ?? process.env;
|
|
28947
|
-
const root = env.CLAUDE_CONFIG_DIR || join11(options.home ??
|
|
29627
|
+
const root = env.CLAUDE_CONFIG_DIR || join11(options.home ?? homedir5(), ".claude");
|
|
28948
29628
|
try {
|
|
28949
29629
|
const value = options.readCredentialsFile ? await options.readCredentialsFile(join11(root, ".credentials.json")) : await readFile(join11(root, ".credentials.json"), "utf8");
|
|
28950
29630
|
return parseCredentials(value);
|
|
@@ -29946,7 +30626,19 @@ class AgentProcessManager {
|
|
|
29946
30626
|
const session2 = this.sessions.get(agentId);
|
|
29947
30627
|
if (!session2)
|
|
29948
30628
|
return;
|
|
29949
|
-
await session2.interrupt({ requestId: randomUUID6(), reason: "owner_request" });
|
|
30629
|
+
const result = await session2.interrupt({ requestId: randomUUID6(), reason: "owner_request" });
|
|
30630
|
+
if (result.status !== "accepted")
|
|
30631
|
+
return;
|
|
30632
|
+
this.log.info("agent turn interrupt accepted", {
|
|
30633
|
+
agentId,
|
|
30634
|
+
sessionInstanceId: session2.sessionInstanceId,
|
|
30635
|
+
turnId: result.turnId
|
|
30636
|
+
});
|
|
30637
|
+
try {
|
|
30638
|
+
this.opts.onBotAuditEvent?.(agentId, { kind: "turn_interrupt", payload: { status: "accepted" } }, this.auditContext(agentId));
|
|
30639
|
+
} catch (err) {
|
|
30640
|
+
this.log.debug("audit emit failed (turn interrupt)", { agentId, err: String(err) });
|
|
30641
|
+
}
|
|
29950
30642
|
}
|
|
29951
30643
|
async stopAll() {
|
|
29952
30644
|
if (this.tickTimer) {
|
|
@@ -30288,6 +30980,10 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
30288
30980
|
sent = session2.send(input);
|
|
30289
30981
|
if (exactOwner)
|
|
30290
30982
|
this.markPendingDeliveryObserved(exactOwner, input.id);
|
|
30983
|
+
this.log.info("steering message submitted to logical session", {
|
|
30984
|
+
agentId: effect.agentId,
|
|
30985
|
+
mode: effect.mode
|
|
30986
|
+
});
|
|
30291
30987
|
} catch (error51) {
|
|
30292
30988
|
if (exactOwner) {
|
|
30293
30989
|
this.settlePendingDelivery(exactOwner, input.id);
|
|
@@ -30333,7 +31029,6 @@ ${this.opts.wakePromptFooter}` : text2;
|
|
|
30333
31029
|
mode: effect.mode
|
|
30334
31030
|
});
|
|
30335
31031
|
}
|
|
30336
|
-
this.log.info("steering message sent to running agent", { agentId: effect.agentId, mode: effect.mode });
|
|
30337
31032
|
break;
|
|
30338
31033
|
}
|
|
30339
31034
|
case "stop":
|
|
@@ -31379,6 +32074,26 @@ class AgentRouter {
|
|
|
31379
32074
|
return;
|
|
31380
32075
|
}
|
|
31381
32076
|
break;
|
|
32077
|
+
case "agent:event":
|
|
32078
|
+
this.log.info("agent:event received", { agentId: cmd.agentId });
|
|
32079
|
+
try {
|
|
32080
|
+
await this.opts.onBeforeAgent?.(cmd.agentId);
|
|
32081
|
+
this.opts.manager.register(cmd.agentId, {
|
|
32082
|
+
runtimeConfig: cmd.config,
|
|
32083
|
+
launchId: cmd.launchId
|
|
32084
|
+
});
|
|
32085
|
+
this.running.add(cmd.agentId);
|
|
32086
|
+
this.opts.manager.deliver(cmd.agentId, {
|
|
32087
|
+
id: `${cmd.agentId}:event:${cmd.launchId}`,
|
|
32088
|
+
text: cmd.prompt
|
|
32089
|
+
});
|
|
32090
|
+
} catch (err) {
|
|
32091
|
+
this.log.warn("agent:event failed", {
|
|
32092
|
+
agentId: cmd.agentId,
|
|
32093
|
+
err: err instanceof Error ? err.message : String(err)
|
|
32094
|
+
});
|
|
32095
|
+
}
|
|
32096
|
+
break;
|
|
31382
32097
|
case "agent:reset":
|
|
31383
32098
|
await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:reset", () => this.opts.manager.resetSession(cmd.agentId, {
|
|
31384
32099
|
runtimeConfig: cmd.config,
|
|
@@ -31609,11 +32324,11 @@ function timelineRowHash(line) {
|
|
|
31609
32324
|
}
|
|
31610
32325
|
function timelineFileGeneration(filePath, lines) {
|
|
31611
32326
|
try {
|
|
31612
|
-
const
|
|
31613
|
-
if (!
|
|
32327
|
+
const stat2 = fs9.lstatSync(filePath, { bigint: true });
|
|
32328
|
+
if (!stat2.isFile())
|
|
31614
32329
|
return null;
|
|
31615
32330
|
const digest = createHash2("sha256");
|
|
31616
|
-
digest.update(`${
|
|
32331
|
+
digest.update(`${stat2.dev}:${stat2.ino}:${stat2.size}:${stat2.mtimeNs}:${stat2.ctimeNs}
|
|
31617
32332
|
`);
|
|
31618
32333
|
for (const line of lines)
|
|
31619
32334
|
digest.update(line.text, "utf8").update(`
|
|
@@ -31671,9 +32386,9 @@ function compactLines(input) {
|
|
|
31671
32386
|
}
|
|
31672
32387
|
return suffix;
|
|
31673
32388
|
}
|
|
31674
|
-
function isRealDirectory(
|
|
32389
|
+
function isRealDirectory(path15) {
|
|
31675
32390
|
try {
|
|
31676
|
-
return fs9.lstatSync(
|
|
32391
|
+
return fs9.lstatSync(path15).isDirectory();
|
|
31677
32392
|
} catch {
|
|
31678
32393
|
return false;
|
|
31679
32394
|
}
|
|
@@ -31713,8 +32428,8 @@ function scanTimelineFile(filePath) {
|
|
|
31713
32428
|
let fd = null;
|
|
31714
32429
|
try {
|
|
31715
32430
|
fd = fs9.openSync(filePath, fs9.constants.O_RDONLY | (fs9.constants.O_NOFOLLOW ?? 0));
|
|
31716
|
-
const
|
|
31717
|
-
if (!
|
|
32431
|
+
const stat2 = fs9.fstatSync(fd);
|
|
32432
|
+
if (!stat2.isFile())
|
|
31718
32433
|
return null;
|
|
31719
32434
|
const chunk2 = Buffer.allocUnsafe(TIMELINE_READ_CHUNK_BYTES);
|
|
31720
32435
|
const newest = [];
|
|
@@ -31727,9 +32442,9 @@ function scanTimelineFile(filePath) {
|
|
|
31727
32442
|
let oversized = false;
|
|
31728
32443
|
let stop = false;
|
|
31729
32444
|
let discardIncompleteTail = false;
|
|
31730
|
-
if (
|
|
32445
|
+
if (stat2.size > 0) {
|
|
31731
32446
|
const last = Buffer.allocUnsafe(1);
|
|
31732
|
-
fs9.readSync(fd, last, 0, 1,
|
|
32447
|
+
fs9.readSync(fd, last, 0, 1, stat2.size - 1);
|
|
31733
32448
|
discardIncompleteTail = last[0] !== 10;
|
|
31734
32449
|
}
|
|
31735
32450
|
const resetPhysicalLine = () => {
|
|
@@ -31788,7 +32503,7 @@ function scanTimelineFile(filePath) {
|
|
|
31788
32503
|
}
|
|
31789
32504
|
resetPhysicalLine();
|
|
31790
32505
|
};
|
|
31791
|
-
let position =
|
|
32506
|
+
let position = stat2.size;
|
|
31792
32507
|
while (position > 0 && !stop) {
|
|
31793
32508
|
const start = Math.max(0, position - chunk2.length);
|
|
31794
32509
|
const requested = position - start;
|
|
@@ -31940,11 +32655,11 @@ function readResumeControlState(timelineDir) {
|
|
|
31940
32655
|
let fd = null;
|
|
31941
32656
|
try {
|
|
31942
32657
|
fd = fs9.openSync(filePath, fs9.constants.O_RDONLY | (fs9.constants.O_NOFOLLOW ?? 0));
|
|
31943
|
-
const
|
|
31944
|
-
if (!
|
|
32658
|
+
const stat2 = fs9.fstatSync(fd);
|
|
32659
|
+
if (!stat2.isFile() || stat2.size <= 0 || stat2.size > RESUME_CONTROL_MAX_BYTES) {
|
|
31945
32660
|
return { kind: "invalid" };
|
|
31946
32661
|
}
|
|
31947
|
-
const bounded = Buffer.allocUnsafe(
|
|
32662
|
+
const bounded = Buffer.allocUnsafe(stat2.size + 1);
|
|
31948
32663
|
let bytesRead = 0;
|
|
31949
32664
|
while (bytesRead < bounded.length) {
|
|
31950
32665
|
const count = fs9.readSync(fd, bounded, bytesRead, bounded.length - bytesRead, bytesRead);
|
|
@@ -31952,7 +32667,7 @@ function readResumeControlState(timelineDir) {
|
|
|
31952
32667
|
break;
|
|
31953
32668
|
bytesRead += count;
|
|
31954
32669
|
}
|
|
31955
|
-
if (bytesRead !==
|
|
32670
|
+
if (bytesRead !== stat2.size)
|
|
31956
32671
|
return { kind: "invalid" };
|
|
31957
32672
|
const raw = bounded.subarray(0, bytesRead).toString("utf8");
|
|
31958
32673
|
const value = JSON.parse(raw);
|
|
@@ -32878,7 +33593,7 @@ class MessageReminderScheduler {
|
|
|
32878
33593
|
|
|
32879
33594
|
// src/manager/agentDriverHost.ts
|
|
32880
33595
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
32881
|
-
import { homedir as
|
|
33596
|
+
import { homedir as homedir6 } from "node:os";
|
|
32882
33597
|
import { join as join13 } from "node:path";
|
|
32883
33598
|
|
|
32884
33599
|
// src/drivers/gitIdentityEnv.ts
|
|
@@ -33004,7 +33719,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
|
|
|
33004
33719
|
hostUser: readHostGitIdentity() ?? undefined
|
|
33005
33720
|
}),
|
|
33006
33721
|
platformProtected: {
|
|
33007
|
-
ALOOK_HOME: process.env.ALOOK_HOME ?? join13(
|
|
33722
|
+
ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir6(), ".alook"),
|
|
33008
33723
|
ALOOK_ID: ctx.agentId,
|
|
33009
33724
|
ALOOK_CLI: ctx.agentCliPath,
|
|
33010
33725
|
ALOOK_SERVER_URL: ctx.config.serverUrl,
|
|
@@ -33325,13 +34040,13 @@ function createRuntimeRawLineTap(args) {
|
|
|
33325
34040
|
const maxBytes = args.maxBytes ?? RUNTIME_RAW_TRACE_MAX_BYTES;
|
|
33326
34041
|
const sinks = new Map;
|
|
33327
34042
|
const warned = new Set;
|
|
33328
|
-
const warnOnce = (agentId,
|
|
34043
|
+
const warnOnce = (agentId, path15, operation, error51) => {
|
|
33329
34044
|
if (warned.has(agentId))
|
|
33330
34045
|
return;
|
|
33331
34046
|
warned.add(agentId);
|
|
33332
34047
|
args.logger.warn("runtime raw trace sink failed", {
|
|
33333
34048
|
agentId,
|
|
33334
|
-
path:
|
|
34049
|
+
path: path15,
|
|
33335
34050
|
operation,
|
|
33336
34051
|
error: error51 instanceof Error ? error51.message : String(error51)
|
|
33337
34052
|
});
|
|
@@ -33340,10 +34055,10 @@ function createRuntimeRawLineTap(args) {
|
|
|
33340
34055
|
if (!args.enabledAgentIds.has(agentId))
|
|
33341
34056
|
return;
|
|
33342
34057
|
const encodedAgentId = encodeURIComponent(agentId).replace(/\./g, "%2E");
|
|
33343
|
-
const
|
|
34058
|
+
const path15 = `${traceDir}/runtime-raw-events-${encodedAgentId}.jsonl`;
|
|
33344
34059
|
const serializedBytes = Buffer.byteLength(line, "utf8") + 1;
|
|
33345
34060
|
if (serializedBytes > maxBytes) {
|
|
33346
|
-
warnOnce(agentId,
|
|
34061
|
+
warnOnce(agentId, path15, "oversize", new Error(`raw line is ${serializedBytes} bytes; max is ${maxBytes}`));
|
|
33347
34062
|
return;
|
|
33348
34063
|
}
|
|
33349
34064
|
let sink = sinks.get(agentId);
|
|
@@ -33351,12 +34066,12 @@ function createRuntimeRawLineTap(args) {
|
|
|
33351
34066
|
try {
|
|
33352
34067
|
mkdirSync8(traceDir, { recursive: true });
|
|
33353
34068
|
} catch (error51) {
|
|
33354
|
-
warnOnce(agentId,
|
|
34069
|
+
warnOnce(agentId, path15, "mkdir", error51);
|
|
33355
34070
|
}
|
|
33356
|
-
sink = createRotatingFileSink(
|
|
34071
|
+
sink = createRotatingFileSink(path15, maxBytes, {
|
|
33357
34072
|
mode: 384,
|
|
33358
34073
|
hardMaxBytes: true,
|
|
33359
|
-
onError: ({ operation, error: error51 }) => warnOnce(agentId,
|
|
34074
|
+
onError: ({ operation, error: error51 }) => warnOnce(agentId, path15, operation, error51)
|
|
33360
34075
|
});
|
|
33361
34076
|
sinks.set(agentId, sink);
|
|
33362
34077
|
}
|
|
@@ -33448,7 +34163,7 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
|
|
|
33448
34163
|
}
|
|
33449
34164
|
async function createDaemon(opts) {
|
|
33450
34165
|
const log2 = opts.logger ?? createLogger2({ header: "@alook/daemon" });
|
|
33451
|
-
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${
|
|
34166
|
+
const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir7()}/.alook`) + "/daemon";
|
|
33452
34167
|
const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
|
|
33453
34168
|
const workdirFor = (agentId) => `${workingDirectoryBase}/${agentId}`;
|
|
33454
34169
|
const dailyTokenUsage2 = new DailyTokenUsageStore(workingDirectoryBase);
|
|
@@ -34154,12 +34869,12 @@ async function readPinnedJsonFile(args) {
|
|
|
34154
34869
|
}
|
|
34155
34870
|
}
|
|
34156
34871
|
fd = openSync3(args.path, constants3.O_RDONLY | noFollow);
|
|
34157
|
-
const
|
|
34158
|
-
if (!
|
|
34872
|
+
const stat2 = fstatSync3(fd);
|
|
34873
|
+
if (!stat2.isFile())
|
|
34159
34874
|
return { value: null, warnings: ["source_unavailable"] };
|
|
34160
|
-
if (
|
|
34875
|
+
if (stat2.size > args.maxBytes)
|
|
34161
34876
|
return { value: null, warnings: ["line_too_long"] };
|
|
34162
|
-
const bytes = readPinnedBytes(fd,
|
|
34877
|
+
const bytes = readPinnedBytes(fd, stat2.size);
|
|
34163
34878
|
try {
|
|
34164
34879
|
return { value: JSON.parse(bytes.toString("utf8")), warnings: [] };
|
|
34165
34880
|
} catch {
|
|
@@ -34237,7 +34952,7 @@ var DAEMON_LOG_DIAGNOSTIC_POLICY = [
|
|
|
34237
34952
|
reason: string5(32, { values: ["turn_end", "stopped", "terminate_stalled", "exit"] }),
|
|
34238
34953
|
sessionId: string5(256, { scrub: true })
|
|
34239
34954
|
} },
|
|
34240
|
-
{ header: "@alook/daemon:manager", message: "steering message
|
|
34955
|
+
{ header: "@alook/daemon:manager", message: "steering message submitted to logical session", scope: "target", fields: {
|
|
34241
34956
|
agentId: AGENT,
|
|
34242
34957
|
mode: string5(8, { values: ["busy", "idle"] })
|
|
34243
34958
|
} },
|
|
@@ -34792,7 +35507,7 @@ import { createHash as createHash4, randomBytes as randomBytes7 } from "node:cry
|
|
|
34792
35507
|
import {
|
|
34793
35508
|
chmodSync as chmodSync2,
|
|
34794
35509
|
closeSync as closeSync4,
|
|
34795
|
-
createReadStream,
|
|
35510
|
+
createReadStream as createReadStream3,
|
|
34796
35511
|
existsSync as existsSync7,
|
|
34797
35512
|
fsyncSync as fsyncSync2,
|
|
34798
35513
|
lstatSync as lstatSync5,
|
|
@@ -34815,7 +35530,7 @@ class CoordinatorError extends Error {
|
|
|
34815
35530
|
function defaultFsOps() {
|
|
34816
35531
|
return {
|
|
34817
35532
|
randomSuffix: () => randomBytes7(12).toString("hex"),
|
|
34818
|
-
open: (
|
|
35533
|
+
open: (path15, flags, mode) => openSync4(path15, flags, mode),
|
|
34819
35534
|
write: (fd, bytes) => {
|
|
34820
35535
|
writeSync(fd, bytes);
|
|
34821
35536
|
},
|
|
@@ -34828,10 +35543,10 @@ function defaultFsOps() {
|
|
|
34828
35543
|
rename: (from, to) => {
|
|
34829
35544
|
renameSync5(from, to);
|
|
34830
35545
|
},
|
|
34831
|
-
fsyncDirectory: (
|
|
35546
|
+
fsyncDirectory: (path15) => {
|
|
34832
35547
|
if (process.platform === "win32")
|
|
34833
35548
|
return;
|
|
34834
|
-
const fd = openSync4(
|
|
35549
|
+
const fd = openSync4(path15, "r");
|
|
34835
35550
|
try {
|
|
34836
35551
|
fsyncSync2(fd);
|
|
34837
35552
|
} finally {
|
|
@@ -34882,8 +35597,8 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
34882
35597
|
const sidecarPath = (reportId) => join15(dir, `report-${reportId}.json`);
|
|
34883
35598
|
const ensureDir = () => {
|
|
34884
35599
|
if (existsSync7(dir)) {
|
|
34885
|
-
const
|
|
34886
|
-
if (!
|
|
35600
|
+
const stat2 = lstatSync5(dir);
|
|
35601
|
+
if (!stat2.isDirectory() || stat2.isSymbolicLink())
|
|
34887
35602
|
throw new CoordinatorError("local_artifact_invalid");
|
|
34888
35603
|
if (process.platform !== "win32")
|
|
34889
35604
|
chmodSync2(dir, 448);
|
|
@@ -34896,11 +35611,11 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
34896
35611
|
args.logger?.warn("diagnostic local artifact invalid", { reportId, code: "local_artifact_invalid" });
|
|
34897
35612
|
} catch {}
|
|
34898
35613
|
};
|
|
34899
|
-
const removeExact = (
|
|
35614
|
+
const removeExact = (path15) => {
|
|
34900
35615
|
try {
|
|
34901
|
-
const
|
|
34902
|
-
if (
|
|
34903
|
-
unlinkSync6(
|
|
35616
|
+
const stat2 = lstatSync5(path15);
|
|
35617
|
+
if (stat2.isFile() && !stat2.isSymbolicLink())
|
|
35618
|
+
unlinkSync6(path15);
|
|
34904
35619
|
} catch {}
|
|
34905
35620
|
};
|
|
34906
35621
|
const cleanup = (reportId) => {
|
|
@@ -34938,12 +35653,12 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
34938
35653
|
checkpoint(sidecar.phase === "collecting" ? "collecting_dir_fsynced" : "ready_dir_fsynced");
|
|
34939
35654
|
};
|
|
34940
35655
|
const committedArchive = (reportId) => {
|
|
34941
|
-
const
|
|
35656
|
+
const path15 = archivePath(reportId);
|
|
34942
35657
|
try {
|
|
34943
|
-
const
|
|
34944
|
-
if (!
|
|
35658
|
+
const stat2 = lstatSync5(path15);
|
|
35659
|
+
if (!stat2.isFile() || stat2.isSymbolicLink())
|
|
34945
35660
|
return null;
|
|
34946
|
-
const bytes = readFileSync6(
|
|
35661
|
+
const bytes = readFileSync6(path15);
|
|
34947
35662
|
return { sizeBytes: bytes.byteLength, sha256: createHash4("sha256").update(bytes).digest("hex") };
|
|
34948
35663
|
} catch {
|
|
34949
35664
|
return null;
|
|
@@ -35004,7 +35719,7 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
35004
35719
|
if (stopped)
|
|
35005
35720
|
throw new CoordinatorError("coordinator_shutdown");
|
|
35006
35721
|
checkpoint("before_upload");
|
|
35007
|
-
const result = await args.transport.upload({ reportId: sidecar.reportId, sizeBytes: sidecar.sizeBytes, sha256: sidecar.sha256 },
|
|
35722
|
+
const result = await args.transport.upload({ reportId: sidecar.reportId, sizeBytes: sidecar.sizeBytes, sha256: sidecar.sha256 }, createReadStream3(archivePath(sidecar.reportId)));
|
|
35008
35723
|
checkpoint("after_upload");
|
|
35009
35724
|
if (result.kind === "terminal") {
|
|
35010
35725
|
cleanup(sidecar.reportId);
|
|
@@ -35068,12 +35783,12 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
35068
35783
|
return uploadReady(ready);
|
|
35069
35784
|
};
|
|
35070
35785
|
const readSidecar = (reportId) => {
|
|
35071
|
-
const
|
|
35786
|
+
const path15 = sidecarPath(reportId);
|
|
35072
35787
|
try {
|
|
35073
|
-
const
|
|
35074
|
-
if (!
|
|
35788
|
+
const stat2 = lstatSync5(path15);
|
|
35789
|
+
if (!stat2.isFile() || stat2.isSymbolicLink())
|
|
35075
35790
|
return null;
|
|
35076
|
-
return parseSidecar(JSON.parse(readFileSync6(
|
|
35791
|
+
return parseSidecar(JSON.parse(readFileSync6(path15, "utf8")));
|
|
35077
35792
|
} catch {
|
|
35078
35793
|
return null;
|
|
35079
35794
|
}
|
|
@@ -35161,9 +35876,9 @@ function createDiagnosticReportCoordinator(args) {
|
|
|
35161
35876
|
}
|
|
35162
35877
|
// src/diagnostics/httpTransport.ts
|
|
35163
35878
|
import { Readable as Readable2 } from "node:stream";
|
|
35164
|
-
function endpoint(serverUrl,
|
|
35879
|
+
function endpoint(serverUrl, path15) {
|
|
35165
35880
|
const base = serverUrl.endsWith("/") ? serverUrl : `${serverUrl}/`;
|
|
35166
|
-
return new URL(
|
|
35881
|
+
return new URL(path15.replace(/^\//, ""), base).toString();
|
|
35167
35882
|
}
|
|
35168
35883
|
async function strictOutcome(response, allowed) {
|
|
35169
35884
|
try {
|
|
@@ -35227,29 +35942,29 @@ function createDiagnosticHttpTransport(args) {
|
|
|
35227
35942
|
// src/cli/daemonUpdate.ts
|
|
35228
35943
|
import * as crypto4 from "node:crypto";
|
|
35229
35944
|
import * as fs10 from "node:fs";
|
|
35230
|
-
import * as
|
|
35945
|
+
import * as path15 from "node:path";
|
|
35231
35946
|
import { spawn as spawn2 } from "node:child_process";
|
|
35232
35947
|
var UPDATE_INTENT_SCHEMA_VERSION = 1;
|
|
35233
35948
|
var UPDATE_LOG_MAX_BYTES = 512 * 1024;
|
|
35234
35949
|
var UPDATE_LOG_KEEP_BYTES = 256 * 1024;
|
|
35235
35950
|
var REQUEST_ID_PATTERN = /^[A-Za-z0-9_-]{16,128}$/;
|
|
35236
35951
|
function intentPath(baseDir, machineId) {
|
|
35237
|
-
return
|
|
35952
|
+
return path15.join(daemonDirById(baseDir, machineId), "update-intent.json");
|
|
35238
35953
|
}
|
|
35239
35954
|
function updateLogPath(baseDir, machineId) {
|
|
35240
|
-
return
|
|
35955
|
+
return path15.join(daemonDirById(baseDir, machineId), "update.log");
|
|
35241
35956
|
}
|
|
35242
35957
|
function updatePackageMapPath(baseDir, machineId) {
|
|
35243
|
-
return
|
|
35958
|
+
return path15.join(daemonDirById(baseDir, machineId), "update-package-map.json");
|
|
35244
35959
|
}
|
|
35245
35960
|
function ensurePrivateDir(dir) {
|
|
35246
35961
|
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
35247
35962
|
fs10.chmodSync(dir, 448);
|
|
35248
35963
|
}
|
|
35249
35964
|
function writePrivateJsonAtomic(filePath, value) {
|
|
35250
|
-
const dir =
|
|
35965
|
+
const dir = path15.dirname(filePath);
|
|
35251
35966
|
ensurePrivateDir(dir);
|
|
35252
|
-
const tempPath =
|
|
35967
|
+
const tempPath = path15.join(dir, `.${path15.basename(filePath)}.${process.pid}.${crypto4.randomBytes(8).toString("hex")}.tmp`);
|
|
35253
35968
|
let fd = fs10.openSync(tempPath, "wx", 384);
|
|
35254
35969
|
try {
|
|
35255
35970
|
fs10.writeFileSync(fd, JSON.stringify(value));
|
|
@@ -35274,7 +35989,7 @@ function scrub(value) {
|
|
|
35274
35989
|
}
|
|
35275
35990
|
function appendUpdateLog(baseDir, machineId, event, fields = {}) {
|
|
35276
35991
|
const filePath = updateLogPath(baseDir, machineId);
|
|
35277
|
-
ensurePrivateDir(
|
|
35992
|
+
ensurePrivateDir(path15.dirname(filePath));
|
|
35278
35993
|
try {
|
|
35279
35994
|
if (fs10.existsSync(filePath) && fs10.statSync(filePath).size > UPDATE_LOG_MAX_BYTES) {
|
|
35280
35995
|
const data = fs10.readFileSync(filePath);
|
|
@@ -35301,8 +36016,8 @@ function tuplesEqual(value, expected) {
|
|
|
35301
36016
|
}
|
|
35302
36017
|
function readIntent(baseDir, machineId) {
|
|
35303
36018
|
const filePath = intentPath(baseDir, machineId);
|
|
35304
|
-
const
|
|
35305
|
-
if (!
|
|
36019
|
+
const stat2 = fs10.lstatSync(filePath);
|
|
36020
|
+
if (!stat2.isFile())
|
|
35306
36021
|
throw new Error("unsafe daemon update intent type");
|
|
35307
36022
|
fs10.chmodSync(filePath, 384);
|
|
35308
36023
|
const value = JSON.parse(fs10.readFileSync(filePath, "utf8"));
|
|
@@ -35317,12 +36032,81 @@ function removeIntentIfMatches(baseDir, machineId, requestId) {
|
|
|
35317
36032
|
fs10.unlinkSync(intentPath(baseDir, machineId));
|
|
35318
36033
|
} catch {}
|
|
35319
36034
|
}
|
|
35320
|
-
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
|
|
36035
|
+
var defaultNpmLaunchFileProbe = {
|
|
36036
|
+
isFile(filePath) {
|
|
36037
|
+
try {
|
|
36038
|
+
return fs10.statSync(filePath).isFile();
|
|
36039
|
+
} catch {
|
|
36040
|
+
return false;
|
|
36041
|
+
}
|
|
36042
|
+
},
|
|
36043
|
+
isExecutable(filePath) {
|
|
36044
|
+
try {
|
|
36045
|
+
fs10.accessSync(filePath, fs10.constants.X_OK);
|
|
36046
|
+
return true;
|
|
36047
|
+
} catch {
|
|
36048
|
+
return false;
|
|
36049
|
+
}
|
|
35324
36050
|
}
|
|
35325
|
-
|
|
36051
|
+
};
|
|
36052
|
+
function pathEnvironment(env, platform) {
|
|
36053
|
+
if (platform !== "win32")
|
|
36054
|
+
return env.PATH;
|
|
36055
|
+
const entry = Object.entries(env).find(([key]) => key.toLowerCase() === "path");
|
|
36056
|
+
return entry?.[1];
|
|
36057
|
+
}
|
|
36058
|
+
function launchFromNpmPath(args) {
|
|
36059
|
+
const pathApi = args.platform === "win32" ? path15.win32 : path15.posix;
|
|
36060
|
+
if (!pathApi.isAbsolute(args.npmPath) || !args.probe.isFile(args.npmPath))
|
|
36061
|
+
return null;
|
|
36062
|
+
const extension = pathApi.extname(args.npmPath).toLowerCase();
|
|
36063
|
+
if ([".js", ".cjs", ".mjs"].includes(extension)) {
|
|
36064
|
+
return { file: args.nodePath, prefixArgs: [args.npmPath] };
|
|
36065
|
+
}
|
|
36066
|
+
if (args.platform === "win32" && [".cmd", ".bat"].includes(extension)) {
|
|
36067
|
+
const cliPath = pathApi.join(pathApi.dirname(args.npmPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
36068
|
+
return args.probe.isFile(cliPath) ? { file: args.nodePath, prefixArgs: [cliPath] } : null;
|
|
36069
|
+
}
|
|
36070
|
+
if (args.requireExecutable && !args.probe.isExecutable(args.npmPath))
|
|
36071
|
+
return null;
|
|
36072
|
+
return { file: args.npmPath, prefixArgs: [] };
|
|
36073
|
+
}
|
|
36074
|
+
function resolveNpmLaunchCommand(options = {}) {
|
|
36075
|
+
const env = options.env ?? process.env;
|
|
36076
|
+
const platform = options.platform ?? process.platform;
|
|
36077
|
+
const nodePath = options.nodePath ?? process.execPath;
|
|
36078
|
+
const probe = options.probe ?? defaultNpmLaunchFileProbe;
|
|
36079
|
+
const inherited = options.npmExecPath ?? env.npm_execpath;
|
|
36080
|
+
if (inherited) {
|
|
36081
|
+
const launch = launchFromNpmPath({
|
|
36082
|
+
npmPath: inherited,
|
|
36083
|
+
platform,
|
|
36084
|
+
nodePath,
|
|
36085
|
+
probe,
|
|
36086
|
+
requireExecutable: platform !== "win32"
|
|
36087
|
+
});
|
|
36088
|
+
if (launch)
|
|
36089
|
+
return launch;
|
|
36090
|
+
}
|
|
36091
|
+
const pathApi = platform === "win32" ? path15.win32 : path15.posix;
|
|
36092
|
+
const names = platform === "win32" ? ["npm.exe", "npm.cmd", "npm.bat", "npm"] : ["npm"];
|
|
36093
|
+
const pathValue = pathEnvironment(env, platform);
|
|
36094
|
+
for (const directory of pathValue?.split(pathApi.delimiter) ?? []) {
|
|
36095
|
+
if (!directory || !pathApi.isAbsolute(directory))
|
|
36096
|
+
continue;
|
|
36097
|
+
for (const name of names) {
|
|
36098
|
+
const launch = launchFromNpmPath({
|
|
36099
|
+
npmPath: pathApi.join(directory, name),
|
|
36100
|
+
platform,
|
|
36101
|
+
nodePath,
|
|
36102
|
+
probe,
|
|
36103
|
+
requireExecutable: platform !== "win32"
|
|
36104
|
+
});
|
|
36105
|
+
if (launch)
|
|
36106
|
+
return launch;
|
|
36107
|
+
}
|
|
36108
|
+
}
|
|
36109
|
+
throw new Error("npm launch context unavailable and no safe npm executable was found on PATH; daemon remains online");
|
|
35326
36110
|
}
|
|
35327
36111
|
function readTestPackageMap(baseDir, machineId) {
|
|
35328
36112
|
if (true)
|
|
@@ -35330,10 +36114,10 @@ function readTestPackageMap(baseDir, machineId) {
|
|
|
35330
36114
|
const filePath = updatePackageMapPath(baseDir, machineId);
|
|
35331
36115
|
if (!fs10.existsSync(filePath))
|
|
35332
36116
|
return null;
|
|
35333
|
-
const
|
|
35334
|
-
if (!
|
|
36117
|
+
const stat2 = fs10.lstatSync(filePath);
|
|
36118
|
+
if (!stat2.isFile())
|
|
35335
36119
|
throw new Error("unsafe daemon update package map type");
|
|
35336
|
-
if (process.platform !== "win32" && (
|
|
36120
|
+
if (process.platform !== "win32" && (stat2.mode & 63) !== 0) {
|
|
35337
36121
|
throw new Error("daemon update package map must be mode 0600");
|
|
35338
36122
|
}
|
|
35339
36123
|
const parsed = JSON.parse(fs10.readFileSync(filePath, "utf8"));
|
|
@@ -35343,7 +36127,7 @@ function readTestPackageMap(baseDir, machineId) {
|
|
|
35343
36127
|
for (const [key, value] of Object.entries(parsed)) {
|
|
35344
36128
|
if (key !== "latest" && !parseReleaseVersion(key))
|
|
35345
36129
|
throw new Error("invalid daemon update package map key");
|
|
35346
|
-
if (typeof value !== "string" || !
|
|
36130
|
+
if (typeof value !== "string" || !path15.isAbsolute(value))
|
|
35347
36131
|
throw new Error("invalid daemon update package map value");
|
|
35348
36132
|
result[key] = value;
|
|
35349
36133
|
}
|
|
@@ -35357,9 +36141,9 @@ function packageSpec(baseDir, machineId, version3) {
|
|
|
35357
36141
|
}
|
|
35358
36142
|
function fixedNpmArgs(args) {
|
|
35359
36143
|
return [
|
|
35360
|
-
args.npmPath,
|
|
35361
36144
|
"exec",
|
|
35362
36145
|
"--yes",
|
|
36146
|
+
"--no-audit",
|
|
35363
36147
|
`--package=${args.packageSpec}`,
|
|
35364
36148
|
"--",
|
|
35365
36149
|
"alook-daemon",
|
|
@@ -35390,7 +36174,13 @@ function createDaemonSelfUpdateHandler(context, deps = {}) {
|
|
|
35390
36174
|
const current = pidTuple(readPidFile(pidfilePathById(context.baseDir, context.machineId)));
|
|
35391
36175
|
if (!current || current.pid !== context.pid || current.machineId !== context.machineId || current.startedAt !== context.startedAt || current.ownerToken !== context.ownerToken)
|
|
35392
36176
|
throw new Error("daemon ownership changed before update launch");
|
|
35393
|
-
const
|
|
36177
|
+
const npmLaunch = resolveNpmLaunchCommand({
|
|
36178
|
+
npmExecPath: deps.npmExecPath,
|
|
36179
|
+
env: deps.npmLaunchEnv,
|
|
36180
|
+
platform: deps.npmLaunchPlatform,
|
|
36181
|
+
nodePath: deps.npmLaunchNodePath,
|
|
36182
|
+
probe: deps.npmLaunchFileProbe
|
|
36183
|
+
});
|
|
35394
36184
|
requestId = crypto4.randomUUID();
|
|
35395
36185
|
const intent = {
|
|
35396
36186
|
schemaVersion: UPDATE_INTENT_SCHEMA_VERSION,
|
|
@@ -35400,14 +36190,16 @@ function createDaemonSelfUpdateHandler(context, deps = {}) {
|
|
|
35400
36190
|
writePrivateJsonAtomic(intentPath(context.baseDir, context.machineId), intent);
|
|
35401
36191
|
const logFd = openUpdateLog(context.baseDir, context.machineId);
|
|
35402
36192
|
try {
|
|
35403
|
-
const child = (deps.spawnProcess ?? spawn2)(
|
|
35404
|
-
|
|
35405
|
-
|
|
35406
|
-
|
|
35407
|
-
|
|
35408
|
-
|
|
35409
|
-
|
|
35410
|
-
|
|
36193
|
+
const child = (deps.spawnProcess ?? spawn2)(npmLaunch.file, [
|
|
36194
|
+
...npmLaunch.prefixArgs,
|
|
36195
|
+
...fixedNpmArgs({
|
|
36196
|
+
packageSpec: packageSpec(context.baseDir, context.machineId, "latest"),
|
|
36197
|
+
command: "replace",
|
|
36198
|
+
machineId: context.machineId,
|
|
36199
|
+
baseDir: context.baseDir,
|
|
36200
|
+
requestId
|
|
36201
|
+
})
|
|
36202
|
+
], {
|
|
35411
36203
|
detached: true,
|
|
35412
36204
|
shell: false,
|
|
35413
36205
|
stdio: ["ignore", logFd, logFd]
|
|
@@ -35448,14 +36240,16 @@ function removeOldPidfileIfMatches(baseDir, intent) {
|
|
|
35448
36240
|
async function runPinnedResume(args) {
|
|
35449
36241
|
const fd = openUpdateLog(args.baseDir, args.machineId);
|
|
35450
36242
|
try {
|
|
35451
|
-
const child = spawn2(
|
|
35452
|
-
|
|
35453
|
-
|
|
35454
|
-
|
|
35455
|
-
|
|
35456
|
-
|
|
35457
|
-
|
|
35458
|
-
|
|
36243
|
+
const child = spawn2(args.npmLaunch.file, [
|
|
36244
|
+
...args.npmLaunch.prefixArgs,
|
|
36245
|
+
...fixedNpmArgs({
|
|
36246
|
+
packageSpec: packageSpec(args.baseDir, args.machineId, args.version),
|
|
36247
|
+
command: "resume",
|
|
36248
|
+
machineId: args.machineId,
|
|
36249
|
+
baseDir: args.baseDir,
|
|
36250
|
+
requestId: args.requestId
|
|
36251
|
+
})
|
|
36252
|
+
], {
|
|
35459
36253
|
shell: false,
|
|
35460
36254
|
stdio: ["ignore", fd, fd]
|
|
35461
36255
|
});
|
|
@@ -35499,7 +36293,7 @@ async function daemonReplace(opts) {
|
|
|
35499
36293
|
removeIntentIfMatches(baseDir, opts.id, opts.requestId);
|
|
35500
36294
|
throw new Error("daemon ownership changed before replacement");
|
|
35501
36295
|
}
|
|
35502
|
-
const
|
|
36296
|
+
const npmLaunch = resolveNpmLaunchCommand();
|
|
35503
36297
|
const acquired = acquireDaemonReplacementLock({ baseDir, machineId: opts.id, requestId: opts.requestId });
|
|
35504
36298
|
let oldStopped = false;
|
|
35505
36299
|
try {
|
|
@@ -35519,7 +36313,7 @@ async function daemonReplace(opts) {
|
|
|
35519
36313
|
} catch (error51) {
|
|
35520
36314
|
appendUpdateLog(baseDir, opts.id, "replacement_start_failed", { error: error51 });
|
|
35521
36315
|
await runPinnedResume({
|
|
35522
|
-
|
|
36316
|
+
npmLaunch,
|
|
35523
36317
|
version: launch.daemonVersion,
|
|
35524
36318
|
machineId: opts.id,
|
|
35525
36319
|
baseDir,
|
|
@@ -35642,7 +36436,7 @@ async function buildRunnerDiagnosticBundle(args) {
|
|
|
35642
36436
|
function createDaemonProcessLogger(daemonDir, foreground) {
|
|
35643
36437
|
fs11.mkdirSync(daemonDir, { recursive: true, mode: 448 });
|
|
35644
36438
|
fs11.chmodSync(daemonDir, 448);
|
|
35645
|
-
const logPath =
|
|
36439
|
+
const logPath = path16.join(daemonDir, "daemon.log");
|
|
35646
36440
|
let warnedOversize = false;
|
|
35647
36441
|
const sink = createRotatingFileSink(logPath, DAEMON_LOG_MAX_BYTES, {
|
|
35648
36442
|
mode: 384,
|
|
@@ -35878,16 +36672,16 @@ var LEGACY_DAEMON_ID_PATTERN = /^[a-f0-9]{12}$/;
|
|
|
35878
36672
|
var DEFAULT_SERVER_URL = "https://alook.ai";
|
|
35879
36673
|
var DEFAULT_WS_URL = "wss://alook.ai/api/ws/community-daemon";
|
|
35880
36674
|
function resolveDefaultBaseDir() {
|
|
35881
|
-
const root = process.env.ALOOK_PROJECT_ROOT ||
|
|
35882
|
-
return
|
|
36675
|
+
const root = process.env.ALOOK_PROJECT_ROOT || path17.join(homedir8(), ".alook");
|
|
36676
|
+
return path17.join(root, "daemon");
|
|
35883
36677
|
}
|
|
35884
36678
|
var DEFAULT_BASE_DIR = resolveDefaultBaseDir();
|
|
35885
36679
|
var log2 = createLogger2({ header: "@alook/daemon" });
|
|
35886
36680
|
function daemonsDir(baseDir) {
|
|
35887
|
-
return
|
|
36681
|
+
return path17.join(baseDir, "daemons");
|
|
35888
36682
|
}
|
|
35889
36683
|
function daemonDirById(baseDir, id) {
|
|
35890
|
-
return
|
|
36684
|
+
return path17.join(daemonsDir(baseDir), validateDaemonId(id));
|
|
35891
36685
|
}
|
|
35892
36686
|
function validateMachineId(machineId) {
|
|
35893
36687
|
if (!MACHINE_ID_PATTERN.test(machineId))
|
|
@@ -35903,10 +36697,10 @@ function validateDaemonId(id) {
|
|
|
35903
36697
|
return id;
|
|
35904
36698
|
}
|
|
35905
36699
|
function pidfilePathById(baseDir, id) {
|
|
35906
|
-
return
|
|
36700
|
+
return path17.join(daemonDirById(baseDir, id), "daemon.pid");
|
|
35907
36701
|
}
|
|
35908
36702
|
function statusFilePathById(baseDir, id) {
|
|
35909
|
-
return
|
|
36703
|
+
return path17.join(daemonDirById(baseDir, id), "status.json");
|
|
35910
36704
|
}
|
|
35911
36705
|
function isProcessAlive(pid) {
|
|
35912
36706
|
try {
|
|
@@ -35966,9 +36760,9 @@ function syncDirectory(dir) {
|
|
|
35966
36760
|
}
|
|
35967
36761
|
}
|
|
35968
36762
|
function writeExclusive(filePath, value) {
|
|
35969
|
-
const dir =
|
|
36763
|
+
const dir = path17.dirname(filePath);
|
|
35970
36764
|
ensurePrivateDir2(dir);
|
|
35971
|
-
const tempPath =
|
|
36765
|
+
const tempPath = path17.join(dir, `.${path17.basename(filePath)}.${process.pid}.${crypto5.randomBytes(12).toString("hex")}.tmp`);
|
|
35972
36766
|
let fd = fs12.openSync(tempPath, "wx", 384);
|
|
35973
36767
|
try {
|
|
35974
36768
|
fs12.writeFileSync(fd, JSON.stringify(value));
|
|
@@ -35990,9 +36784,9 @@ function writeExclusive(filePath, value) {
|
|
|
35990
36784
|
}
|
|
35991
36785
|
}
|
|
35992
36786
|
function writePrivateJsonAtomic2(filePath, value) {
|
|
35993
|
-
const dir =
|
|
36787
|
+
const dir = path17.dirname(filePath);
|
|
35994
36788
|
ensurePrivateDir2(dir);
|
|
35995
|
-
const tempPath =
|
|
36789
|
+
const tempPath = path17.join(dir, `.${path17.basename(filePath)}.${process.pid}.${crypto5.randomBytes(12).toString("hex")}.tmp`);
|
|
35996
36790
|
let fd = fs12.openSync(tempPath, "wx", 384);
|
|
35997
36791
|
try {
|
|
35998
36792
|
fs12.writeFileSync(fd, JSON.stringify(value));
|
|
@@ -36016,8 +36810,8 @@ function writePrivateJsonAtomic2(filePath, value) {
|
|
|
36016
36810
|
function secureExistingFile(filePath) {
|
|
36017
36811
|
if (!fs12.existsSync(filePath))
|
|
36018
36812
|
return;
|
|
36019
|
-
const
|
|
36020
|
-
if (!
|
|
36813
|
+
const stat2 = fs12.lstatSync(filePath);
|
|
36814
|
+
if (!stat2.isFile())
|
|
36021
36815
|
throw new Error("unsafe daemon ownership file type");
|
|
36022
36816
|
fs12.chmodSync(filePath, 384);
|
|
36023
36817
|
}
|
|
@@ -36079,14 +36873,14 @@ function clearStaleOwnership(filePath, liveError) {
|
|
|
36079
36873
|
} catch {}
|
|
36080
36874
|
}
|
|
36081
36875
|
function replacementLockPathById(baseDir, machineId) {
|
|
36082
|
-
return
|
|
36876
|
+
return path17.join(daemonDirById(baseDir, machineId), "daemon.replace.lock");
|
|
36083
36877
|
}
|
|
36084
36878
|
function readReplacementLock(filePath) {
|
|
36085
36879
|
if (!fs12.existsSync(filePath))
|
|
36086
36880
|
return null;
|
|
36087
36881
|
try {
|
|
36088
|
-
const
|
|
36089
|
-
if (!
|
|
36882
|
+
const stat2 = fs12.lstatSync(filePath);
|
|
36883
|
+
if (!stat2.isFile())
|
|
36090
36884
|
throw new Error("unsafe daemon replacement lock type");
|
|
36091
36885
|
fs12.chmodSync(filePath, 384);
|
|
36092
36886
|
const content = JSON.parse(fs12.readFileSync(filePath, "utf8"));
|
|
@@ -36149,7 +36943,7 @@ function checkReplacementLock(baseDir, machineId, resumeRequestId) {
|
|
|
36149
36943
|
}
|
|
36150
36944
|
function acquireDaemonReplacementLock(args) {
|
|
36151
36945
|
const lockPath = replacementLockPathById(args.baseDir, args.machineId);
|
|
36152
|
-
ensurePrivateDir2(
|
|
36946
|
+
ensurePrivateDir2(path17.dirname(lockPath));
|
|
36153
36947
|
checkReplacementLock(args.baseDir, args.machineId);
|
|
36154
36948
|
const lock = {
|
|
36155
36949
|
pid: process.pid,
|
|
@@ -36162,11 +36956,11 @@ function acquireDaemonReplacementLock(args) {
|
|
|
36162
36956
|
return { path: lockPath, lock };
|
|
36163
36957
|
}
|
|
36164
36958
|
function coarseStartLockPath(baseDir) {
|
|
36165
|
-
return
|
|
36959
|
+
return path17.join(daemonsDir(baseDir), ".start.lock");
|
|
36166
36960
|
}
|
|
36167
36961
|
function acquireCoarseLock(baseDir, ownerToken) {
|
|
36168
36962
|
const lf = coarseStartLockPath(baseDir);
|
|
36169
|
-
ensurePrivateDir2(
|
|
36963
|
+
ensurePrivateDir2(path17.dirname(lf));
|
|
36170
36964
|
secureExistingFile(lf);
|
|
36171
36965
|
clearStaleOwnership(lf, (pid) => new Error(`another daemon start is in progress on this machine (pid ${pid})`));
|
|
36172
36966
|
writeExclusive(lf, { pid: process.pid, machineId: "coarse", startedAt: new Date().toISOString(), ownerToken });
|
|
@@ -36175,7 +36969,7 @@ function acquireCoarseLock(baseDir, ownerToken) {
|
|
|
36175
36969
|
function acquireLaunchLock(baseDir, machineId, ownerToken, resumeRequestId) {
|
|
36176
36970
|
const daemonDir = daemonDirById(baseDir, machineId);
|
|
36177
36971
|
ensurePrivateDir2(daemonDir);
|
|
36178
|
-
const lockPath =
|
|
36972
|
+
const lockPath = path17.join(daemonDir, "daemon.launch.lock");
|
|
36179
36973
|
const finalPath = pidfilePathById(baseDir, machineId);
|
|
36180
36974
|
checkReplacementLock(baseDir, machineId, resumeRequestId);
|
|
36181
36975
|
secureExistingFile(lockPath);
|
|
@@ -36197,9 +36991,9 @@ function legacyPidfileCandidates(baseDir) {
|
|
|
36197
36991
|
const candidates = [];
|
|
36198
36992
|
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
36199
36993
|
if (entry.isDirectory()) {
|
|
36200
|
-
candidates.push(
|
|
36994
|
+
candidates.push(path17.join(dir, entry.name, "daemon.pid"));
|
|
36201
36995
|
} else if (entry.isFile() && entry.name.endsWith(".pid")) {
|
|
36202
|
-
candidates.push(
|
|
36996
|
+
candidates.push(path17.join(dir, entry.name));
|
|
36203
36997
|
}
|
|
36204
36998
|
}
|
|
36205
36999
|
return candidates;
|
|
@@ -36271,7 +37065,7 @@ function daemonList(opts) {
|
|
|
36271
37065
|
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
36272
37066
|
if (entry.isDirectory() && isDaemonId(entry.name)) {
|
|
36273
37067
|
const id = entry.name;
|
|
36274
|
-
pushRow(id,
|
|
37068
|
+
pushRow(id, path17.join(dir, id, "daemon.pid"), path17.join(dir, id, "status.json"));
|
|
36275
37069
|
}
|
|
36276
37070
|
}
|
|
36277
37071
|
return results;
|
|
@@ -36319,7 +37113,7 @@ function daemonIdsWithStatus(baseDir) {
|
|
|
36319
37113
|
return [];
|
|
36320
37114
|
const ids = [];
|
|
36321
37115
|
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
36322
|
-
if (entry.isDirectory() && isDaemonId(entry.name) && fs12.existsSync(
|
|
37116
|
+
if (entry.isDirectory() && isDaemonId(entry.name) && fs12.existsSync(path17.join(dir, entry.name, "status.json"))) {
|
|
36323
37117
|
ids.push(entry.name);
|
|
36324
37118
|
}
|
|
36325
37119
|
}
|
|
@@ -36382,16 +37176,16 @@ async function stopByPidfile(pf, notFoundHint) {
|
|
|
36382
37176
|
}
|
|
36383
37177
|
async function daemonStop(opts) {
|
|
36384
37178
|
const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
|
|
36385
|
-
await stopByPidfile(
|
|
37179
|
+
await stopByPidfile(path17.join(daemonDirById(baseDir, opts.id), "daemon.pid"), `no daemon with id '${opts.id}' (pidfile not found — check \`alook daemon list\`)`);
|
|
36386
37180
|
}
|
|
36387
37181
|
function credentialFilePathByMachineId(baseDir, machineId) {
|
|
36388
|
-
return
|
|
37182
|
+
return path17.join(daemonsDir(baseDir), `${validateMachineId(machineId)}.credential.json`);
|
|
36389
37183
|
}
|
|
36390
37184
|
function readCredentialFile(filePath) {
|
|
36391
37185
|
if (!fs12.existsSync(filePath))
|
|
36392
37186
|
return null;
|
|
36393
|
-
const
|
|
36394
|
-
if (!
|
|
37187
|
+
const stat2 = fs12.lstatSync(filePath);
|
|
37188
|
+
if (!stat2.isFile())
|
|
36395
37189
|
throw new Error("unsafe daemon credential file type");
|
|
36396
37190
|
fs12.chmodSync(filePath, 384);
|
|
36397
37191
|
try {
|
|
@@ -36432,7 +37226,7 @@ function findExistingCredentialForBearer(baseDir, bearer) {
|
|
|
36432
37226
|
for (const file2 of fs12.readdirSync(dir)) {
|
|
36433
37227
|
if (!file2.endsWith(".credential.json"))
|
|
36434
37228
|
continue;
|
|
36435
|
-
const parsed = readCredentialFile(
|
|
37229
|
+
const parsed = readCredentialFile(path17.join(dir, file2));
|
|
36436
37230
|
if (parsed && parsed.credential === bearer)
|
|
36437
37231
|
return parsed;
|
|
36438
37232
|
}
|
|
@@ -36713,7 +37507,7 @@ function waitForReceipt(child, prepared) {
|
|
|
36713
37507
|
};
|
|
36714
37508
|
const configuredTestTimeout = NaN;
|
|
36715
37509
|
const receiptTimeoutMs = Number.isFinite(configuredTestTimeout) && configuredTestTimeout > 0 ? configuredTestTimeout : START_RECEIPT_TIMEOUT_MS;
|
|
36716
|
-
const timeout = setTimeout(() => settle(new Error(`daemon start timed out; inspect ${
|
|
37510
|
+
const timeout = setTimeout(() => settle(new Error(`daemon start timed out; inspect ${path17.join(prepared.daemonDir, "daemon.log")}`)), receiptTimeoutMs);
|
|
36717
37511
|
child.on("message", (message2) => {
|
|
36718
37512
|
const accepted = message2;
|
|
36719
37513
|
if (accepted.type === "daemon:accepted") {
|
|
@@ -36732,7 +37526,7 @@ function waitForReceipt(child, prepared) {
|
|
|
36732
37526
|
settle(undefined, receipt);
|
|
36733
37527
|
});
|
|
36734
37528
|
child.once("exit", (code, signal) => {
|
|
36735
|
-
settle(new Error(`daemon child exited before ready (${signal ?? code}); inspect ${
|
|
37529
|
+
settle(new Error(`daemon child exited before ready (${signal ?? code}); inspect ${path17.join(prepared.daemonDir, "daemon.log")}`));
|
|
36736
37530
|
});
|
|
36737
37531
|
child.once("error", (error51) => settle(error51));
|
|
36738
37532
|
});
|