@ganglion/xacpx 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -460
- package/dist/bridge/bridge-main.js +112 -5
- package/dist/cli.js +436 -101
- package/dist/commands/handlers/session-handler.d.ts +3 -9
- package/dist/commands/router-types.d.ts +2 -5
- package/dist/control/control-event-bus.d.ts +10 -0
- package/dist/control/control-service.d.ts +15 -0
- package/dist/control/upload-store.d.ts +28 -0
- package/dist/i18n/types.d.ts +1 -0
- package/dist/plugin-api.js +2 -0
- package/dist/transport/types.d.ts +37 -4
- package/dist/weixin/agent/interface.d.ts +4 -4
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1075,6 +1075,7 @@ var init_misc = __esm(() => {
|
|
|
1075
1075
|
defaultHomeWorkspaceDescription: "Home directory",
|
|
1076
1076
|
pluginChannelFeishu: "Feishu channel",
|
|
1077
1077
|
pluginChannelYuanbao: "Tencent Yuanbao channel",
|
|
1078
|
+
pluginChannelRelay: "Relay hub connector (drive this instance from a self-hosted relay hub)",
|
|
1078
1079
|
pluginChannelInstallHint: (channelType, packageName) => `Channel ${channelType} requires a plugin: xacpx plugin add ${packageName}`,
|
|
1079
1080
|
orchestrationSuggestion1: "Run /tasks --stuck to locate stuck tasks",
|
|
1080
1081
|
orchestrationSuggestion2: "/task <id> shows the full timeline to locate errors",
|
|
@@ -2171,6 +2172,7 @@ var init_misc2 = __esm(() => {
|
|
|
2171
2172
|
defaultHomeWorkspaceDescription: "用户主目录",
|
|
2172
2173
|
pluginChannelFeishu: "飞书频道",
|
|
2173
2174
|
pluginChannelYuanbao: "腾讯元宝频道",
|
|
2175
|
+
pluginChannelRelay: "Relay hub 连接器(从自托管 relay hub 遥控这台实例)",
|
|
2174
2176
|
pluginChannelInstallHint: (channelType, packageName) => `频道 ${channelType} 需要安装插件:xacpx plugin add ${packageName}`,
|
|
2175
2177
|
orchestrationSuggestion1: "查看 /tasks --stuck 定位卡住的任务",
|
|
2176
2178
|
orchestrationSuggestion2: "/task <id> 可看完整时间线定位错误点",
|
|
@@ -19512,6 +19514,12 @@ var init_known_plugins = __esm(() => {
|
|
|
19512
19514
|
channels: ["yuanbao"],
|
|
19513
19515
|
descriptionKey: "pluginChannelYuanbao",
|
|
19514
19516
|
official: true
|
|
19517
|
+
},
|
|
19518
|
+
{
|
|
19519
|
+
packageName: "@ganglion/xacpx-channel-relay",
|
|
19520
|
+
channels: ["relay"],
|
|
19521
|
+
descriptionKey: "pluginChannelRelay",
|
|
19522
|
+
official: true
|
|
19515
19523
|
}
|
|
19516
19524
|
];
|
|
19517
19525
|
});
|
|
@@ -21780,8 +21788,9 @@ async function buildCoordinatorPrompt(input) {
|
|
|
21780
21788
|
`));
|
|
21781
21789
|
}
|
|
21782
21790
|
if (input.userText) {
|
|
21783
|
-
sections.
|
|
21784
|
-
`
|
|
21791
|
+
const hasOrchestrationContext = sections.length > 0;
|
|
21792
|
+
sections.push(hasOrchestrationContext ? [t().coordinatorPrompt.userMessageLabel, input.userText].join(`
|
|
21793
|
+
`) : input.userText);
|
|
21785
21794
|
}
|
|
21786
21795
|
const claimHumanReply = shouldBind && input.chatKey && activePackage?.awaitingReplyMessageId ? {
|
|
21787
21796
|
coordinatorSession: input.coordinatorSession,
|
|
@@ -22377,7 +22386,7 @@ async function handleSessionArchive(context, chatKey, alias, archive) {
|
|
|
22377
22386
|
await archive(internalAlias);
|
|
22378
22387
|
return { text: t().session.sessionArchived(alias) };
|
|
22379
22388
|
}
|
|
22380
|
-
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22389
|
+
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22381
22390
|
if (session3.archived) {
|
|
22382
22391
|
await context.sessions.setArchived(session3.alias, false);
|
|
22383
22392
|
}
|
|
@@ -22412,7 +22421,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22412
22421
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session3, chatKey, text, replyContextToken, accountId);
|
|
22413
22422
|
try {
|
|
22414
22423
|
const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
|
|
22415
|
-
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage);
|
|
22424
|
+
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands);
|
|
22416
22425
|
if (claimHumanReply) {
|
|
22417
22426
|
try {
|
|
22418
22427
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -22432,23 +22441,23 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22432
22441
|
throw error2;
|
|
22433
22442
|
}
|
|
22434
22443
|
}
|
|
22435
|
-
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22444
|
+
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22436
22445
|
try {
|
|
22437
|
-
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22446
|
+
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22438
22447
|
} catch (error2) {
|
|
22439
22448
|
const recovered = await context.recovery.tryRecoverMissingSession(session3, error2);
|
|
22440
22449
|
if (recovered) {
|
|
22441
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22450
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22442
22451
|
}
|
|
22443
22452
|
return context.recovery.renderTransportError(session3, error2);
|
|
22444
22453
|
}
|
|
22445
22454
|
}
|
|
22446
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22455
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22447
22456
|
const session3 = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
22448
22457
|
if (!session3) {
|
|
22449
22458
|
return { text: t().session.noCurrent };
|
|
22450
22459
|
}
|
|
22451
|
-
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22460
|
+
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22452
22461
|
}
|
|
22453
22462
|
function toCoordinatorRouteChatMetadata(metadata) {
|
|
22454
22463
|
if (!metadata) {
|
|
@@ -24707,7 +24716,7 @@ class CommandRouter {
|
|
|
24707
24716
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
24708
24717
|
this.activeTurns = activeTurns;
|
|
24709
24718
|
}
|
|
24710
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
24719
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
|
|
24711
24720
|
const startedAt = Date.now();
|
|
24712
24721
|
let command = parseCommand(input);
|
|
24713
24722
|
if (metadata?.channel === "control" && command.kind !== "prompt") {
|
|
@@ -24869,16 +24878,16 @@ class CommandRouter {
|
|
|
24869
24878
|
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
24870
24879
|
transient: true
|
|
24871
24880
|
};
|
|
24872
|
-
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24881
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24873
24882
|
}
|
|
24874
24883
|
if (metadata?.scheduledSessionAlias) {
|
|
24875
24884
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
24876
24885
|
if (!scheduledSession) {
|
|
24877
24886
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
24878
24887
|
}
|
|
24879
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24888
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24880
24889
|
}
|
|
24881
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24890
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24882
24891
|
}
|
|
24883
24892
|
}
|
|
24884
24893
|
});
|
|
@@ -25102,7 +25111,7 @@ class CommandRouter {
|
|
|
25102
25111
|
setModelTransportSession: (session3, modelId) => this.setModelTransportSession(session3, modelId),
|
|
25103
25112
|
getModelTransportSession: (session3) => this.getModelTransportSession(session3),
|
|
25104
25113
|
cancelTransportSession: (session3) => this.cancelTransportSession(session3),
|
|
25105
|
-
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage)
|
|
25114
|
+
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage, onCommands) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage, onCommands)
|
|
25106
25115
|
};
|
|
25107
25116
|
}
|
|
25108
25117
|
createSessionRenderRecoveryOps() {
|
|
@@ -25281,7 +25290,7 @@ class CommandRouter {
|
|
|
25281
25290
|
async checkTransportSession(session3) {
|
|
25282
25291
|
return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
|
|
25283
25292
|
}
|
|
25284
|
-
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
25293
|
+
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
|
|
25285
25294
|
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
25286
25295
|
let done = false;
|
|
25287
25296
|
let abortRequested = false;
|
|
@@ -25340,7 +25349,8 @@ class CommandRouter {
|
|
|
25340
25349
|
...onToolEvent ? { onToolEvent } : {},
|
|
25341
25350
|
...onThought ? { onThought } : {},
|
|
25342
25351
|
...onPlan ? { onPlan } : {},
|
|
25343
|
-
...onUsage ? { onUsage } : {}
|
|
25352
|
+
...onUsage ? { onUsage } : {},
|
|
25353
|
+
...onCommands ? { onCommands } : {}
|
|
25344
25354
|
}));
|
|
25345
25355
|
} catch (error2) {
|
|
25346
25356
|
localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
|
|
@@ -25526,7 +25536,7 @@ class ConsoleAgent {
|
|
|
25526
25536
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
25527
25537
|
})) : undefined;
|
|
25528
25538
|
request.perfSpan?.mark("agent.dispatched");
|
|
25529
|
-
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan, request.onUsage);
|
|
25539
|
+
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan, request.onUsage, request.onCommands);
|
|
25530
25540
|
}
|
|
25531
25541
|
isKnownCommand(text) {
|
|
25532
25542
|
return isKnownXacpxCommandText(text);
|
|
@@ -30607,6 +30617,10 @@ function encodeBridgePromptUsageEvent(event) {
|
|
|
30607
30617
|
return `${JSON.stringify(event)}
|
|
30608
30618
|
`;
|
|
30609
30619
|
}
|
|
30620
|
+
function encodeBridgePromptCommandsEvent(event) {
|
|
30621
|
+
return `${JSON.stringify(event)}
|
|
30622
|
+
`;
|
|
30623
|
+
}
|
|
30610
30624
|
function encodeBridgeSessionProgressEvent(event) {
|
|
30611
30625
|
return `${JSON.stringify(event)}
|
|
30612
30626
|
`;
|
|
@@ -30913,7 +30927,14 @@ class AcpxBridgeClient {
|
|
|
30913
30927
|
pending.onEvent?.({
|
|
30914
30928
|
type: "prompt.usage",
|
|
30915
30929
|
used: message.used,
|
|
30916
|
-
size: message.size
|
|
30930
|
+
size: message.size,
|
|
30931
|
+
...message.cost ? { cost: message.cost } : {},
|
|
30932
|
+
...message.breakdown ? { breakdown: message.breakdown } : {}
|
|
30933
|
+
});
|
|
30934
|
+
} else if (message.event === "prompt.commands") {
|
|
30935
|
+
pending.onEvent?.({
|
|
30936
|
+
type: "prompt.commands",
|
|
30937
|
+
commands: message.commands
|
|
30917
30938
|
});
|
|
30918
30939
|
} else if (message.event === "session.progress") {
|
|
30919
30940
|
pending.onEvent?.({
|
|
@@ -31376,6 +31397,8 @@ class AcpxBridgeTransport {
|
|
|
31376
31397
|
let planChain = Promise.resolve();
|
|
31377
31398
|
let usageError;
|
|
31378
31399
|
let usageChain = Promise.resolve();
|
|
31400
|
+
let commandsError;
|
|
31401
|
+
let commandsChain = Promise.resolve();
|
|
31379
31402
|
let toolEventMode = resolveToolEventMode(options);
|
|
31380
31403
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
31381
31404
|
toolEventMode = "text";
|
|
@@ -31431,19 +31454,30 @@ class AcpxBridgeTransport {
|
|
|
31431
31454
|
if (event.type === "prompt.usage") {
|
|
31432
31455
|
const onUsage = options?.onUsage;
|
|
31433
31456
|
if (onUsage) {
|
|
31434
|
-
const usage = { used: event.used, size: event.size };
|
|
31457
|
+
const usage = { used: event.used, size: event.size, ...event.cost ? { cost: event.cost } : {}, ...event.breakdown ? { breakdown: event.breakdown } : {} };
|
|
31435
31458
|
usageChain = usageChain.then(() => onUsage(usage)).catch((error2) => {
|
|
31436
31459
|
usageError ??= error2;
|
|
31437
31460
|
});
|
|
31438
31461
|
}
|
|
31439
31462
|
return;
|
|
31440
31463
|
}
|
|
31464
|
+
if (event.type === "prompt.commands") {
|
|
31465
|
+
const onCommands = options?.onCommands;
|
|
31466
|
+
if (onCommands) {
|
|
31467
|
+
const commands = event.commands;
|
|
31468
|
+
commandsChain = commandsChain.then(() => onCommands(commands)).catch((error2) => {
|
|
31469
|
+
commandsError ??= error2;
|
|
31470
|
+
});
|
|
31471
|
+
}
|
|
31472
|
+
return;
|
|
31473
|
+
}
|
|
31441
31474
|
});
|
|
31442
31475
|
await segmentChain;
|
|
31443
31476
|
await toolEventChain;
|
|
31444
31477
|
await thoughtChain;
|
|
31445
31478
|
await planChain;
|
|
31446
31479
|
await usageChain;
|
|
31480
|
+
await commandsChain;
|
|
31447
31481
|
if (sink) {
|
|
31448
31482
|
const { overflowCount } = sink.finalize();
|
|
31449
31483
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -31467,6 +31501,9 @@ class AcpxBridgeTransport {
|
|
|
31467
31501
|
if (usageError) {
|
|
31468
31502
|
throw usageError;
|
|
31469
31503
|
}
|
|
31504
|
+
if (commandsError) {
|
|
31505
|
+
throw commandsError;
|
|
31506
|
+
}
|
|
31470
31507
|
return { text: summary ? `${summary}
|
|
31471
31508
|
|
|
31472
31509
|
${result.text}` : "" };
|
|
@@ -31486,6 +31523,9 @@ ${result.text}` : "" };
|
|
|
31486
31523
|
if (usageError) {
|
|
31487
31524
|
throw usageError;
|
|
31488
31525
|
}
|
|
31526
|
+
if (commandsError) {
|
|
31527
|
+
throw commandsError;
|
|
31528
|
+
}
|
|
31489
31529
|
return result;
|
|
31490
31530
|
}
|
|
31491
31531
|
async setMode(session3, modeId) {
|
|
@@ -31695,6 +31735,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31695
31735
|
let onThought;
|
|
31696
31736
|
let onPlan;
|
|
31697
31737
|
let onUsage;
|
|
31738
|
+
let onCommands;
|
|
31698
31739
|
let rawStream = false;
|
|
31699
31740
|
if (options === undefined) {
|
|
31700
31741
|
toolEventMode = "text";
|
|
@@ -31707,6 +31748,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31707
31748
|
onThought = options.onThought;
|
|
31708
31749
|
onPlan = options.onPlan;
|
|
31709
31750
|
onUsage = options.onUsage;
|
|
31751
|
+
onCommands = options.onCommands;
|
|
31710
31752
|
rawStream = options.rawStream ?? false;
|
|
31711
31753
|
toolEventMode = resolveToolEventMode({
|
|
31712
31754
|
toolEventMode: options.mode,
|
|
@@ -31720,12 +31762,14 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31720
31762
|
pendingLine: "",
|
|
31721
31763
|
formatToolCalls,
|
|
31722
31764
|
emittedToolCallIds: new Set,
|
|
31765
|
+
toolCalls: new Map,
|
|
31723
31766
|
toolEventMode,
|
|
31724
31767
|
rawStream,
|
|
31725
31768
|
onToolEvent,
|
|
31726
31769
|
onThought,
|
|
31727
31770
|
onPlan,
|
|
31728
31771
|
onUsage,
|
|
31772
|
+
onCommands,
|
|
31729
31773
|
finalize() {
|
|
31730
31774
|
if (this.pendingLine.trim().length > 0) {
|
|
31731
31775
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -31766,7 +31810,8 @@ function parseStreamingChunks(state, line) {
|
|
|
31766
31810
|
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
31767
31811
|
const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
|
|
31768
31812
|
if (wantsStructured && state.onToolEvent) {
|
|
31769
|
-
const
|
|
31813
|
+
const merged = update.toolCallId ? mergeToolCallUpdate(state, update.toolCallId, update) : update;
|
|
31814
|
+
const toolEvent = buildToolUseEvent(merged);
|
|
31770
31815
|
if (toolEvent)
|
|
31771
31816
|
state.onToolEvent(toolEvent);
|
|
31772
31817
|
}
|
|
@@ -31793,8 +31838,17 @@ function parseStreamingChunks(state, line) {
|
|
|
31793
31838
|
if (update.sessionUpdate === "usage_update") {
|
|
31794
31839
|
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
31795
31840
|
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
31796
|
-
if (used !== undefined && size !== undefined && size > 0)
|
|
31797
|
-
|
|
31841
|
+
if (used !== undefined && size !== undefined && size > 0) {
|
|
31842
|
+
const cost = normalizeUsageCost(update.cost);
|
|
31843
|
+
const breakdown = normalizeUsageBreakdown(update._meta?.usage);
|
|
31844
|
+
state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
|
|
31845
|
+
}
|
|
31846
|
+
return;
|
|
31847
|
+
}
|
|
31848
|
+
if (update.sessionUpdate === "available_commands_update") {
|
|
31849
|
+
if (Array.isArray(update.availableCommands)) {
|
|
31850
|
+
state.onCommands?.(normalizeAgentCommands(update.availableCommands));
|
|
31851
|
+
}
|
|
31798
31852
|
return;
|
|
31799
31853
|
}
|
|
31800
31854
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
@@ -31844,6 +31898,29 @@ function formatToolCallEvent(update, sessionUpdate) {
|
|
|
31844
31898
|
const statusText = status ? ` (${status})` : "";
|
|
31845
31899
|
return `${emoji2} ${title}${statusText}${summaryText}`;
|
|
31846
31900
|
}
|
|
31901
|
+
function isEmptyToolField(v) {
|
|
31902
|
+
if (v === undefined || v === null)
|
|
31903
|
+
return true;
|
|
31904
|
+
if (typeof v === "string")
|
|
31905
|
+
return v.trim().length === 0;
|
|
31906
|
+
if (Array.isArray(v))
|
|
31907
|
+
return v.length === 0;
|
|
31908
|
+
if (typeof v === "object")
|
|
31909
|
+
return Object.keys(v).length === 0;
|
|
31910
|
+
return false;
|
|
31911
|
+
}
|
|
31912
|
+
function mergeToolCallUpdate(state, toolCallId, update) {
|
|
31913
|
+
const prev = state.toolCalls.get(toolCallId) ?? { toolCallId };
|
|
31914
|
+
const merged = { ...prev };
|
|
31915
|
+
for (const key of ["kind", "title", "rawInput", "content", "rawOutput", "locations", "status"]) {
|
|
31916
|
+
const next = update[key];
|
|
31917
|
+
if (!isEmptyToolField(next))
|
|
31918
|
+
merged[key] = next;
|
|
31919
|
+
}
|
|
31920
|
+
merged.toolCallId = toolCallId;
|
|
31921
|
+
state.toolCalls.set(toolCallId, merged);
|
|
31922
|
+
return merged;
|
|
31923
|
+
}
|
|
31847
31924
|
function buildToolUseEvent(update) {
|
|
31848
31925
|
if (!update)
|
|
31849
31926
|
return null;
|
|
@@ -31961,6 +32038,52 @@ function readFirstStringArray(record3, keys) {
|
|
|
31961
32038
|
}
|
|
31962
32039
|
return;
|
|
31963
32040
|
}
|
|
32041
|
+
function asFiniteNumber(value) {
|
|
32042
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
32043
|
+
}
|
|
32044
|
+
function firstFiniteNumber(record3, keys) {
|
|
32045
|
+
for (const key of keys) {
|
|
32046
|
+
const n = asFiniteNumber(record3[key]);
|
|
32047
|
+
if (n !== undefined)
|
|
32048
|
+
return n;
|
|
32049
|
+
}
|
|
32050
|
+
return;
|
|
32051
|
+
}
|
|
32052
|
+
function normalizeUsageBreakdown(value) {
|
|
32053
|
+
if (!isRecord3(value))
|
|
32054
|
+
return;
|
|
32055
|
+
const out = {};
|
|
32056
|
+
for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
|
|
32057
|
+
const n = firstFiniteNumber(value, aliases);
|
|
32058
|
+
if (n !== undefined)
|
|
32059
|
+
out[key] = n;
|
|
32060
|
+
}
|
|
32061
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
32062
|
+
}
|
|
32063
|
+
function normalizeUsageCost(value) {
|
|
32064
|
+
if (!isRecord3(value))
|
|
32065
|
+
return;
|
|
32066
|
+
const amount = asFiniteNumber(value.amount);
|
|
32067
|
+
const currency = readString(value, "currency");
|
|
32068
|
+
if (amount === undefined && !currency)
|
|
32069
|
+
return;
|
|
32070
|
+
return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
|
|
32071
|
+
}
|
|
32072
|
+
function normalizeAgentCommands(value) {
|
|
32073
|
+
if (!Array.isArray(value))
|
|
32074
|
+
return [];
|
|
32075
|
+
const out = [];
|
|
32076
|
+
for (const entry of value) {
|
|
32077
|
+
if (!isRecord3(entry))
|
|
32078
|
+
continue;
|
|
32079
|
+
const name = readString(entry, "name");
|
|
32080
|
+
if (!name)
|
|
32081
|
+
continue;
|
|
32082
|
+
const description = readString(entry, "description");
|
|
32083
|
+
out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
|
|
32084
|
+
}
|
|
32085
|
+
return out;
|
|
32086
|
+
}
|
|
31964
32087
|
function isRecord3(value) {
|
|
31965
32088
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31966
32089
|
}
|
|
@@ -31986,8 +32109,17 @@ function isGenericToolTitle(kind, title) {
|
|
|
31986
32109
|
}
|
|
31987
32110
|
return false;
|
|
31988
32111
|
}
|
|
32112
|
+
var USAGE_BREAKDOWN_FIELDS;
|
|
31989
32113
|
var init_streaming_prompt = __esm(() => {
|
|
31990
32114
|
init_tool_kind_emoji();
|
|
32115
|
+
USAGE_BREAKDOWN_FIELDS = [
|
|
32116
|
+
["inputTokens", ["inputTokens", "input_tokens"]],
|
|
32117
|
+
["outputTokens", ["outputTokens", "output_tokens"]],
|
|
32118
|
+
["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
|
|
32119
|
+
["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
|
|
32120
|
+
["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
|
|
32121
|
+
["totalTokens", ["totalTokens", "total_tokens"]]
|
|
32122
|
+
];
|
|
31991
32123
|
});
|
|
31992
32124
|
|
|
31993
32125
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
@@ -33497,24 +33629,29 @@ var init_workspace_fs = __esm(() => {
|
|
|
33497
33629
|
});
|
|
33498
33630
|
|
|
33499
33631
|
// src/control/control-service.ts
|
|
33632
|
+
import path15 from "node:path";
|
|
33633
|
+
|
|
33500
33634
|
class ControlService {
|
|
33501
33635
|
deps;
|
|
33502
33636
|
constructor(deps) {
|
|
33503
33637
|
this.deps = deps;
|
|
33504
33638
|
}
|
|
33505
33639
|
workspaceFs = new WorkspaceFs(() => this.deps.workspaces.list().map((w) => ({ name: w.name, cwd: w.cwd })));
|
|
33506
|
-
listDirectory(workspace3,
|
|
33507
|
-
return this.workspaceFs.listDirectory(workspace3,
|
|
33640
|
+
listDirectory(workspace3, path16) {
|
|
33641
|
+
return this.workspaceFs.listDirectory(workspace3, path16);
|
|
33508
33642
|
}
|
|
33509
|
-
readWorkspaceFile(workspace3,
|
|
33510
|
-
return this.workspaceFs.readFile(workspace3,
|
|
33643
|
+
readWorkspaceFile(workspace3, path16) {
|
|
33644
|
+
return this.workspaceFs.readFile(workspace3, path16);
|
|
33511
33645
|
}
|
|
33512
|
-
workspaceGitDiff(workspace3,
|
|
33513
|
-
return this.workspaceFs.gitDiff(workspace3,
|
|
33646
|
+
workspaceGitDiff(workspace3, path16) {
|
|
33647
|
+
return this.workspaceFs.gitDiff(workspace3, path16);
|
|
33514
33648
|
}
|
|
33515
33649
|
searchWorkspace(workspace3, query) {
|
|
33516
33650
|
return this.workspaceFs.search(workspace3, query);
|
|
33517
33651
|
}
|
|
33652
|
+
async uploadFile(input) {
|
|
33653
|
+
return this.deps.uploadStore.save(input.filename, input.content, input.mimeType);
|
|
33654
|
+
}
|
|
33518
33655
|
async getSessionModel(chatKey, alias) {
|
|
33519
33656
|
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33520
33657
|
if (!session3)
|
|
@@ -33658,7 +33795,8 @@ class ControlService {
|
|
|
33658
33795
|
text: input.text,
|
|
33659
33796
|
senderId: input.senderId,
|
|
33660
33797
|
...input.isOwner !== undefined ? { isOwner: input.isOwner } : {},
|
|
33661
|
-
...input.accountId !== undefined ? { accountId: input.accountId } : {}
|
|
33798
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {},
|
|
33799
|
+
...input.media !== undefined ? { media: input.media } : {}
|
|
33662
33800
|
});
|
|
33663
33801
|
}
|
|
33664
33802
|
async runScheduledTurn(input) {
|
|
@@ -33730,6 +33868,32 @@ ${chunk}` : chunk
|
|
|
33730
33868
|
});
|
|
33731
33869
|
emittedChunk = true;
|
|
33732
33870
|
};
|
|
33871
|
+
const incomingMedia = params.media ?? [];
|
|
33872
|
+
const sandboxedMedia = incomingMedia.length ? (() => {
|
|
33873
|
+
const uploadRoot = path15.resolve(this.deps.uploadStore.root);
|
|
33874
|
+
const kept = incomingMedia.filter((ref) => {
|
|
33875
|
+
const resolved = path15.resolve(ref.filePath);
|
|
33876
|
+
return resolved === uploadRoot || resolved.startsWith(uploadRoot + path15.sep);
|
|
33877
|
+
});
|
|
33878
|
+
const dropped = incomingMedia.length - kept.length;
|
|
33879
|
+
if (dropped > 0) {
|
|
33880
|
+
console.warn(`[control] dropped ${dropped} media ref(s) with filePath outside the upload sandbox`);
|
|
33881
|
+
}
|
|
33882
|
+
return kept;
|
|
33883
|
+
})() : incomingMedia;
|
|
33884
|
+
const chatMedia = sandboxedMedia.map((ref) => ({
|
|
33885
|
+
kind: ref.kind,
|
|
33886
|
+
filePath: ref.filePath,
|
|
33887
|
+
mimeType: ref.mimeType,
|
|
33888
|
+
...ref.fileName ? { fileName: ref.fileName } : {},
|
|
33889
|
+
sizeBytes: ref.size,
|
|
33890
|
+
source: {
|
|
33891
|
+
channelId: "relay",
|
|
33892
|
+
accountId: params.accountId ?? "control",
|
|
33893
|
+
chatKey: params.chatKey,
|
|
33894
|
+
messageId: ref.id
|
|
33895
|
+
}
|
|
33896
|
+
}));
|
|
33733
33897
|
try {
|
|
33734
33898
|
const response = await this.deps.agent.chat({
|
|
33735
33899
|
accountId: params.accountId ?? "control",
|
|
@@ -33737,6 +33901,7 @@ ${chunk}` : chunk
|
|
|
33737
33901
|
text: params.text,
|
|
33738
33902
|
metadata: buildControlMetadata(params.senderId, params.isOwner),
|
|
33739
33903
|
abortSignal: controller.signal,
|
|
33904
|
+
...chatMedia.length > 0 ? { media: chatMedia } : {},
|
|
33740
33905
|
reply: async (chunk) => {
|
|
33741
33906
|
emitChunk(chunk);
|
|
33742
33907
|
},
|
|
@@ -33770,7 +33935,17 @@ ${chunk}` : chunk
|
|
|
33770
33935
|
chatKey: params.chatKey,
|
|
33771
33936
|
sessionAlias: params.sessionAlias,
|
|
33772
33937
|
used: usage.used,
|
|
33773
|
-
size: usage.size
|
|
33938
|
+
size: usage.size,
|
|
33939
|
+
...usage.cost ? { cost: usage.cost } : {},
|
|
33940
|
+
...usage.breakdown ? { breakdown: usage.breakdown } : {}
|
|
33941
|
+
});
|
|
33942
|
+
},
|
|
33943
|
+
onCommands: (commands) => {
|
|
33944
|
+
this.deps.events.emit({
|
|
33945
|
+
type: "agent-commands",
|
|
33946
|
+
chatKey: params.chatKey,
|
|
33947
|
+
sessionAlias: params.sessionAlias,
|
|
33948
|
+
commands
|
|
33774
33949
|
});
|
|
33775
33950
|
}
|
|
33776
33951
|
});
|
|
@@ -33859,13 +34034,92 @@ var init_control_service = __esm(() => {
|
|
|
33859
34034
|
init_workspace_fs();
|
|
33860
34035
|
});
|
|
33861
34036
|
|
|
34037
|
+
// src/control/upload-store.ts
|
|
34038
|
+
import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
34039
|
+
import { homedir as homedir12 } from "node:os";
|
|
34040
|
+
import path16 from "node:path";
|
|
34041
|
+
function defaultRootDir() {
|
|
34042
|
+
const home = process.env.HOME ?? homedir12();
|
|
34043
|
+
return path16.join(coreHomeDir(home), "runtime", "uploads");
|
|
34044
|
+
}
|
|
34045
|
+
function sanitizeUploadFilename(raw) {
|
|
34046
|
+
const base = path16.basename(raw).replace(/[/\\]/g, "").replace(/^\.+/, "");
|
|
34047
|
+
const cleaned = base.trim();
|
|
34048
|
+
return cleaned.length > 0 ? cleaned : "file";
|
|
34049
|
+
}
|
|
34050
|
+
|
|
34051
|
+
class UploadStore {
|
|
34052
|
+
rootDir;
|
|
34053
|
+
maxBytes;
|
|
34054
|
+
ttlMs;
|
|
34055
|
+
now;
|
|
34056
|
+
constructor(opts = {}) {
|
|
34057
|
+
this.rootDir = opts.rootDir ?? defaultRootDir();
|
|
34058
|
+
this.maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
34059
|
+
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
34060
|
+
this.now = opts.now ?? (() => new Date);
|
|
34061
|
+
}
|
|
34062
|
+
get root() {
|
|
34063
|
+
return this.rootDir;
|
|
34064
|
+
}
|
|
34065
|
+
async save(filename, base642, mimeType) {
|
|
34066
|
+
if (base642.length > Math.ceil(this.maxBytes * 4 / 3) + 4)
|
|
34067
|
+
throw new Error("file-too-large");
|
|
34068
|
+
const bytes = Buffer.from(base642, "base64");
|
|
34069
|
+
if (bytes.byteLength === 0)
|
|
34070
|
+
throw new Error("empty-file");
|
|
34071
|
+
if (bytes.byteLength > this.maxBytes)
|
|
34072
|
+
throw new Error("file-too-large");
|
|
34073
|
+
const safeName = sanitizeUploadFilename(filename);
|
|
34074
|
+
const { mkdir: mkdir9 } = await import("node:fs/promises");
|
|
34075
|
+
await mkdir9(this.rootDir, { recursive: true });
|
|
34076
|
+
const dir = await mkdtemp2(path16.join(this.rootDir, "u-"));
|
|
34077
|
+
const filePath = path16.join(dir, safeName);
|
|
34078
|
+
await writeFile8(filePath, bytes);
|
|
34079
|
+
return {
|
|
34080
|
+
id: path16.basename(dir),
|
|
34081
|
+
path: filePath,
|
|
34082
|
+
filename: safeName,
|
|
34083
|
+
mimeType,
|
|
34084
|
+
size: bytes.byteLength
|
|
34085
|
+
};
|
|
34086
|
+
}
|
|
34087
|
+
async cleanup() {
|
|
34088
|
+
let entries;
|
|
34089
|
+
try {
|
|
34090
|
+
entries = await readdir5(this.rootDir);
|
|
34091
|
+
} catch {
|
|
34092
|
+
return 0;
|
|
34093
|
+
}
|
|
34094
|
+
const cutoff = this.now().getTime() - this.ttlMs;
|
|
34095
|
+
let removed = 0;
|
|
34096
|
+
for (const name of entries) {
|
|
34097
|
+
const dir = path16.join(this.rootDir, name);
|
|
34098
|
+
try {
|
|
34099
|
+
const info = await stat4(dir);
|
|
34100
|
+
if (info.mtimeMs < cutoff) {
|
|
34101
|
+
await rm10(dir, { recursive: true, force: true });
|
|
34102
|
+
removed += 1;
|
|
34103
|
+
}
|
|
34104
|
+
} catch {}
|
|
34105
|
+
}
|
|
34106
|
+
return removed;
|
|
34107
|
+
}
|
|
34108
|
+
}
|
|
34109
|
+
var DEFAULT_MAX_BYTES, DEFAULT_TTL_MS;
|
|
34110
|
+
var init_upload_store = __esm(() => {
|
|
34111
|
+
init_core_home();
|
|
34112
|
+
DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
34113
|
+
DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
34114
|
+
});
|
|
34115
|
+
|
|
33862
34116
|
// src/config/agent-catalog.ts
|
|
33863
34117
|
import { existsSync as existsSync3 } from "node:fs";
|
|
33864
34118
|
import { delimiter as delimiter2, join as join21 } from "node:path";
|
|
33865
34119
|
function isBinaryOnPath(binary) {
|
|
33866
|
-
const
|
|
34120
|
+
const path17 = process.env.PATH ?? "";
|
|
33867
34121
|
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
33868
|
-
for (const dir of
|
|
34122
|
+
for (const dir of path17.split(delimiter2)) {
|
|
33869
34123
|
if (!dir)
|
|
33870
34124
|
continue;
|
|
33871
34125
|
for (const ext of exts) {
|
|
@@ -33900,6 +34154,57 @@ var init_agent_catalog = __esm(() => {
|
|
|
33900
34154
|
};
|
|
33901
34155
|
});
|
|
33902
34156
|
|
|
34157
|
+
// src/config/config-watcher.ts
|
|
34158
|
+
import { watch } from "node:fs";
|
|
34159
|
+
import { basename as basename3, dirname as dirname12 } from "node:path";
|
|
34160
|
+
function startConfigWatcher(options) {
|
|
34161
|
+
const { configPath, onChange, debounceMs = 250, logger: logger2 } = options;
|
|
34162
|
+
const watchFactory = options.watchFactory ?? watch;
|
|
34163
|
+
const dir = dirname12(configPath);
|
|
34164
|
+
const target = basename3(configPath);
|
|
34165
|
+
let timer;
|
|
34166
|
+
let watcher;
|
|
34167
|
+
const fire = () => {
|
|
34168
|
+
timer = undefined;
|
|
34169
|
+
try {
|
|
34170
|
+
onChange();
|
|
34171
|
+
} catch (error2) {
|
|
34172
|
+
logger2?.error("config.watch.callback_failed", "config watch callback threw", {
|
|
34173
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34174
|
+
});
|
|
34175
|
+
}
|
|
34176
|
+
};
|
|
34177
|
+
try {
|
|
34178
|
+
watcher = watchFactory(dir, { persistent: false }, (_event, filename) => {
|
|
34179
|
+
if (filename !== null && basename3(filename.toString()) !== target)
|
|
34180
|
+
return;
|
|
34181
|
+
if (timer)
|
|
34182
|
+
clearTimeout(timer);
|
|
34183
|
+
timer = setTimeout(fire, debounceMs);
|
|
34184
|
+
});
|
|
34185
|
+
watcher.on("error", (error2) => {
|
|
34186
|
+
logger2?.error("config.watch.error", "config watcher errored", {
|
|
34187
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34188
|
+
});
|
|
34189
|
+
});
|
|
34190
|
+
} catch (error2) {
|
|
34191
|
+
logger2?.error("config.watch.start_failed", "could not start config watcher", {
|
|
34192
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34193
|
+
});
|
|
34194
|
+
}
|
|
34195
|
+
return {
|
|
34196
|
+
close: () => {
|
|
34197
|
+
if (timer) {
|
|
34198
|
+
clearTimeout(timer);
|
|
34199
|
+
timer = undefined;
|
|
34200
|
+
}
|
|
34201
|
+
watcher?.close();
|
|
34202
|
+
watcher = undefined;
|
|
34203
|
+
}
|
|
34204
|
+
};
|
|
34205
|
+
}
|
|
34206
|
+
var init_config_watcher = () => {};
|
|
34207
|
+
|
|
33903
34208
|
// src/main.ts
|
|
33904
34209
|
var exports_main = {};
|
|
33905
34210
|
__export(exports_main, {
|
|
@@ -33910,8 +34215,8 @@ __export(exports_main, {
|
|
|
33910
34215
|
buildApp: () => buildApp
|
|
33911
34216
|
});
|
|
33912
34217
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
33913
|
-
import { homedir as
|
|
33914
|
-
import { dirname as
|
|
34218
|
+
import { homedir as homedir13 } from "node:os";
|
|
34219
|
+
import { dirname as dirname13, join as join22 } from "node:path";
|
|
33915
34220
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
33916
34221
|
function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
|
|
33917
34222
|
const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
|
|
@@ -34402,6 +34707,9 @@ async function buildApp(paths, deps = {}) {
|
|
|
34402
34707
|
const router3 = new CommandRouter(sessions, transport, config4, configStore, logger2, undefined, orchestration3, quota, scheduledService, deps.channel?.supportsScheduledMessages ? { supportsScheduledMessages: deps.channel.supportsScheduledMessages.bind(deps.channel) } : undefined, deps.channel?.nativeSessionListFormat ? deps.channel.nativeSessionListFormat.bind(deps.channel) : undefined, activeTurns);
|
|
34403
34708
|
const agent3 = new ConsoleAgent(router3, logger2);
|
|
34404
34709
|
const controlEvents = createControlEventBus(logger2);
|
|
34710
|
+
const uploadStore = new UploadStore;
|
|
34711
|
+
uploadStore.cleanup();
|
|
34712
|
+
const uploadCleanupInterval = setInterval(() => void uploadStore.cleanup().catch(() => {}), 60 * 60 * 1000);
|
|
34405
34713
|
const control = new ControlService({
|
|
34406
34714
|
agent: agent3,
|
|
34407
34715
|
sessions,
|
|
@@ -34438,12 +34746,35 @@ async function buildApp(paths, deps = {}) {
|
|
|
34438
34746
|
create: async (name, cwd, description) => {
|
|
34439
34747
|
const updated = await configStore.upsertWorkspace(name, cwd, description);
|
|
34440
34748
|
replaceRuntimeConfig(config4, updated);
|
|
34749
|
+
controlEvents.emit({ type: "workspaces-changed" });
|
|
34441
34750
|
return { name, cwd, ...description ? { description } : {} };
|
|
34442
34751
|
},
|
|
34443
34752
|
remove: async (name) => {
|
|
34444
34753
|
const updated = await configStore.removeWorkspace(name);
|
|
34445
34754
|
replaceRuntimeConfig(config4, updated);
|
|
34755
|
+
controlEvents.emit({ type: "workspaces-changed" });
|
|
34446
34756
|
}
|
|
34757
|
+
},
|
|
34758
|
+
uploadStore
|
|
34759
|
+
});
|
|
34760
|
+
const workspaceSignature = (cfg) => JSON.stringify(Object.keys(cfg.workspaces).sort().map((name) => {
|
|
34761
|
+
const ws = cfg.workspaces[name];
|
|
34762
|
+
return [name, ws.cwd, ws.description ?? ""];
|
|
34763
|
+
}));
|
|
34764
|
+
const configWatcher = startConfigWatcher({
|
|
34765
|
+
configPath: paths.configPath,
|
|
34766
|
+
logger: logger2,
|
|
34767
|
+
onChange: () => {
|
|
34768
|
+
const before = workspaceSignature(config4);
|
|
34769
|
+
reloadRuntimeConfig().then(() => {
|
|
34770
|
+
if (workspaceSignature(config4) !== before) {
|
|
34771
|
+
controlEvents.emit({ type: "workspaces-changed" });
|
|
34772
|
+
}
|
|
34773
|
+
}).catch((error2) => {
|
|
34774
|
+
logger2.error("config.reload_failed", "failed to reload config after file change", {
|
|
34775
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34776
|
+
});
|
|
34777
|
+
});
|
|
34447
34778
|
}
|
|
34448
34779
|
});
|
|
34449
34780
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
@@ -34514,6 +34845,8 @@ async function buildApp(paths, deps = {}) {
|
|
|
34514
34845
|
reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
|
|
34515
34846
|
dispose: async () => {
|
|
34516
34847
|
scheduledScheduler.stop();
|
|
34848
|
+
configWatcher.close();
|
|
34849
|
+
clearInterval(uploadCleanupInterval);
|
|
34517
34850
|
if (progressHeartbeatInterval !== undefined) {
|
|
34518
34851
|
clearInterval(progressHeartbeatInterval);
|
|
34519
34852
|
}
|
|
@@ -34574,7 +34907,7 @@ async function main() {
|
|
|
34574
34907
|
}
|
|
34575
34908
|
}
|
|
34576
34909
|
async function prepareChannelMedia(configPath, config4) {
|
|
34577
|
-
const runtimeDir = join22(
|
|
34910
|
+
const runtimeDir = join22(dirname13(configPath), "runtime");
|
|
34578
34911
|
const mediaRootDir = join22(runtimeDir, "media");
|
|
34579
34912
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
34580
34913
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
@@ -34584,12 +34917,12 @@ async function prepareChannelMedia(configPath, config4) {
|
|
|
34584
34917
|
return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
|
|
34585
34918
|
}
|
|
34586
34919
|
function resolveRuntimePaths() {
|
|
34587
|
-
const home = process.env.HOME ??
|
|
34920
|
+
const home = process.env.HOME ?? homedir13();
|
|
34588
34921
|
if (!home) {
|
|
34589
34922
|
throw new Error("Unable to resolve the current user home directory");
|
|
34590
34923
|
}
|
|
34591
34924
|
const configPath = coreEnv("CONFIG") ?? join22(coreHomeDir(home), "config.json");
|
|
34592
|
-
const runtimeDir = join22(
|
|
34925
|
+
const runtimeDir = join22(dirname13(configPath), "runtime");
|
|
34593
34926
|
return {
|
|
34594
34927
|
configPath,
|
|
34595
34928
|
statePath: coreEnv("STATE") ?? join22(coreHomeDir(home), "state.json"),
|
|
@@ -34604,12 +34937,12 @@ function resolveBridgeEntryPath() {
|
|
|
34604
34937
|
return fileURLToPath5(new URL("./bridge/bridge-main.ts", import.meta.url));
|
|
34605
34938
|
}
|
|
34606
34939
|
function resolveAppLogPath(configPath) {
|
|
34607
|
-
const rootDir =
|
|
34940
|
+
const rootDir = dirname13(configPath);
|
|
34608
34941
|
const runtimeDir = join22(rootDir, "runtime");
|
|
34609
34942
|
return join22(runtimeDir, "app.log");
|
|
34610
34943
|
}
|
|
34611
34944
|
function resolvePerfLogPath(configPath) {
|
|
34612
|
-
const rootDir =
|
|
34945
|
+
const rootDir = dirname13(configPath);
|
|
34613
34946
|
const runtimeDir = join22(rootDir, "runtime");
|
|
34614
34947
|
return join22(runtimeDir, "perf.log");
|
|
34615
34948
|
}
|
|
@@ -34656,7 +34989,9 @@ var init_main = __esm(async () => {
|
|
|
34656
34989
|
init_render_text();
|
|
34657
34990
|
init_quota_manager();
|
|
34658
34991
|
init_control_service();
|
|
34992
|
+
init_upload_store();
|
|
34659
34993
|
init_agent_catalog();
|
|
34994
|
+
init_config_watcher();
|
|
34660
34995
|
init_perf_tracer();
|
|
34661
34996
|
init_bootstrap();
|
|
34662
34997
|
init_i18n();
|
|
@@ -34849,12 +35184,12 @@ var init_config_check = __esm(async () => {
|
|
|
34849
35184
|
});
|
|
34850
35185
|
|
|
34851
35186
|
// src/doctor/checks/daemon-check.ts
|
|
34852
|
-
import { readdir as
|
|
35187
|
+
import { readdir as readdir6, readFile as readFile15, rm as rm11 } from "node:fs/promises";
|
|
34853
35188
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
34854
|
-
import { homedir as
|
|
35189
|
+
import { homedir as homedir14 } from "node:os";
|
|
34855
35190
|
import { join as join23 } from "node:path";
|
|
34856
35191
|
async function checkDaemon(options = {}) {
|
|
34857
|
-
const home = options.home ?? process.env.HOME ??
|
|
35192
|
+
const home = options.home ?? process.env.HOME ?? homedir14();
|
|
34858
35193
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
34859
35194
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
34860
35195
|
home,
|
|
@@ -34988,22 +35323,22 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
34988
35323
|
}
|
|
34989
35324
|
async function defaultListConsumerLocks(runtimeDir) {
|
|
34990
35325
|
try {
|
|
34991
|
-
return await
|
|
35326
|
+
return await readdir6(runtimeDir);
|
|
34992
35327
|
} catch {
|
|
34993
35328
|
return [];
|
|
34994
35329
|
}
|
|
34995
35330
|
}
|
|
34996
|
-
async function defaultReadConsumerLock(
|
|
35331
|
+
async function defaultReadConsumerLock(path17) {
|
|
34997
35332
|
try {
|
|
34998
|
-
const raw = await readFile15(
|
|
35333
|
+
const raw = await readFile15(path17, "utf8");
|
|
34999
35334
|
const parsed = JSON.parse(raw);
|
|
35000
35335
|
return typeof parsed.pid === "number" ? { pid: parsed.pid } : null;
|
|
35001
35336
|
} catch {
|
|
35002
35337
|
return null;
|
|
35003
35338
|
}
|
|
35004
35339
|
}
|
|
35005
|
-
async function defaultRemoveConsumerLock(
|
|
35006
|
-
await
|
|
35340
|
+
async function defaultRemoveConsumerLock(path17) {
|
|
35341
|
+
await rm11(path17, { force: true });
|
|
35007
35342
|
}
|
|
35008
35343
|
function resolveCliEntryPath() {
|
|
35009
35344
|
return process.argv[1] ?? fileURLToPath6(import.meta.url);
|
|
@@ -35018,11 +35353,11 @@ var init_daemon_check = __esm(() => {
|
|
|
35018
35353
|
});
|
|
35019
35354
|
|
|
35020
35355
|
// src/doctor/checks/logs-check.ts
|
|
35021
|
-
import { stat as
|
|
35022
|
-
import { basename as
|
|
35023
|
-
import { homedir as
|
|
35356
|
+
import { stat as stat5, readdir as readdir7 } from "node:fs/promises";
|
|
35357
|
+
import { basename as basename4, join as join24 } from "node:path";
|
|
35358
|
+
import { homedir as homedir15 } from "node:os";
|
|
35024
35359
|
async function checkLogs(options = {}) {
|
|
35025
|
-
const home = options.home ?? process.env.HOME ??
|
|
35360
|
+
const home = options.home ?? process.env.HOME ?? homedir15();
|
|
35026
35361
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35027
35362
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35028
35363
|
home,
|
|
@@ -35049,18 +35384,18 @@ async function checkLogs(options = {}) {
|
|
|
35049
35384
|
`error: ${formatError6(error2)}`
|
|
35050
35385
|
]);
|
|
35051
35386
|
}
|
|
35052
|
-
const baseNames = [
|
|
35387
|
+
const baseNames = [basename4(paths.appLog), basename4(paths.stdoutLog), basename4(paths.stderrLog)];
|
|
35053
35388
|
const tracked = new Set(baseNames);
|
|
35054
35389
|
const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
|
|
35055
35390
|
const files = [];
|
|
35056
35391
|
for (const name of matched) {
|
|
35057
|
-
const
|
|
35392
|
+
const path17 = join24(paths.runtimeDir, name);
|
|
35058
35393
|
try {
|
|
35059
|
-
const fileStat = await probe.stat(
|
|
35394
|
+
const fileStat = await probe.stat(path17);
|
|
35060
35395
|
if (fileStat.isDirectory()) {
|
|
35061
35396
|
continue;
|
|
35062
35397
|
}
|
|
35063
|
-
files.push({ name, path:
|
|
35398
|
+
files.push({ name, path: path17, size: fileStat.size });
|
|
35064
35399
|
} catch {
|
|
35065
35400
|
continue;
|
|
35066
35401
|
}
|
|
@@ -35134,8 +35469,8 @@ function formatBytes(bytes) {
|
|
|
35134
35469
|
}
|
|
35135
35470
|
function createLogsFsProbe() {
|
|
35136
35471
|
return {
|
|
35137
|
-
stat: async (
|
|
35138
|
-
readdir: async (
|
|
35472
|
+
stat: async (path17) => await stat5(path17),
|
|
35473
|
+
readdir: async (path17) => await readdir7(path17)
|
|
35139
35474
|
};
|
|
35140
35475
|
}
|
|
35141
35476
|
function formatError6(error2) {
|
|
@@ -35204,9 +35539,9 @@ var init_orchestration_health = __esm(() => {
|
|
|
35204
35539
|
});
|
|
35205
35540
|
|
|
35206
35541
|
// src/doctor/checks/orchestration-socket-check.ts
|
|
35207
|
-
import { homedir as
|
|
35542
|
+
import { homedir as homedir16 } from "node:os";
|
|
35208
35543
|
async function checkOrchestrationSocket(options = {}) {
|
|
35209
|
-
const home = options.home ?? process.env.HOME ??
|
|
35544
|
+
const home = options.home ?? process.env.HOME ?? homedir16();
|
|
35210
35545
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35211
35546
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35212
35547
|
home,
|
|
@@ -35379,11 +35714,11 @@ var init_plugin_check = __esm(async () => {
|
|
|
35379
35714
|
|
|
35380
35715
|
// src/doctor/checks/runtime-check.ts
|
|
35381
35716
|
import { constants } from "node:fs";
|
|
35382
|
-
import { access as access4, stat as
|
|
35383
|
-
import { dirname as
|
|
35384
|
-
import { homedir as
|
|
35717
|
+
import { access as access4, stat as stat6 } from "node:fs/promises";
|
|
35718
|
+
import { dirname as dirname14 } from "node:path";
|
|
35719
|
+
import { homedir as homedir17 } from "node:os";
|
|
35385
35720
|
async function checkRuntime(options = {}) {
|
|
35386
|
-
const home = options.home ?? process.env.HOME ??
|
|
35721
|
+
const home = options.home ?? process.env.HOME ?? homedir17();
|
|
35387
35722
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35388
35723
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35389
35724
|
home,
|
|
@@ -35477,107 +35812,107 @@ function formatMode(mode) {
|
|
|
35477
35812
|
}
|
|
35478
35813
|
function createRuntimeFsProbe() {
|
|
35479
35814
|
return {
|
|
35480
|
-
stat: async (
|
|
35481
|
-
access: async (
|
|
35815
|
+
stat: async (path17) => await stat6(path17),
|
|
35816
|
+
access: async (path17, mode) => await access4(path17, mode)
|
|
35482
35817
|
};
|
|
35483
35818
|
}
|
|
35484
|
-
async function checkDirectoryCreatable(label,
|
|
35819
|
+
async function checkDirectoryCreatable(label, path17, probe, platform) {
|
|
35485
35820
|
try {
|
|
35486
|
-
const stats = await probe.stat(
|
|
35821
|
+
const stats = await probe.stat(path17);
|
|
35487
35822
|
if (!stats.isDirectory()) {
|
|
35488
35823
|
return {
|
|
35489
35824
|
ok: false,
|
|
35490
|
-
detail: `${label}: ${
|
|
35825
|
+
detail: `${label}: ${path17} (exists but is not a directory)`
|
|
35491
35826
|
};
|
|
35492
35827
|
}
|
|
35493
|
-
await probe.access(
|
|
35828
|
+
await probe.access(path17, directoryAccessMode(platform));
|
|
35494
35829
|
return {
|
|
35495
35830
|
ok: true,
|
|
35496
|
-
detail: `${label}: ${
|
|
35831
|
+
detail: `${label}: ${path17} (writable)`
|
|
35497
35832
|
};
|
|
35498
35833
|
} catch (error2) {
|
|
35499
35834
|
if (!isMissingPathError2(error2)) {
|
|
35500
35835
|
return {
|
|
35501
35836
|
ok: false,
|
|
35502
|
-
detail: `${label}: ${
|
|
35837
|
+
detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
|
|
35503
35838
|
};
|
|
35504
35839
|
}
|
|
35505
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
35840
|
+
const parentCheck = await checkCreatableAncestorDirectory(path17, probe, platform);
|
|
35506
35841
|
if (!parentCheck.ok) {
|
|
35507
35842
|
return {
|
|
35508
35843
|
ok: false,
|
|
35509
|
-
detail: `${label}: ${
|
|
35844
|
+
detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
|
|
35510
35845
|
};
|
|
35511
35846
|
}
|
|
35512
35847
|
return {
|
|
35513
35848
|
ok: true,
|
|
35514
|
-
detail: `${label}: ${
|
|
35849
|
+
detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
|
|
35515
35850
|
};
|
|
35516
35851
|
}
|
|
35517
35852
|
}
|
|
35518
|
-
async function checkFileCreatable(label,
|
|
35853
|
+
async function checkFileCreatable(label, path17, probe, platform) {
|
|
35519
35854
|
try {
|
|
35520
|
-
const stats = await probe.stat(
|
|
35855
|
+
const stats = await probe.stat(path17);
|
|
35521
35856
|
if (stats.isDirectory()) {
|
|
35522
35857
|
return {
|
|
35523
35858
|
ok: false,
|
|
35524
|
-
detail: `${label}: ${
|
|
35859
|
+
detail: `${label}: ${path17} (exists but is a directory)`
|
|
35525
35860
|
};
|
|
35526
35861
|
}
|
|
35527
|
-
await probe.access(
|
|
35862
|
+
await probe.access(path17, constants.W_OK);
|
|
35528
35863
|
return {
|
|
35529
35864
|
ok: true,
|
|
35530
|
-
detail: `${label}: ${
|
|
35865
|
+
detail: `${label}: ${path17} (writable)`
|
|
35531
35866
|
};
|
|
35532
35867
|
} catch (error2) {
|
|
35533
35868
|
if (!isMissingPathError2(error2)) {
|
|
35534
35869
|
return {
|
|
35535
35870
|
ok: false,
|
|
35536
|
-
detail: `${label}: ${
|
|
35871
|
+
detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
|
|
35537
35872
|
};
|
|
35538
35873
|
}
|
|
35539
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
35874
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname14(path17), probe, platform);
|
|
35540
35875
|
if (!parentCheck.ok) {
|
|
35541
35876
|
return {
|
|
35542
35877
|
ok: false,
|
|
35543
|
-
detail: `${label}: ${
|
|
35878
|
+
detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
|
|
35544
35879
|
};
|
|
35545
35880
|
}
|
|
35546
35881
|
return {
|
|
35547
35882
|
ok: true,
|
|
35548
|
-
detail: `${label}: ${
|
|
35883
|
+
detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
|
|
35549
35884
|
};
|
|
35550
35885
|
}
|
|
35551
35886
|
}
|
|
35552
|
-
async function checkCreatableAncestorDirectory(
|
|
35887
|
+
async function checkCreatableAncestorDirectory(path17, probe, platform) {
|
|
35553
35888
|
try {
|
|
35554
|
-
const stats = await probe.stat(
|
|
35889
|
+
const stats = await probe.stat(path17);
|
|
35555
35890
|
if (!stats.isDirectory()) {
|
|
35556
35891
|
return {
|
|
35557
35892
|
ok: false,
|
|
35558
|
-
creatableFrom:
|
|
35559
|
-
blockingPath:
|
|
35893
|
+
creatableFrom: path17,
|
|
35894
|
+
blockingPath: path17
|
|
35560
35895
|
};
|
|
35561
35896
|
}
|
|
35562
|
-
await probe.access(
|
|
35897
|
+
await probe.access(path17, directoryAccessMode(platform));
|
|
35563
35898
|
return {
|
|
35564
35899
|
ok: true,
|
|
35565
|
-
creatableFrom:
|
|
35900
|
+
creatableFrom: path17
|
|
35566
35901
|
};
|
|
35567
35902
|
} catch (error2) {
|
|
35568
35903
|
if (!isMissingPathError2(error2)) {
|
|
35569
35904
|
return {
|
|
35570
35905
|
ok: false,
|
|
35571
|
-
creatableFrom:
|
|
35572
|
-
blockingPath:
|
|
35906
|
+
creatableFrom: path17,
|
|
35907
|
+
blockingPath: path17
|
|
35573
35908
|
};
|
|
35574
35909
|
}
|
|
35575
|
-
const parent =
|
|
35576
|
-
if (parent ===
|
|
35910
|
+
const parent = dirname14(path17);
|
|
35911
|
+
if (parent === path17) {
|
|
35577
35912
|
return {
|
|
35578
35913
|
ok: false,
|
|
35579
|
-
creatableFrom:
|
|
35580
|
-
blockingPath:
|
|
35914
|
+
creatableFrom: path17,
|
|
35915
|
+
blockingPath: path17
|
|
35581
35916
|
};
|
|
35582
35917
|
}
|
|
35583
35918
|
const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
|
|
@@ -36016,10 +36351,10 @@ var init_render_doctor = __esm(() => {
|
|
|
36016
36351
|
});
|
|
36017
36352
|
|
|
36018
36353
|
// src/doctor/doctor.ts
|
|
36019
|
-
import { homedir as
|
|
36354
|
+
import { homedir as homedir18 } from "node:os";
|
|
36020
36355
|
import { join as join25 } from "node:path";
|
|
36021
36356
|
async function runDoctor(options = {}, deps = {}) {
|
|
36022
|
-
const home = deps.home ?? process.env.HOME ??
|
|
36357
|
+
const home = deps.home ?? process.env.HOME ?? homedir18();
|
|
36023
36358
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
36024
36359
|
const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
|
|
36025
36360
|
const runners = [
|
|
@@ -36356,8 +36691,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
36356
36691
|
// src/cli.ts
|
|
36357
36692
|
init_core_home();
|
|
36358
36693
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
36359
|
-
import { homedir as
|
|
36360
|
-
import { dirname as
|
|
36694
|
+
import { homedir as homedir19 } from "node:os";
|
|
36695
|
+
import { dirname as dirname15, join as join26, sep as sep2 } from "node:path";
|
|
36361
36696
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
36362
36697
|
|
|
36363
36698
|
// src/runtime/migrate-core-home.ts
|
|
@@ -52962,7 +53297,7 @@ function decodeFirstRunOnboarding(raw) {
|
|
|
52962
53297
|
return null;
|
|
52963
53298
|
}
|
|
52964
53299
|
function requireHome2() {
|
|
52965
|
-
const home = process.env.HOME ??
|
|
53300
|
+
const home = process.env.HOME ?? homedir19();
|
|
52966
53301
|
if (!home) {
|
|
52967
53302
|
throw new Error("Unable to resolve the current user home directory");
|
|
52968
53303
|
}
|
|
@@ -52986,7 +53321,7 @@ function safeDaemonLogPaths() {
|
|
|
52986
53321
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
52987
53322
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
52988
53323
|
return {
|
|
52989
|
-
appLog: join26(
|
|
53324
|
+
appLog: join26(dirname15(configPath), "runtime", "app.log"),
|
|
52990
53325
|
stderrLog: paths.stderrLog
|
|
52991
53326
|
};
|
|
52992
53327
|
} catch {
|