@ganglion/xacpx 0.12.1 → 0.14.0
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 +16 -8
- package/dist/bridge/bridge-main.js +139 -11
- package/dist/cli.js +528 -130
- package/dist/commands/handlers/session-handler.d.ts +4 -9
- package/dist/commands/parse-command.d.ts +3 -0
- package/dist/commands/router-types.d.ts +2 -5
- package/dist/control/control-event-bus.d.ts +8 -0
- package/dist/control/control-service.d.ts +23 -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/sessions/session-service.d.ts +1 -0
- package/dist/state/types.d.ts +4 -0
- package/dist/transport/types.d.ts +49 -4
- package/dist/weixin/agent/interface.d.ts +4 -4
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -141,6 +141,7 @@ ${detail}`,
|
|
|
141
141
|
sessionBlockedByTasks: (alias, count) => `Session "${alias}" has ${count} unfinished task(s). Cancel or wait for them to complete first.`,
|
|
142
142
|
sessionBlockedByTasksHint: "Use /tasks to list tasks, or /task cancel <id> to cancel one.",
|
|
143
143
|
sessionRemoved: (alias) => `Session "${alias}" removed.`,
|
|
144
|
+
sessionArchived: (alias) => `Archived session "${alias}". Send a message to restore it.`,
|
|
144
145
|
sessionRemovedWasActive: "This was the active session. Its chat context has been cleared.",
|
|
145
146
|
sessionRemovedWasActivePromoted: (alias) => `This was the active session. Switched back to the previous session "${alias}".`,
|
|
146
147
|
sessionTransportShared: (transportSession, count) => `Note: backend session "${transportSession}" is still referenced by ${count} other session(s) and was not closed.`,
|
|
@@ -1236,6 +1237,7 @@ ${detail}`,
|
|
|
1236
1237
|
sessionBlockedByTasks: (alias, count) => `会话「${alias}」下还有 ${count} 个未结束的任务,请先取消或等待完成。`,
|
|
1237
1238
|
sessionBlockedByTasksHint: "使用 /tasks 查看任务列表,或 /task cancel <id> 取消任务。",
|
|
1238
1239
|
sessionRemoved: (alias) => `已删除会话「${alias}」。`,
|
|
1240
|
+
sessionArchived: (alias) => `已归档会话「${alias}」。发送消息即可恢复。`,
|
|
1239
1241
|
sessionRemovedWasActive: "该会话是当前活跃会话,已自动清除相关聊天上下文。",
|
|
1240
1242
|
sessionRemovedWasActivePromoted: (alias) => `该会话是当前活跃会话,已切换回上一个会话「${alias}」。`,
|
|
1241
1243
|
sessionTransportShared: (transportSession, count) => `提示:后端会话「${transportSession}」仍被其他 ${count} 个会话引用,未关闭。`,
|
|
@@ -4695,6 +4697,7 @@ function parseConfig(raw, options = {}) {
|
|
|
4695
4697
|
...typeof transport.command === "string" ? { command: transport.command } : {},
|
|
4696
4698
|
...typeof transport.sessionInitTimeoutMs === "number" ? { sessionInitTimeoutMs: transport.sessionInitTimeoutMs } : {},
|
|
4697
4699
|
...typeof transport.permissionPolicy === "string" ? { permissionPolicy: transport.permissionPolicy } : {},
|
|
4700
|
+
...typeof transport.preferLocalAgents === "boolean" ? { preferLocalAgents: transport.preferLocalAgents } : {},
|
|
4698
4701
|
type: transportType,
|
|
4699
4702
|
permissionMode,
|
|
4700
4703
|
nonInteractivePermissions,
|
|
@@ -20399,6 +20402,9 @@ function parseCommand(input) {
|
|
|
20399
20402
|
if (command === "/session" && parts[1] === "rm" && parts[2] && parts.length === 3) {
|
|
20400
20403
|
return { kind: "session.rm", alias: parts[2] };
|
|
20401
20404
|
}
|
|
20405
|
+
if (command === "/session" && parts[1] === "archive" && parts[2] && parts.length === 3) {
|
|
20406
|
+
return { kind: "session.archive", alias: parts[2] };
|
|
20407
|
+
}
|
|
20402
20408
|
if (command === "/ssn") {
|
|
20403
20409
|
if (parts.length === 1) {
|
|
20404
20410
|
return { kind: "session.native.list" };
|
|
@@ -20645,7 +20651,7 @@ function parseCommand(input) {
|
|
|
20645
20651
|
return { kind: "session.shortcut.new", agent: parts[2], ...shortcutTarget };
|
|
20646
20652
|
}
|
|
20647
20653
|
}
|
|
20648
|
-
if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm") {
|
|
20654
|
+
if (command === "/session" && parts[1] && parts[1] !== "new" && parts[1] !== "attach" && parts[1] !== "reset" && parts[1] !== "rm" && parts[1] !== "archive") {
|
|
20649
20655
|
const shortcutTarget = readSessionShortcutTarget(parts, 2);
|
|
20650
20656
|
if (shortcutTarget) {
|
|
20651
20657
|
return { kind: "session.shortcut", agent: parts[1], ...shortcutTarget };
|
|
@@ -21109,6 +21115,7 @@ var init_command_policy = __esm(() => {
|
|
|
21109
21115
|
COMMAND_KIND_TO_LABEL = {
|
|
21110
21116
|
"session.reset": "/clear",
|
|
21111
21117
|
"session.rm": "/session rm",
|
|
21118
|
+
"session.archive": "/session archive",
|
|
21112
21119
|
"session.tail": "/session tail",
|
|
21113
21120
|
"replymode.set": "/replymode",
|
|
21114
21121
|
"replymode.reset": "/replymode reset",
|
|
@@ -22326,9 +22333,9 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22326
22333
|
}
|
|
22327
22334
|
let transportTeardownWarning;
|
|
22328
22335
|
const shouldTeardownTransport = sharedAliasCount === 0;
|
|
22329
|
-
if (shouldTeardownTransport && context.transport.
|
|
22336
|
+
if (shouldTeardownTransport && context.transport.deleteSession) {
|
|
22330
22337
|
try {
|
|
22331
|
-
await context.transport.
|
|
22338
|
+
await context.transport.deleteSession(session3);
|
|
22332
22339
|
} catch (error2) {
|
|
22333
22340
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
22334
22341
|
transportTeardownWarning = message;
|
|
@@ -22361,7 +22368,19 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22361
22368
|
return { text: lines.join(`
|
|
22362
22369
|
`) };
|
|
22363
22370
|
}
|
|
22364
|
-
async function
|
|
22371
|
+
async function handleSessionArchive(context, chatKey, alias, archive) {
|
|
22372
|
+
const internalAlias = await context.sessions.resolveAliasForChat(chatKey, alias);
|
|
22373
|
+
const session3 = await context.sessions.getSession(internalAlias);
|
|
22374
|
+
if (!session3) {
|
|
22375
|
+
return { text: t().session.sessionNotFound(alias) };
|
|
22376
|
+
}
|
|
22377
|
+
await archive(internalAlias);
|
|
22378
|
+
return { text: t().session.sessionArchived(alias) };
|
|
22379
|
+
}
|
|
22380
|
+
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22381
|
+
if (session3.archived) {
|
|
22382
|
+
await context.sessions.setArchived(session3.alias, false);
|
|
22383
|
+
}
|
|
22365
22384
|
const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
|
|
22366
22385
|
if (!session3.replyMode)
|
|
22367
22386
|
session3.replyMode = effectiveReplyMode;
|
|
@@ -22393,7 +22412,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22393
22412
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session3, chatKey, text, replyContextToken, accountId);
|
|
22394
22413
|
try {
|
|
22395
22414
|
const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
|
|
22396
|
-
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage);
|
|
22415
|
+
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands);
|
|
22397
22416
|
if (claimHumanReply) {
|
|
22398
22417
|
try {
|
|
22399
22418
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -22413,23 +22432,23 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22413
22432
|
throw error2;
|
|
22414
22433
|
}
|
|
22415
22434
|
}
|
|
22416
|
-
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22435
|
+
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22417
22436
|
try {
|
|
22418
|
-
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22437
|
+
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22419
22438
|
} catch (error2) {
|
|
22420
22439
|
const recovered = await context.recovery.tryRecoverMissingSession(session3, error2);
|
|
22421
22440
|
if (recovered) {
|
|
22422
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22441
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22423
22442
|
}
|
|
22424
22443
|
return context.recovery.renderTransportError(session3, error2);
|
|
22425
22444
|
}
|
|
22426
22445
|
}
|
|
22427
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22446
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands) {
|
|
22428
22447
|
const session3 = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
22429
22448
|
if (!session3) {
|
|
22430
22449
|
return { text: t().session.noCurrent };
|
|
22431
22450
|
}
|
|
22432
|
-
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22451
|
+
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
22433
22452
|
}
|
|
22434
22453
|
function toCoordinatorRouteChatMetadata(metadata) {
|
|
22435
22454
|
if (!metadata) {
|
|
@@ -24688,7 +24707,7 @@ class CommandRouter {
|
|
|
24688
24707
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
24689
24708
|
this.activeTurns = activeTurns;
|
|
24690
24709
|
}
|
|
24691
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
24710
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
|
|
24692
24711
|
const startedAt = Date.now();
|
|
24693
24712
|
let command = parseCommand(input);
|
|
24694
24713
|
if (metadata?.channel === "control" && command.kind !== "prompt") {
|
|
@@ -24795,6 +24814,8 @@ class CommandRouter {
|
|
|
24795
24814
|
return await handleSessionTail(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.lines);
|
|
24796
24815
|
case "session.rm":
|
|
24797
24816
|
return await handleSessionRemove(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias);
|
|
24817
|
+
case "session.archive":
|
|
24818
|
+
return await handleSessionArchive(this.createSessionHandlerContext(undefined, perfSpan), chatKey, command.alias, (internalAlias) => this.archiveSessionWithTransport(internalAlias));
|
|
24798
24819
|
case "groups":
|
|
24799
24820
|
return await handleGroupList(this.createHandlerContext(), chatKey, command.filter);
|
|
24800
24821
|
case "group.new":
|
|
@@ -24848,16 +24869,16 @@ class CommandRouter {
|
|
|
24848
24869
|
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
24849
24870
|
transient: true
|
|
24850
24871
|
};
|
|
24851
|
-
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24872
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24852
24873
|
}
|
|
24853
24874
|
if (metadata?.scheduledSessionAlias) {
|
|
24854
24875
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
24855
24876
|
if (!scheduledSession) {
|
|
24856
24877
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
24857
24878
|
}
|
|
24858
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24879
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24859
24880
|
}
|
|
24860
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24881
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage, onCommands);
|
|
24861
24882
|
}
|
|
24862
24883
|
}
|
|
24863
24884
|
});
|
|
@@ -24943,6 +24964,82 @@ class CommandRouter {
|
|
|
24943
24964
|
await release();
|
|
24944
24965
|
}
|
|
24945
24966
|
}
|
|
24967
|
+
async removeSessionWithTransport(internalAlias) {
|
|
24968
|
+
const session3 = await this.sessions.getSession(internalAlias);
|
|
24969
|
+
if (!session3) {
|
|
24970
|
+
throw new Error(`session "${internalAlias}" does not exist`);
|
|
24971
|
+
}
|
|
24972
|
+
if (this.orchestration) {
|
|
24973
|
+
const blocking = await this.orchestration.listSessionBlockingTasks(session3.transportSession);
|
|
24974
|
+
if (blocking.length > 0) {
|
|
24975
|
+
throw new Error(`session "${internalAlias}" has ${blocking.length} blocking task(s); cancel them before deleting`);
|
|
24976
|
+
}
|
|
24977
|
+
}
|
|
24978
|
+
const sharedAliasCount = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias);
|
|
24979
|
+
const { wasActive } = await this.sessions.removeSession(internalAlias);
|
|
24980
|
+
if (this.orchestration) {
|
|
24981
|
+
try {
|
|
24982
|
+
await this.orchestration.purgeSessionReferences(session3.transportSession);
|
|
24983
|
+
} catch (error2) {
|
|
24984
|
+
await this.logger.error("session.orchestration_purge_failed", "failed to purge orchestration references after web remove", {
|
|
24985
|
+
alias: internalAlias,
|
|
24986
|
+
transportSession: session3.transportSession,
|
|
24987
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
24988
|
+
});
|
|
24989
|
+
}
|
|
24990
|
+
}
|
|
24991
|
+
let transportTornDown = false;
|
|
24992
|
+
let transportTeardownWarning;
|
|
24993
|
+
if (sharedAliasCount === 0 && this.transport.deleteSession) {
|
|
24994
|
+
try {
|
|
24995
|
+
await this.transport.deleteSession(session3);
|
|
24996
|
+
transportTornDown = true;
|
|
24997
|
+
} catch (error2) {
|
|
24998
|
+
transportTeardownWarning = error2 instanceof Error ? error2.message : String(error2);
|
|
24999
|
+
await this.logger.error("session.transport_delete_failed", "failed to delete acpx session after logical remove", {
|
|
25000
|
+
alias: internalAlias,
|
|
25001
|
+
transportSession: session3.transportSession,
|
|
25002
|
+
message: transportTeardownWarning
|
|
25003
|
+
});
|
|
25004
|
+
}
|
|
25005
|
+
}
|
|
25006
|
+
return {
|
|
25007
|
+
wasActive,
|
|
25008
|
+
sharedAliasCount,
|
|
25009
|
+
transportTornDown,
|
|
25010
|
+
...transportTeardownWarning ? { transportTeardownWarning } : {}
|
|
25011
|
+
};
|
|
25012
|
+
}
|
|
25013
|
+
async archiveSessionWithTransport(internalAlias) {
|
|
25014
|
+
const session3 = await this.sessions.getSession(internalAlias);
|
|
25015
|
+
if (!session3) {
|
|
25016
|
+
throw new Error(`session "${internalAlias}" does not exist`);
|
|
25017
|
+
}
|
|
25018
|
+
if (this.activeTurns?.isActiveAnywhere(internalAlias)) {
|
|
25019
|
+
throw new Error(`session "${internalAlias}" has a running turn; stop it before archiving`);
|
|
25020
|
+
}
|
|
25021
|
+
const shared = this.sessions.countAliasesSharingTransport(session3.transportSession, internalAlias) > 0;
|
|
25022
|
+
if (!shared) {
|
|
25023
|
+
try {
|
|
25024
|
+
await this.transport.cancel(session3);
|
|
25025
|
+
} catch {}
|
|
25026
|
+
if (this.transport.removeSession) {
|
|
25027
|
+
try {
|
|
25028
|
+
await this.transport.removeSession(session3);
|
|
25029
|
+
} catch (error2) {
|
|
25030
|
+
await this.logger.error("session.archive_close_failed", "failed to close acpx session on archive", {
|
|
25031
|
+
alias: internalAlias,
|
|
25032
|
+
transportSession: session3.transportSession,
|
|
25033
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
25034
|
+
});
|
|
25035
|
+
}
|
|
25036
|
+
}
|
|
25037
|
+
}
|
|
25038
|
+
await this.sessions.setArchived(internalAlias, true);
|
|
25039
|
+
}
|
|
25040
|
+
async unarchiveSession(internalAlias) {
|
|
25041
|
+
await this.sessions.setArchived(internalAlias, false);
|
|
25042
|
+
}
|
|
24946
25043
|
async listNativeSessionsForControl(agent3, workspace3) {
|
|
24947
25044
|
const listAgentSessions = this.transport.listAgentSessions?.bind(this.transport);
|
|
24948
25045
|
if (!listAgentSessions)
|
|
@@ -25005,7 +25102,7 @@ class CommandRouter {
|
|
|
25005
25102
|
setModelTransportSession: (session3, modelId) => this.setModelTransportSession(session3, modelId),
|
|
25006
25103
|
getModelTransportSession: (session3) => this.getModelTransportSession(session3),
|
|
25007
25104
|
cancelTransportSession: (session3) => this.cancelTransportSession(session3),
|
|
25008
|
-
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)
|
|
25105
|
+
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)
|
|
25009
25106
|
};
|
|
25010
25107
|
}
|
|
25011
25108
|
createSessionRenderRecoveryOps() {
|
|
@@ -25184,7 +25281,7 @@ class CommandRouter {
|
|
|
25184
25281
|
async checkTransportSession(session3) {
|
|
25185
25282
|
return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
|
|
25186
25283
|
}
|
|
25187
|
-
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
25284
|
+
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage, onCommands) {
|
|
25188
25285
|
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
25189
25286
|
let done = false;
|
|
25190
25287
|
let abortRequested = false;
|
|
@@ -25243,7 +25340,8 @@ class CommandRouter {
|
|
|
25243
25340
|
...onToolEvent ? { onToolEvent } : {},
|
|
25244
25341
|
...onThought ? { onThought } : {},
|
|
25245
25342
|
...onPlan ? { onPlan } : {},
|
|
25246
|
-
...onUsage ? { onUsage } : {}
|
|
25343
|
+
...onUsage ? { onUsage } : {},
|
|
25344
|
+
...onCommands ? { onCommands } : {}
|
|
25247
25345
|
}));
|
|
25248
25346
|
} catch (error2) {
|
|
25249
25347
|
localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
|
|
@@ -25429,7 +25527,7 @@ class ConsoleAgent {
|
|
|
25429
25527
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
25430
25528
|
})) : undefined;
|
|
25431
25529
|
request.perfSpan?.mark("agent.dispatched");
|
|
25432
|
-
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);
|
|
25530
|
+
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);
|
|
25433
25531
|
}
|
|
25434
25532
|
isKnownCommand(text) {
|
|
25435
25533
|
return isKnownXacpxCommandText(text);
|
|
@@ -29659,6 +29757,10 @@ class SessionService {
|
|
|
29659
29757
|
const previousCurrent = prevCtx?.current_session;
|
|
29660
29758
|
const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
|
|
29661
29759
|
session3.last_used_at = new Date().toISOString();
|
|
29760
|
+
if (session3.archived) {
|
|
29761
|
+
delete session3.archived;
|
|
29762
|
+
delete session3.archived_at;
|
|
29763
|
+
}
|
|
29662
29764
|
const nextCtx = { ...prevCtx, current_session: internalAlias };
|
|
29663
29765
|
if (carriedPrevious) {
|
|
29664
29766
|
nextCtx.previous_session = carriedPrevious;
|
|
@@ -29860,6 +29962,22 @@ class SessionService {
|
|
|
29860
29962
|
}
|
|
29861
29963
|
return count;
|
|
29862
29964
|
}
|
|
29965
|
+
async setArchived(alias, archived) {
|
|
29966
|
+
await this.mutate(async () => {
|
|
29967
|
+
const session3 = this.state.sessions[alias];
|
|
29968
|
+
if (!session3) {
|
|
29969
|
+
throw new Error(`session "${alias}" does not exist`);
|
|
29970
|
+
}
|
|
29971
|
+
if (archived) {
|
|
29972
|
+
session3.archived = true;
|
|
29973
|
+
session3.archived_at = new Date(this.now()).toISOString();
|
|
29974
|
+
} else {
|
|
29975
|
+
delete session3.archived;
|
|
29976
|
+
delete session3.archived_at;
|
|
29977
|
+
}
|
|
29978
|
+
await this.persist();
|
|
29979
|
+
});
|
|
29980
|
+
}
|
|
29863
29981
|
async removeSession(alias) {
|
|
29864
29982
|
return await this.mutate(async () => {
|
|
29865
29983
|
const session3 = this.state.sessions[alias];
|
|
@@ -29974,7 +30092,8 @@ class SessionService {
|
|
|
29974
30092
|
modeId: session3.mode_id,
|
|
29975
30093
|
replyMode: session3.reply_mode,
|
|
29976
30094
|
effectiveReplyMode,
|
|
29977
|
-
cwd: workspaceConfig.cwd
|
|
30095
|
+
cwd: workspaceConfig.cwd,
|
|
30096
|
+
archived: session3.archived === true
|
|
29978
30097
|
};
|
|
29979
30098
|
}
|
|
29980
30099
|
async setSessionModel(alias, modelId) {
|
|
@@ -30489,6 +30608,10 @@ function encodeBridgePromptUsageEvent(event) {
|
|
|
30489
30608
|
return `${JSON.stringify(event)}
|
|
30490
30609
|
`;
|
|
30491
30610
|
}
|
|
30611
|
+
function encodeBridgePromptCommandsEvent(event) {
|
|
30612
|
+
return `${JSON.stringify(event)}
|
|
30613
|
+
`;
|
|
30614
|
+
}
|
|
30492
30615
|
function encodeBridgeSessionProgressEvent(event) {
|
|
30493
30616
|
return `${JSON.stringify(event)}
|
|
30494
30617
|
`;
|
|
@@ -30795,7 +30918,14 @@ class AcpxBridgeClient {
|
|
|
30795
30918
|
pending.onEvent?.({
|
|
30796
30919
|
type: "prompt.usage",
|
|
30797
30920
|
used: message.used,
|
|
30798
|
-
size: message.size
|
|
30921
|
+
size: message.size,
|
|
30922
|
+
...message.cost ? { cost: message.cost } : {},
|
|
30923
|
+
...message.breakdown ? { breakdown: message.breakdown } : {}
|
|
30924
|
+
});
|
|
30925
|
+
} else if (message.event === "prompt.commands") {
|
|
30926
|
+
pending.onEvent?.({
|
|
30927
|
+
type: "prompt.commands",
|
|
30928
|
+
commands: message.commands
|
|
30799
30929
|
});
|
|
30800
30930
|
} else if (message.event === "session.progress") {
|
|
30801
30931
|
pending.onEvent?.({
|
|
@@ -31258,6 +31388,8 @@ class AcpxBridgeTransport {
|
|
|
31258
31388
|
let planChain = Promise.resolve();
|
|
31259
31389
|
let usageError;
|
|
31260
31390
|
let usageChain = Promise.resolve();
|
|
31391
|
+
let commandsError;
|
|
31392
|
+
let commandsChain = Promise.resolve();
|
|
31261
31393
|
let toolEventMode = resolveToolEventMode(options);
|
|
31262
31394
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
31263
31395
|
toolEventMode = "text";
|
|
@@ -31313,19 +31445,30 @@ class AcpxBridgeTransport {
|
|
|
31313
31445
|
if (event.type === "prompt.usage") {
|
|
31314
31446
|
const onUsage = options?.onUsage;
|
|
31315
31447
|
if (onUsage) {
|
|
31316
|
-
const usage = { used: event.used, size: event.size };
|
|
31448
|
+
const usage = { used: event.used, size: event.size, ...event.cost ? { cost: event.cost } : {}, ...event.breakdown ? { breakdown: event.breakdown } : {} };
|
|
31317
31449
|
usageChain = usageChain.then(() => onUsage(usage)).catch((error2) => {
|
|
31318
31450
|
usageError ??= error2;
|
|
31319
31451
|
});
|
|
31320
31452
|
}
|
|
31321
31453
|
return;
|
|
31322
31454
|
}
|
|
31455
|
+
if (event.type === "prompt.commands") {
|
|
31456
|
+
const onCommands = options?.onCommands;
|
|
31457
|
+
if (onCommands) {
|
|
31458
|
+
const commands = event.commands;
|
|
31459
|
+
commandsChain = commandsChain.then(() => onCommands(commands)).catch((error2) => {
|
|
31460
|
+
commandsError ??= error2;
|
|
31461
|
+
});
|
|
31462
|
+
}
|
|
31463
|
+
return;
|
|
31464
|
+
}
|
|
31323
31465
|
});
|
|
31324
31466
|
await segmentChain;
|
|
31325
31467
|
await toolEventChain;
|
|
31326
31468
|
await thoughtChain;
|
|
31327
31469
|
await planChain;
|
|
31328
31470
|
await usageChain;
|
|
31471
|
+
await commandsChain;
|
|
31329
31472
|
if (sink) {
|
|
31330
31473
|
const { overflowCount } = sink.finalize();
|
|
31331
31474
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -31349,6 +31492,9 @@ class AcpxBridgeTransport {
|
|
|
31349
31492
|
if (usageError) {
|
|
31350
31493
|
throw usageError;
|
|
31351
31494
|
}
|
|
31495
|
+
if (commandsError) {
|
|
31496
|
+
throw commandsError;
|
|
31497
|
+
}
|
|
31352
31498
|
return { text: summary ? `${summary}
|
|
31353
31499
|
|
|
31354
31500
|
${result.text}` : "" };
|
|
@@ -31368,6 +31514,9 @@ ${result.text}` : "" };
|
|
|
31368
31514
|
if (usageError) {
|
|
31369
31515
|
throw usageError;
|
|
31370
31516
|
}
|
|
31517
|
+
if (commandsError) {
|
|
31518
|
+
throw commandsError;
|
|
31519
|
+
}
|
|
31371
31520
|
return result;
|
|
31372
31521
|
}
|
|
31373
31522
|
async setMode(session3, modeId) {
|
|
@@ -31391,6 +31540,9 @@ ${result.text}` : "" };
|
|
|
31391
31540
|
async removeSession(session3) {
|
|
31392
31541
|
await this.client.request("removeSession", this.toParams(session3));
|
|
31393
31542
|
}
|
|
31543
|
+
async deleteSession(session3) {
|
|
31544
|
+
await this.client.request("deleteSession", this.toParams(session3));
|
|
31545
|
+
}
|
|
31394
31546
|
async getAgentSessionId(session3) {
|
|
31395
31547
|
const result = await this.client.request("getAgentSessionId", this.toParams(session3));
|
|
31396
31548
|
return result.agentSessionId;
|
|
@@ -31574,6 +31726,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31574
31726
|
let onThought;
|
|
31575
31727
|
let onPlan;
|
|
31576
31728
|
let onUsage;
|
|
31729
|
+
let onCommands;
|
|
31577
31730
|
let rawStream = false;
|
|
31578
31731
|
if (options === undefined) {
|
|
31579
31732
|
toolEventMode = "text";
|
|
@@ -31586,6 +31739,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31586
31739
|
onThought = options.onThought;
|
|
31587
31740
|
onPlan = options.onPlan;
|
|
31588
31741
|
onUsage = options.onUsage;
|
|
31742
|
+
onCommands = options.onCommands;
|
|
31589
31743
|
rawStream = options.rawStream ?? false;
|
|
31590
31744
|
toolEventMode = resolveToolEventMode({
|
|
31591
31745
|
toolEventMode: options.mode,
|
|
@@ -31605,6 +31759,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31605
31759
|
onThought,
|
|
31606
31760
|
onPlan,
|
|
31607
31761
|
onUsage,
|
|
31762
|
+
onCommands,
|
|
31608
31763
|
finalize() {
|
|
31609
31764
|
if (this.pendingLine.trim().length > 0) {
|
|
31610
31765
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -31672,8 +31827,17 @@ function parseStreamingChunks(state, line) {
|
|
|
31672
31827
|
if (update.sessionUpdate === "usage_update") {
|
|
31673
31828
|
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
31674
31829
|
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
31675
|
-
if (used !== undefined && size !== undefined && size > 0)
|
|
31676
|
-
|
|
31830
|
+
if (used !== undefined && size !== undefined && size > 0) {
|
|
31831
|
+
const cost = normalizeUsageCost(update.cost);
|
|
31832
|
+
const breakdown = normalizeUsageBreakdown(update._meta?.usage);
|
|
31833
|
+
state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
|
|
31834
|
+
}
|
|
31835
|
+
return;
|
|
31836
|
+
}
|
|
31837
|
+
if (update.sessionUpdate === "available_commands_update") {
|
|
31838
|
+
if (Array.isArray(update.availableCommands)) {
|
|
31839
|
+
state.onCommands?.(normalizeAgentCommands(update.availableCommands));
|
|
31840
|
+
}
|
|
31677
31841
|
return;
|
|
31678
31842
|
}
|
|
31679
31843
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
@@ -31840,6 +32004,52 @@ function readFirstStringArray(record3, keys) {
|
|
|
31840
32004
|
}
|
|
31841
32005
|
return;
|
|
31842
32006
|
}
|
|
32007
|
+
function asFiniteNumber(value) {
|
|
32008
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
32009
|
+
}
|
|
32010
|
+
function firstFiniteNumber(record3, keys) {
|
|
32011
|
+
for (const key of keys) {
|
|
32012
|
+
const n = asFiniteNumber(record3[key]);
|
|
32013
|
+
if (n !== undefined)
|
|
32014
|
+
return n;
|
|
32015
|
+
}
|
|
32016
|
+
return;
|
|
32017
|
+
}
|
|
32018
|
+
function normalizeUsageBreakdown(value) {
|
|
32019
|
+
if (!isRecord3(value))
|
|
32020
|
+
return;
|
|
32021
|
+
const out = {};
|
|
32022
|
+
for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
|
|
32023
|
+
const n = firstFiniteNumber(value, aliases);
|
|
32024
|
+
if (n !== undefined)
|
|
32025
|
+
out[key] = n;
|
|
32026
|
+
}
|
|
32027
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
32028
|
+
}
|
|
32029
|
+
function normalizeUsageCost(value) {
|
|
32030
|
+
if (!isRecord3(value))
|
|
32031
|
+
return;
|
|
32032
|
+
const amount = asFiniteNumber(value.amount);
|
|
32033
|
+
const currency = readString(value, "currency");
|
|
32034
|
+
if (amount === undefined && !currency)
|
|
32035
|
+
return;
|
|
32036
|
+
return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
|
|
32037
|
+
}
|
|
32038
|
+
function normalizeAgentCommands(value) {
|
|
32039
|
+
if (!Array.isArray(value))
|
|
32040
|
+
return [];
|
|
32041
|
+
const out = [];
|
|
32042
|
+
for (const entry of value) {
|
|
32043
|
+
if (!isRecord3(entry))
|
|
32044
|
+
continue;
|
|
32045
|
+
const name = readString(entry, "name");
|
|
32046
|
+
if (!name)
|
|
32047
|
+
continue;
|
|
32048
|
+
const description = readString(entry, "description");
|
|
32049
|
+
out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
|
|
32050
|
+
}
|
|
32051
|
+
return out;
|
|
32052
|
+
}
|
|
31843
32053
|
function isRecord3(value) {
|
|
31844
32054
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31845
32055
|
}
|
|
@@ -31865,8 +32075,17 @@ function isGenericToolTitle(kind, title) {
|
|
|
31865
32075
|
}
|
|
31866
32076
|
return false;
|
|
31867
32077
|
}
|
|
32078
|
+
var USAGE_BREAKDOWN_FIELDS;
|
|
31868
32079
|
var init_streaming_prompt = __esm(() => {
|
|
31869
32080
|
init_tool_kind_emoji();
|
|
32081
|
+
USAGE_BREAKDOWN_FIELDS = [
|
|
32082
|
+
["inputTokens", ["inputTokens", "input_tokens"]],
|
|
32083
|
+
["outputTokens", ["outputTokens", "output_tokens"]],
|
|
32084
|
+
["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
|
|
32085
|
+
["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
|
|
32086
|
+
["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
|
|
32087
|
+
["totalTokens", ["totalTokens", "total_tokens"]]
|
|
32088
|
+
];
|
|
31870
32089
|
});
|
|
31871
32090
|
|
|
31872
32091
|
// src/transport/acpx-cli/node-pty-helper.ts
|
|
@@ -31959,6 +32178,31 @@ var init_agent_session_list = __esm(() => {
|
|
|
31959
32178
|
init_path();
|
|
31960
32179
|
});
|
|
31961
32180
|
|
|
32181
|
+
// src/transport/acpx-session-files.ts
|
|
32182
|
+
import { readdir as readdir3, unlink as unlink2 } from "node:fs/promises";
|
|
32183
|
+
import { homedir as homedir9 } from "node:os";
|
|
32184
|
+
import { join as join19 } from "node:path";
|
|
32185
|
+
async function deleteAcpxSessionFiles(options) {
|
|
32186
|
+
const dir = options.sessionsDir ?? join19(homedir9(), ".acpx", "sessions");
|
|
32187
|
+
const safeId = encodeURIComponent(options.acpxRecordId);
|
|
32188
|
+
await unlink2(join19(dir, `${safeId}.json`)).catch(() => {
|
|
32189
|
+
return;
|
|
32190
|
+
});
|
|
32191
|
+
let entries;
|
|
32192
|
+
try {
|
|
32193
|
+
entries = await readdir3(dir);
|
|
32194
|
+
} catch {
|
|
32195
|
+
return;
|
|
32196
|
+
}
|
|
32197
|
+
const streamFiles = entries.filter((name) => name.startsWith(`${safeId}.stream.`));
|
|
32198
|
+
for (const name of streamFiles) {
|
|
32199
|
+
await unlink2(join19(dir, name)).catch(() => {
|
|
32200
|
+
return;
|
|
32201
|
+
});
|
|
32202
|
+
}
|
|
32203
|
+
}
|
|
32204
|
+
var init_acpx_session_files = () => {};
|
|
32205
|
+
|
|
31962
32206
|
// src/transport/acpx-cli/acpx-cli-transport.ts
|
|
31963
32207
|
import { createRequire as createRequire5 } from "node:module";
|
|
31964
32208
|
import { spawn as spawn9 } from "node:child_process";
|
|
@@ -32217,6 +32461,16 @@ ${baseText}` : "" };
|
|
|
32217
32461
|
const detail = normalizeCommandError(result) ?? `command failed with exit code ${result.code}`;
|
|
32218
32462
|
throw new Error(detail);
|
|
32219
32463
|
}
|
|
32464
|
+
async deleteSession(session3) {
|
|
32465
|
+
let acpxRecordId;
|
|
32466
|
+
try {
|
|
32467
|
+
({ acpxRecordId } = await this.readSessionRecord(session3));
|
|
32468
|
+
} catch {
|
|
32469
|
+
return;
|
|
32470
|
+
}
|
|
32471
|
+
await this.removeSession(session3);
|
|
32472
|
+
await deleteAcpxSessionFiles({ acpxRecordId });
|
|
32473
|
+
}
|
|
32220
32474
|
async hasSession(session3) {
|
|
32221
32475
|
const result = await this.runCommand(this.command, this.buildArgs(session3, [
|
|
32222
32476
|
"sessions",
|
|
@@ -32253,7 +32507,7 @@ ${baseText}` : "" };
|
|
|
32253
32507
|
const parsed = JSON.parse(result.stdout);
|
|
32254
32508
|
const acpxRecordId = typeof parsed.acpxRecordId === "string" ? parsed.acpxRecordId : typeof parsed.id === "string" ? parsed.id : undefined;
|
|
32255
32509
|
const agentSessionId = typeof parsed.agentSessionId === "string" ? parsed.agentSessionId : undefined;
|
|
32256
|
-
if (acpxRecordId) {
|
|
32510
|
+
if (acpxRecordId && /^[\w.:-]+$/.test(acpxRecordId) && acpxRecordId.length >= 8) {
|
|
32257
32511
|
return { acpxRecordId, agentSessionId };
|
|
32258
32512
|
}
|
|
32259
32513
|
} catch {
|
|
@@ -32532,6 +32786,7 @@ var init_acpx_cli_transport = __esm(() => {
|
|
|
32532
32786
|
init_terminate_process_tree();
|
|
32533
32787
|
init_acpx_queue_owner_launcher();
|
|
32534
32788
|
init_agent_session_list();
|
|
32789
|
+
init_acpx_session_files();
|
|
32535
32790
|
require4 = createRequire5(import.meta.url);
|
|
32536
32791
|
});
|
|
32537
32792
|
|
|
@@ -33003,8 +33258,8 @@ function createControlEventBus(logger2) {
|
|
|
33003
33258
|
|
|
33004
33259
|
// src/transport/native-session-history.ts
|
|
33005
33260
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
33006
|
-
import { homedir as
|
|
33007
|
-
import { join as
|
|
33261
|
+
import { homedir as homedir10 } from "node:os";
|
|
33262
|
+
import { join as join20 } from "node:path";
|
|
33008
33263
|
function classifyToolKind(name) {
|
|
33009
33264
|
const n = name.toLowerCase();
|
|
33010
33265
|
if (/(^|[^a-z])(read|cat|view|open)([^a-z]|$)/.test(n))
|
|
@@ -33116,8 +33371,8 @@ function mapAcpxMessagesToHistory(raw) {
|
|
|
33116
33371
|
}
|
|
33117
33372
|
async function readNativeSessionHistory(opts) {
|
|
33118
33373
|
try {
|
|
33119
|
-
const dir = opts.sessionsDir ??
|
|
33120
|
-
const indexRaw = await readFile14(
|
|
33374
|
+
const dir = opts.sessionsDir ?? join20(opts.homeDir ?? homedir10(), ".acpx", "sessions");
|
|
33375
|
+
const indexRaw = await readFile14(join20(dir, "index.json"), "utf8").catch(() => null);
|
|
33121
33376
|
if (!indexRaw)
|
|
33122
33377
|
return [];
|
|
33123
33378
|
const index = JSON.parse(indexRaw);
|
|
@@ -33126,7 +33381,7 @@ async function readNativeSessionHistory(opts) {
|
|
|
33126
33381
|
for (const entry of candidates) {
|
|
33127
33382
|
if (!entry.file)
|
|
33128
33383
|
continue;
|
|
33129
|
-
const recRaw = await readFile14(
|
|
33384
|
+
const recRaw = await readFile14(join20(dir, entry.file), "utf8").catch(() => null);
|
|
33130
33385
|
if (!recRaw)
|
|
33131
33386
|
continue;
|
|
33132
33387
|
const record3 = JSON.parse(recRaw);
|
|
@@ -33144,14 +33399,14 @@ var init_native_session_history = () => {};
|
|
|
33144
33399
|
// src/control/workspace-fs.ts
|
|
33145
33400
|
import { execFile } from "node:child_process";
|
|
33146
33401
|
import { promisify } from "node:util";
|
|
33147
|
-
import { homedir as
|
|
33148
|
-
import { readdir as
|
|
33402
|
+
import { homedir as homedir11 } from "node:os";
|
|
33403
|
+
import { readdir as readdir4, realpath, stat as stat3, open as open5 } from "node:fs/promises";
|
|
33149
33404
|
import { isAbsolute as isAbsolute3, relative, resolve as resolve3, sep } from "node:path";
|
|
33150
33405
|
function expandHome2(p) {
|
|
33151
33406
|
if (p === "~")
|
|
33152
|
-
return
|
|
33407
|
+
return homedir11();
|
|
33153
33408
|
if (p.startsWith("~/") || p.startsWith("~" + sep))
|
|
33154
|
-
return resolve3(
|
|
33409
|
+
return resolve3(homedir11(), p.slice(2));
|
|
33155
33410
|
return p;
|
|
33156
33411
|
}
|
|
33157
33412
|
|
|
@@ -33186,7 +33441,7 @@ class WorkspaceFs {
|
|
|
33186
33441
|
}
|
|
33187
33442
|
async listDirectory(workspace3, relPath) {
|
|
33188
33443
|
const { abs, rel } = await this.resolve(workspace3, relPath);
|
|
33189
|
-
const dirents = await
|
|
33444
|
+
const dirents = await readdir4(abs, { withFileTypes: true });
|
|
33190
33445
|
const entries = [];
|
|
33191
33446
|
for (const d of dirents.slice(0, MAX_ENTRIES)) {
|
|
33192
33447
|
if (d.isDirectory()) {
|
|
@@ -33240,7 +33495,7 @@ class WorkspaceFs {
|
|
|
33240
33495
|
const dir = queue.shift();
|
|
33241
33496
|
let dirents;
|
|
33242
33497
|
try {
|
|
33243
|
-
dirents = await
|
|
33498
|
+
dirents = await readdir4(dir, { withFileTypes: true });
|
|
33244
33499
|
} catch {
|
|
33245
33500
|
continue;
|
|
33246
33501
|
}
|
|
@@ -33340,24 +33595,29 @@ var init_workspace_fs = __esm(() => {
|
|
|
33340
33595
|
});
|
|
33341
33596
|
|
|
33342
33597
|
// src/control/control-service.ts
|
|
33598
|
+
import path15 from "node:path";
|
|
33599
|
+
|
|
33343
33600
|
class ControlService {
|
|
33344
33601
|
deps;
|
|
33345
33602
|
constructor(deps) {
|
|
33346
33603
|
this.deps = deps;
|
|
33347
33604
|
}
|
|
33348
33605
|
workspaceFs = new WorkspaceFs(() => this.deps.workspaces.list().map((w) => ({ name: w.name, cwd: w.cwd })));
|
|
33349
|
-
listDirectory(workspace3,
|
|
33350
|
-
return this.workspaceFs.listDirectory(workspace3,
|
|
33606
|
+
listDirectory(workspace3, path16) {
|
|
33607
|
+
return this.workspaceFs.listDirectory(workspace3, path16);
|
|
33351
33608
|
}
|
|
33352
|
-
readWorkspaceFile(workspace3,
|
|
33353
|
-
return this.workspaceFs.readFile(workspace3,
|
|
33609
|
+
readWorkspaceFile(workspace3, path16) {
|
|
33610
|
+
return this.workspaceFs.readFile(workspace3, path16);
|
|
33354
33611
|
}
|
|
33355
|
-
workspaceGitDiff(workspace3,
|
|
33356
|
-
return this.workspaceFs.gitDiff(workspace3,
|
|
33612
|
+
workspaceGitDiff(workspace3, path16) {
|
|
33613
|
+
return this.workspaceFs.gitDiff(workspace3, path16);
|
|
33357
33614
|
}
|
|
33358
33615
|
searchWorkspace(workspace3, query) {
|
|
33359
33616
|
return this.workspaceFs.search(workspace3, query);
|
|
33360
33617
|
}
|
|
33618
|
+
async uploadFile(input) {
|
|
33619
|
+
return this.deps.uploadStore.save(input.filename, input.content, input.mimeType);
|
|
33620
|
+
}
|
|
33361
33621
|
async getSessionModel(chatKey, alias) {
|
|
33362
33622
|
const session3 = await this.resolveControlSession(chatKey, alias);
|
|
33363
33623
|
if (!session3)
|
|
@@ -33389,7 +33649,8 @@ class ControlService {
|
|
|
33389
33649
|
agent: session3.agent,
|
|
33390
33650
|
workspace: session3.workspace,
|
|
33391
33651
|
transportSession: session3.transportSession,
|
|
33392
|
-
running: this.deps.activeTurns.isActiveAnywhere(session3.alias)
|
|
33652
|
+
running: this.deps.activeTurns.isActiveAnywhere(session3.alias),
|
|
33653
|
+
archived: session3.archived === true
|
|
33393
33654
|
}));
|
|
33394
33655
|
}
|
|
33395
33656
|
async listNativeSessions(_chatKey, agent3, workspace3) {
|
|
@@ -33419,15 +33680,26 @@ class ControlService {
|
|
|
33419
33680
|
agent: session3.agent,
|
|
33420
33681
|
workspace: session3.workspace,
|
|
33421
33682
|
transportSession: session3.transportSession,
|
|
33422
|
-
running: false
|
|
33683
|
+
running: false,
|
|
33684
|
+
archived: false
|
|
33423
33685
|
};
|
|
33424
33686
|
}
|
|
33425
33687
|
async removeSession(chatKey, alias) {
|
|
33426
33688
|
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33427
|
-
const result = await this.deps.
|
|
33689
|
+
const result = await this.deps.removeSessionWithTransport(internalAlias);
|
|
33428
33690
|
this.deps.events.emit({ type: "sessions-changed" });
|
|
33429
33691
|
return result;
|
|
33430
33692
|
}
|
|
33693
|
+
async archiveSession(chatKey, alias) {
|
|
33694
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33695
|
+
await this.deps.archiveSessionWithTransport(internalAlias);
|
|
33696
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33697
|
+
}
|
|
33698
|
+
async unarchiveSession(chatKey, alias) {
|
|
33699
|
+
const internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
|
|
33700
|
+
await this.deps.unarchiveSession(internalAlias);
|
|
33701
|
+
this.deps.events.emit({ type: "sessions-changed" });
|
|
33702
|
+
}
|
|
33431
33703
|
listAgents() {
|
|
33432
33704
|
return this.deps.agents.list();
|
|
33433
33705
|
}
|
|
@@ -33489,7 +33761,8 @@ class ControlService {
|
|
|
33489
33761
|
text: input.text,
|
|
33490
33762
|
senderId: input.senderId,
|
|
33491
33763
|
...input.isOwner !== undefined ? { isOwner: input.isOwner } : {},
|
|
33492
|
-
...input.accountId !== undefined ? { accountId: input.accountId } : {}
|
|
33764
|
+
...input.accountId !== undefined ? { accountId: input.accountId } : {},
|
|
33765
|
+
...input.media !== undefined ? { media: input.media } : {}
|
|
33493
33766
|
});
|
|
33494
33767
|
}
|
|
33495
33768
|
async runScheduledTurn(input) {
|
|
@@ -33561,6 +33834,32 @@ ${chunk}` : chunk
|
|
|
33561
33834
|
});
|
|
33562
33835
|
emittedChunk = true;
|
|
33563
33836
|
};
|
|
33837
|
+
const incomingMedia = params.media ?? [];
|
|
33838
|
+
const sandboxedMedia = incomingMedia.length ? (() => {
|
|
33839
|
+
const uploadRoot = path15.resolve(this.deps.uploadStore.root);
|
|
33840
|
+
const kept = incomingMedia.filter((ref) => {
|
|
33841
|
+
const resolved = path15.resolve(ref.filePath);
|
|
33842
|
+
return resolved === uploadRoot || resolved.startsWith(uploadRoot + path15.sep);
|
|
33843
|
+
});
|
|
33844
|
+
const dropped = incomingMedia.length - kept.length;
|
|
33845
|
+
if (dropped > 0) {
|
|
33846
|
+
console.warn(`[control] dropped ${dropped} media ref(s) with filePath outside the upload sandbox`);
|
|
33847
|
+
}
|
|
33848
|
+
return kept;
|
|
33849
|
+
})() : incomingMedia;
|
|
33850
|
+
const chatMedia = sandboxedMedia.map((ref) => ({
|
|
33851
|
+
kind: ref.kind,
|
|
33852
|
+
filePath: ref.filePath,
|
|
33853
|
+
mimeType: ref.mimeType,
|
|
33854
|
+
...ref.fileName ? { fileName: ref.fileName } : {},
|
|
33855
|
+
sizeBytes: ref.size,
|
|
33856
|
+
source: {
|
|
33857
|
+
channelId: "relay",
|
|
33858
|
+
accountId: params.accountId ?? "control",
|
|
33859
|
+
chatKey: params.chatKey,
|
|
33860
|
+
messageId: ref.id
|
|
33861
|
+
}
|
|
33862
|
+
}));
|
|
33564
33863
|
try {
|
|
33565
33864
|
const response = await this.deps.agent.chat({
|
|
33566
33865
|
accountId: params.accountId ?? "control",
|
|
@@ -33568,6 +33867,7 @@ ${chunk}` : chunk
|
|
|
33568
33867
|
text: params.text,
|
|
33569
33868
|
metadata: buildControlMetadata(params.senderId, params.isOwner),
|
|
33570
33869
|
abortSignal: controller.signal,
|
|
33870
|
+
...chatMedia.length > 0 ? { media: chatMedia } : {},
|
|
33571
33871
|
reply: async (chunk) => {
|
|
33572
33872
|
emitChunk(chunk);
|
|
33573
33873
|
},
|
|
@@ -33601,7 +33901,17 @@ ${chunk}` : chunk
|
|
|
33601
33901
|
chatKey: params.chatKey,
|
|
33602
33902
|
sessionAlias: params.sessionAlias,
|
|
33603
33903
|
used: usage.used,
|
|
33604
|
-
size: usage.size
|
|
33904
|
+
size: usage.size,
|
|
33905
|
+
...usage.cost ? { cost: usage.cost } : {},
|
|
33906
|
+
...usage.breakdown ? { breakdown: usage.breakdown } : {}
|
|
33907
|
+
});
|
|
33908
|
+
},
|
|
33909
|
+
onCommands: (commands) => {
|
|
33910
|
+
this.deps.events.emit({
|
|
33911
|
+
type: "agent-commands",
|
|
33912
|
+
chatKey: params.chatKey,
|
|
33913
|
+
sessionAlias: params.sessionAlias,
|
|
33914
|
+
commands
|
|
33605
33915
|
});
|
|
33606
33916
|
}
|
|
33607
33917
|
});
|
|
@@ -33690,18 +34000,97 @@ var init_control_service = __esm(() => {
|
|
|
33690
34000
|
init_workspace_fs();
|
|
33691
34001
|
});
|
|
33692
34002
|
|
|
34003
|
+
// src/control/upload-store.ts
|
|
34004
|
+
import { mkdtemp as mkdtemp2, readdir as readdir5, rm as rm10, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
|
|
34005
|
+
import { homedir as homedir12 } from "node:os";
|
|
34006
|
+
import path16 from "node:path";
|
|
34007
|
+
function defaultRootDir() {
|
|
34008
|
+
const home = process.env.HOME ?? homedir12();
|
|
34009
|
+
return path16.join(coreHomeDir(home), "runtime", "uploads");
|
|
34010
|
+
}
|
|
34011
|
+
function sanitizeUploadFilename(raw) {
|
|
34012
|
+
const base = path16.basename(raw).replace(/[/\\]/g, "").replace(/^\.+/, "");
|
|
34013
|
+
const cleaned = base.trim();
|
|
34014
|
+
return cleaned.length > 0 ? cleaned : "file";
|
|
34015
|
+
}
|
|
34016
|
+
|
|
34017
|
+
class UploadStore {
|
|
34018
|
+
rootDir;
|
|
34019
|
+
maxBytes;
|
|
34020
|
+
ttlMs;
|
|
34021
|
+
now;
|
|
34022
|
+
constructor(opts = {}) {
|
|
34023
|
+
this.rootDir = opts.rootDir ?? defaultRootDir();
|
|
34024
|
+
this.maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
34025
|
+
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
34026
|
+
this.now = opts.now ?? (() => new Date);
|
|
34027
|
+
}
|
|
34028
|
+
get root() {
|
|
34029
|
+
return this.rootDir;
|
|
34030
|
+
}
|
|
34031
|
+
async save(filename, base642, mimeType) {
|
|
34032
|
+
if (base642.length > Math.ceil(this.maxBytes * 4 / 3) + 4)
|
|
34033
|
+
throw new Error("file-too-large");
|
|
34034
|
+
const bytes = Buffer.from(base642, "base64");
|
|
34035
|
+
if (bytes.byteLength === 0)
|
|
34036
|
+
throw new Error("empty-file");
|
|
34037
|
+
if (bytes.byteLength > this.maxBytes)
|
|
34038
|
+
throw new Error("file-too-large");
|
|
34039
|
+
const safeName = sanitizeUploadFilename(filename);
|
|
34040
|
+
const { mkdir: mkdir9 } = await import("node:fs/promises");
|
|
34041
|
+
await mkdir9(this.rootDir, { recursive: true });
|
|
34042
|
+
const dir = await mkdtemp2(path16.join(this.rootDir, "u-"));
|
|
34043
|
+
const filePath = path16.join(dir, safeName);
|
|
34044
|
+
await writeFile8(filePath, bytes);
|
|
34045
|
+
return {
|
|
34046
|
+
id: path16.basename(dir),
|
|
34047
|
+
path: filePath,
|
|
34048
|
+
filename: safeName,
|
|
34049
|
+
mimeType,
|
|
34050
|
+
size: bytes.byteLength
|
|
34051
|
+
};
|
|
34052
|
+
}
|
|
34053
|
+
async cleanup() {
|
|
34054
|
+
let entries;
|
|
34055
|
+
try {
|
|
34056
|
+
entries = await readdir5(this.rootDir);
|
|
34057
|
+
} catch {
|
|
34058
|
+
return 0;
|
|
34059
|
+
}
|
|
34060
|
+
const cutoff = this.now().getTime() - this.ttlMs;
|
|
34061
|
+
let removed = 0;
|
|
34062
|
+
for (const name of entries) {
|
|
34063
|
+
const dir = path16.join(this.rootDir, name);
|
|
34064
|
+
try {
|
|
34065
|
+
const info = await stat4(dir);
|
|
34066
|
+
if (info.mtimeMs < cutoff) {
|
|
34067
|
+
await rm10(dir, { recursive: true, force: true });
|
|
34068
|
+
removed += 1;
|
|
34069
|
+
}
|
|
34070
|
+
} catch {}
|
|
34071
|
+
}
|
|
34072
|
+
return removed;
|
|
34073
|
+
}
|
|
34074
|
+
}
|
|
34075
|
+
var DEFAULT_MAX_BYTES, DEFAULT_TTL_MS;
|
|
34076
|
+
var init_upload_store = __esm(() => {
|
|
34077
|
+
init_core_home();
|
|
34078
|
+
DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
|
|
34079
|
+
DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
34080
|
+
});
|
|
34081
|
+
|
|
33693
34082
|
// src/config/agent-catalog.ts
|
|
33694
34083
|
import { existsSync as existsSync3 } from "node:fs";
|
|
33695
|
-
import { delimiter as delimiter2, join as
|
|
34084
|
+
import { delimiter as delimiter2, join as join21 } from "node:path";
|
|
33696
34085
|
function isBinaryOnPath(binary) {
|
|
33697
|
-
const
|
|
34086
|
+
const path17 = process.env.PATH ?? "";
|
|
33698
34087
|
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
33699
|
-
for (const dir of
|
|
34088
|
+
for (const dir of path17.split(delimiter2)) {
|
|
33700
34089
|
if (!dir)
|
|
33701
34090
|
continue;
|
|
33702
34091
|
for (const ext of exts) {
|
|
33703
34092
|
try {
|
|
33704
|
-
if (existsSync3(
|
|
34093
|
+
if (existsSync3(join21(dir, binary + ext)))
|
|
33705
34094
|
return true;
|
|
33706
34095
|
} catch {}
|
|
33707
34096
|
}
|
|
@@ -33741,8 +34130,8 @@ __export(exports_main, {
|
|
|
33741
34130
|
buildApp: () => buildApp
|
|
33742
34131
|
});
|
|
33743
34132
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
33744
|
-
import { homedir as
|
|
33745
|
-
import { dirname as dirname12, join as
|
|
34133
|
+
import { homedir as homedir13 } from "node:os";
|
|
34134
|
+
import { dirname as dirname12, join as join22 } from "node:path";
|
|
33746
34135
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
33747
34136
|
function startProgressHeartbeat(orchestration3, config4, logger2, channel) {
|
|
33748
34137
|
const thresholdSeconds = config4.orchestration.progressHeartbeatSeconds;
|
|
@@ -34233,6 +34622,9 @@ async function buildApp(paths, deps = {}) {
|
|
|
34233
34622
|
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);
|
|
34234
34623
|
const agent3 = new ConsoleAgent(router3, logger2);
|
|
34235
34624
|
const controlEvents = createControlEventBus(logger2);
|
|
34625
|
+
const uploadStore = new UploadStore;
|
|
34626
|
+
uploadStore.cleanup();
|
|
34627
|
+
const uploadCleanupInterval = setInterval(() => void uploadStore.cleanup().catch(() => {}), 60 * 60 * 1000);
|
|
34236
34628
|
const control = new ControlService({
|
|
34237
34629
|
agent: agent3,
|
|
34238
34630
|
sessions,
|
|
@@ -34240,6 +34632,9 @@ async function buildApp(paths, deps = {}) {
|
|
|
34240
34632
|
createSessionWithTransport: (internalAlias, agent4, workspace3, model) => router3.createSessionWithTransport(internalAlias, agent4, workspace3, model),
|
|
34241
34633
|
listNativeSessions: (agent4, workspace3) => router3.listNativeSessionsForControl(agent4, workspace3),
|
|
34242
34634
|
attachNativeSessionWithTransport: (internalAlias, agent4, workspace3, agentSessionId, nativeMeta) => router3.attachNativeSessionWithTransport(internalAlias, agent4, workspace3, agentSessionId, nativeMeta),
|
|
34635
|
+
removeSessionWithTransport: (internalAlias) => router3.removeSessionWithTransport(internalAlias),
|
|
34636
|
+
archiveSessionWithTransport: (internalAlias) => router3.archiveSessionWithTransport(internalAlias),
|
|
34637
|
+
unarchiveSession: (internalAlias) => router3.unarchiveSession(internalAlias),
|
|
34243
34638
|
activeTurns,
|
|
34244
34639
|
scheduled: scheduledService,
|
|
34245
34640
|
orchestration: orchestration3,
|
|
@@ -34272,7 +34667,8 @@ async function buildApp(paths, deps = {}) {
|
|
|
34272
34667
|
const updated = await configStore.removeWorkspace(name);
|
|
34273
34668
|
replaceRuntimeConfig(config4, updated);
|
|
34274
34669
|
}
|
|
34275
|
-
}
|
|
34670
|
+
},
|
|
34671
|
+
uploadStore
|
|
34276
34672
|
});
|
|
34277
34673
|
const scheduledScheduler = new ScheduledTaskScheduler(scheduledService, {
|
|
34278
34674
|
dispatchTask: buildScheduledDispatchTask({
|
|
@@ -34342,6 +34738,7 @@ async function buildApp(paths, deps = {}) {
|
|
|
34342
34738
|
reapStaleQueueOwners: () => reapWarmQueueOwners("startup"),
|
|
34343
34739
|
dispose: async () => {
|
|
34344
34740
|
scheduledScheduler.stop();
|
|
34741
|
+
clearInterval(uploadCleanupInterval);
|
|
34345
34742
|
if (progressHeartbeatInterval !== undefined) {
|
|
34346
34743
|
clearInterval(progressHeartbeatInterval);
|
|
34347
34744
|
}
|
|
@@ -34402,8 +34799,8 @@ async function main() {
|
|
|
34402
34799
|
}
|
|
34403
34800
|
}
|
|
34404
34801
|
async function prepareChannelMedia(configPath, config4) {
|
|
34405
|
-
const runtimeDir =
|
|
34406
|
-
const mediaRootDir =
|
|
34802
|
+
const runtimeDir = join22(dirname12(configPath), "runtime");
|
|
34803
|
+
const mediaRootDir = join22(runtimeDir, "media");
|
|
34407
34804
|
const mediaStore = new RuntimeMediaStore({ rootDir: mediaRootDir });
|
|
34408
34805
|
await mediaStore.cleanupExpired().catch((error2) => {
|
|
34409
34806
|
console.error("[xacpx] media cleanup failed:", error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -34412,16 +34809,16 @@ async function prepareChannelMedia(configPath, config4) {
|
|
|
34412
34809
|
return { mediaStore, channelDeps: { mediaStore, allowedMediaRoots } };
|
|
34413
34810
|
}
|
|
34414
34811
|
function resolveRuntimePaths() {
|
|
34415
|
-
const home = process.env.HOME ??
|
|
34812
|
+
const home = process.env.HOME ?? homedir13();
|
|
34416
34813
|
if (!home) {
|
|
34417
34814
|
throw new Error("Unable to resolve the current user home directory");
|
|
34418
34815
|
}
|
|
34419
|
-
const configPath = coreEnv("CONFIG") ??
|
|
34420
|
-
const runtimeDir =
|
|
34816
|
+
const configPath = coreEnv("CONFIG") ?? join22(coreHomeDir(home), "config.json");
|
|
34817
|
+
const runtimeDir = join22(dirname12(configPath), "runtime");
|
|
34421
34818
|
return {
|
|
34422
34819
|
configPath,
|
|
34423
|
-
statePath: coreEnv("STATE") ??
|
|
34424
|
-
perfLogPath:
|
|
34820
|
+
statePath: coreEnv("STATE") ?? join22(coreHomeDir(home), "state.json"),
|
|
34821
|
+
perfLogPath: join22(runtimeDir, "perf.log"),
|
|
34425
34822
|
orchestrationSocketPath: coreEnv("ORCHESTRATION_SOCKET") ?? resolveDaemonOrchestrationSocketPath(runtimeDir)
|
|
34426
34823
|
};
|
|
34427
34824
|
}
|
|
@@ -34433,13 +34830,13 @@ function resolveBridgeEntryPath() {
|
|
|
34433
34830
|
}
|
|
34434
34831
|
function resolveAppLogPath(configPath) {
|
|
34435
34832
|
const rootDir = dirname12(configPath);
|
|
34436
|
-
const runtimeDir =
|
|
34437
|
-
return
|
|
34833
|
+
const runtimeDir = join22(rootDir, "runtime");
|
|
34834
|
+
return join22(runtimeDir, "app.log");
|
|
34438
34835
|
}
|
|
34439
34836
|
function resolvePerfLogPath(configPath) {
|
|
34440
34837
|
const rootDir = dirname12(configPath);
|
|
34441
|
-
const runtimeDir =
|
|
34442
|
-
return
|
|
34838
|
+
const runtimeDir = join22(rootDir, "runtime");
|
|
34839
|
+
return join22(runtimeDir, "perf.log");
|
|
34443
34840
|
}
|
|
34444
34841
|
function resolveOrchestrationSocketPathFromConfigPath(configPath) {
|
|
34445
34842
|
const runtimeDir = resolveRuntimeDirFromConfigPath(configPath);
|
|
@@ -34484,6 +34881,7 @@ var init_main = __esm(async () => {
|
|
|
34484
34881
|
init_render_text();
|
|
34485
34882
|
init_quota_manager();
|
|
34486
34883
|
init_control_service();
|
|
34884
|
+
init_upload_store();
|
|
34487
34885
|
init_agent_catalog();
|
|
34488
34886
|
init_perf_tracer();
|
|
34489
34887
|
init_bootstrap();
|
|
@@ -34677,12 +35075,12 @@ var init_config_check = __esm(async () => {
|
|
|
34677
35075
|
});
|
|
34678
35076
|
|
|
34679
35077
|
// src/doctor/checks/daemon-check.ts
|
|
34680
|
-
import { readdir as
|
|
35078
|
+
import { readdir as readdir6, readFile as readFile15, rm as rm11 } from "node:fs/promises";
|
|
34681
35079
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
34682
|
-
import { homedir as
|
|
34683
|
-
import { join as
|
|
35080
|
+
import { homedir as homedir14 } from "node:os";
|
|
35081
|
+
import { join as join23 } from "node:path";
|
|
34684
35082
|
async function checkDaemon(options = {}) {
|
|
34685
|
-
const home = options.home ?? process.env.HOME ??
|
|
35083
|
+
const home = options.home ?? process.env.HOME ?? homedir14();
|
|
34686
35084
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
34687
35085
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
34688
35086
|
home,
|
|
@@ -34782,7 +35180,7 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
34782
35180
|
if (!fileName.endsWith(CONSUMER_LOCK_SUFFIX)) {
|
|
34783
35181
|
continue;
|
|
34784
35182
|
}
|
|
34785
|
-
const lockPath =
|
|
35183
|
+
const lockPath = join23(runtimeDir, fileName);
|
|
34786
35184
|
const lock2 = await deps.readConsumerLock(lockPath);
|
|
34787
35185
|
if (lock2 && !deps.isProcessRunning(lock2.pid)) {
|
|
34788
35186
|
stalePaths.push(lockPath);
|
|
@@ -34816,22 +35214,22 @@ async function detectStaleConsumerLockFix(runtimeDir, deps) {
|
|
|
34816
35214
|
}
|
|
34817
35215
|
async function defaultListConsumerLocks(runtimeDir) {
|
|
34818
35216
|
try {
|
|
34819
|
-
return await
|
|
35217
|
+
return await readdir6(runtimeDir);
|
|
34820
35218
|
} catch {
|
|
34821
35219
|
return [];
|
|
34822
35220
|
}
|
|
34823
35221
|
}
|
|
34824
|
-
async function defaultReadConsumerLock(
|
|
35222
|
+
async function defaultReadConsumerLock(path17) {
|
|
34825
35223
|
try {
|
|
34826
|
-
const raw = await readFile15(
|
|
35224
|
+
const raw = await readFile15(path17, "utf8");
|
|
34827
35225
|
const parsed = JSON.parse(raw);
|
|
34828
35226
|
return typeof parsed.pid === "number" ? { pid: parsed.pid } : null;
|
|
34829
35227
|
} catch {
|
|
34830
35228
|
return null;
|
|
34831
35229
|
}
|
|
34832
35230
|
}
|
|
34833
|
-
async function defaultRemoveConsumerLock(
|
|
34834
|
-
await
|
|
35231
|
+
async function defaultRemoveConsumerLock(path17) {
|
|
35232
|
+
await rm11(path17, { force: true });
|
|
34835
35233
|
}
|
|
34836
35234
|
function resolveCliEntryPath() {
|
|
34837
35235
|
return process.argv[1] ?? fileURLToPath6(import.meta.url);
|
|
@@ -34846,11 +35244,11 @@ var init_daemon_check = __esm(() => {
|
|
|
34846
35244
|
});
|
|
34847
35245
|
|
|
34848
35246
|
// src/doctor/checks/logs-check.ts
|
|
34849
|
-
import { stat as
|
|
34850
|
-
import { basename as basename3, join as
|
|
34851
|
-
import { homedir as
|
|
35247
|
+
import { stat as stat5, readdir as readdir7 } from "node:fs/promises";
|
|
35248
|
+
import { basename as basename3, join as join24 } from "node:path";
|
|
35249
|
+
import { homedir as homedir15 } from "node:os";
|
|
34852
35250
|
async function checkLogs(options = {}) {
|
|
34853
|
-
const home = options.home ?? process.env.HOME ??
|
|
35251
|
+
const home = options.home ?? process.env.HOME ?? homedir15();
|
|
34854
35252
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
34855
35253
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
34856
35254
|
home,
|
|
@@ -34882,13 +35280,13 @@ async function checkLogs(options = {}) {
|
|
|
34882
35280
|
const matched = entries.filter((entry) => isTrackedLogName(entry, tracked));
|
|
34883
35281
|
const files = [];
|
|
34884
35282
|
for (const name of matched) {
|
|
34885
|
-
const
|
|
35283
|
+
const path17 = join24(paths.runtimeDir, name);
|
|
34886
35284
|
try {
|
|
34887
|
-
const fileStat = await probe.stat(
|
|
35285
|
+
const fileStat = await probe.stat(path17);
|
|
34888
35286
|
if (fileStat.isDirectory()) {
|
|
34889
35287
|
continue;
|
|
34890
35288
|
}
|
|
34891
|
-
files.push({ name, path:
|
|
35289
|
+
files.push({ name, path: path17, size: fileStat.size });
|
|
34892
35290
|
} catch {
|
|
34893
35291
|
continue;
|
|
34894
35292
|
}
|
|
@@ -34962,8 +35360,8 @@ function formatBytes(bytes) {
|
|
|
34962
35360
|
}
|
|
34963
35361
|
function createLogsFsProbe() {
|
|
34964
35362
|
return {
|
|
34965
|
-
stat: async (
|
|
34966
|
-
readdir: async (
|
|
35363
|
+
stat: async (path17) => await stat5(path17),
|
|
35364
|
+
readdir: async (path17) => await readdir7(path17)
|
|
34967
35365
|
};
|
|
34968
35366
|
}
|
|
34969
35367
|
function formatError6(error2) {
|
|
@@ -35032,9 +35430,9 @@ var init_orchestration_health = __esm(() => {
|
|
|
35032
35430
|
});
|
|
35033
35431
|
|
|
35034
35432
|
// src/doctor/checks/orchestration-socket-check.ts
|
|
35035
|
-
import { homedir as
|
|
35433
|
+
import { homedir as homedir16 } from "node:os";
|
|
35036
35434
|
async function checkOrchestrationSocket(options = {}) {
|
|
35037
|
-
const home = options.home ?? process.env.HOME ??
|
|
35435
|
+
const home = options.home ?? process.env.HOME ?? homedir16();
|
|
35038
35436
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35039
35437
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35040
35438
|
home,
|
|
@@ -35207,11 +35605,11 @@ var init_plugin_check = __esm(async () => {
|
|
|
35207
35605
|
|
|
35208
35606
|
// src/doctor/checks/runtime-check.ts
|
|
35209
35607
|
import { constants } from "node:fs";
|
|
35210
|
-
import { access as access4, stat as
|
|
35608
|
+
import { access as access4, stat as stat6 } from "node:fs/promises";
|
|
35211
35609
|
import { dirname as dirname13 } from "node:path";
|
|
35212
|
-
import { homedir as
|
|
35610
|
+
import { homedir as homedir17 } from "node:os";
|
|
35213
35611
|
async function checkRuntime(options = {}) {
|
|
35214
|
-
const home = options.home ?? process.env.HOME ??
|
|
35612
|
+
const home = options.home ?? process.env.HOME ?? homedir17();
|
|
35215
35613
|
const runtimeDir = options.configPath ? resolveRuntimeDirFromConfigPath(options.configPath) : undefined;
|
|
35216
35614
|
const paths = (options.resolveDaemonPaths ?? resolveDaemonPaths)({
|
|
35217
35615
|
home,
|
|
@@ -35305,107 +35703,107 @@ function formatMode(mode) {
|
|
|
35305
35703
|
}
|
|
35306
35704
|
function createRuntimeFsProbe() {
|
|
35307
35705
|
return {
|
|
35308
|
-
stat: async (
|
|
35309
|
-
access: async (
|
|
35706
|
+
stat: async (path17) => await stat6(path17),
|
|
35707
|
+
access: async (path17, mode) => await access4(path17, mode)
|
|
35310
35708
|
};
|
|
35311
35709
|
}
|
|
35312
|
-
async function checkDirectoryCreatable(label,
|
|
35710
|
+
async function checkDirectoryCreatable(label, path17, probe, platform) {
|
|
35313
35711
|
try {
|
|
35314
|
-
const stats = await probe.stat(
|
|
35712
|
+
const stats = await probe.stat(path17);
|
|
35315
35713
|
if (!stats.isDirectory()) {
|
|
35316
35714
|
return {
|
|
35317
35715
|
ok: false,
|
|
35318
|
-
detail: `${label}: ${
|
|
35716
|
+
detail: `${label}: ${path17} (exists but is not a directory)`
|
|
35319
35717
|
};
|
|
35320
35718
|
}
|
|
35321
|
-
await probe.access(
|
|
35719
|
+
await probe.access(path17, directoryAccessMode(platform));
|
|
35322
35720
|
return {
|
|
35323
35721
|
ok: true,
|
|
35324
|
-
detail: `${label}: ${
|
|
35722
|
+
detail: `${label}: ${path17} (writable)`
|
|
35325
35723
|
};
|
|
35326
35724
|
} catch (error2) {
|
|
35327
35725
|
if (!isMissingPathError2(error2)) {
|
|
35328
35726
|
return {
|
|
35329
35727
|
ok: false,
|
|
35330
|
-
detail: `${label}: ${
|
|
35728
|
+
detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
|
|
35331
35729
|
};
|
|
35332
35730
|
}
|
|
35333
|
-
const parentCheck = await checkCreatableAncestorDirectory(
|
|
35731
|
+
const parentCheck = await checkCreatableAncestorDirectory(path17, probe, platform);
|
|
35334
35732
|
if (!parentCheck.ok) {
|
|
35335
35733
|
return {
|
|
35336
35734
|
ok: false,
|
|
35337
|
-
detail: `${label}: ${
|
|
35735
|
+
detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
|
|
35338
35736
|
};
|
|
35339
35737
|
}
|
|
35340
35738
|
return {
|
|
35341
35739
|
ok: true,
|
|
35342
|
-
detail: `${label}: ${
|
|
35740
|
+
detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
|
|
35343
35741
|
};
|
|
35344
35742
|
}
|
|
35345
35743
|
}
|
|
35346
|
-
async function checkFileCreatable(label,
|
|
35744
|
+
async function checkFileCreatable(label, path17, probe, platform) {
|
|
35347
35745
|
try {
|
|
35348
|
-
const stats = await probe.stat(
|
|
35746
|
+
const stats = await probe.stat(path17);
|
|
35349
35747
|
if (stats.isDirectory()) {
|
|
35350
35748
|
return {
|
|
35351
35749
|
ok: false,
|
|
35352
|
-
detail: `${label}: ${
|
|
35750
|
+
detail: `${label}: ${path17} (exists but is a directory)`
|
|
35353
35751
|
};
|
|
35354
35752
|
}
|
|
35355
|
-
await probe.access(
|
|
35753
|
+
await probe.access(path17, constants.W_OK);
|
|
35356
35754
|
return {
|
|
35357
35755
|
ok: true,
|
|
35358
|
-
detail: `${label}: ${
|
|
35756
|
+
detail: `${label}: ${path17} (writable)`
|
|
35359
35757
|
};
|
|
35360
35758
|
} catch (error2) {
|
|
35361
35759
|
if (!isMissingPathError2(error2)) {
|
|
35362
35760
|
return {
|
|
35363
35761
|
ok: false,
|
|
35364
|
-
detail: `${label}: ${
|
|
35762
|
+
detail: `${label}: ${path17} (unusable: ${formatError9(error2)})`
|
|
35365
35763
|
};
|
|
35366
35764
|
}
|
|
35367
|
-
const parentCheck = await checkCreatableAncestorDirectory(dirname13(
|
|
35765
|
+
const parentCheck = await checkCreatableAncestorDirectory(dirname13(path17), probe, platform);
|
|
35368
35766
|
if (!parentCheck.ok) {
|
|
35369
35767
|
return {
|
|
35370
35768
|
ok: false,
|
|
35371
|
-
detail: `${label}: ${
|
|
35769
|
+
detail: `${label}: ${path17} (parent not writable: ${parentCheck.blockingPath})`
|
|
35372
35770
|
};
|
|
35373
35771
|
}
|
|
35374
35772
|
return {
|
|
35375
35773
|
ok: true,
|
|
35376
|
-
detail: `${label}: ${
|
|
35774
|
+
detail: `${label}: ${path17} (creatable via ${parentCheck.creatableFrom})`
|
|
35377
35775
|
};
|
|
35378
35776
|
}
|
|
35379
35777
|
}
|
|
35380
|
-
async function checkCreatableAncestorDirectory(
|
|
35778
|
+
async function checkCreatableAncestorDirectory(path17, probe, platform) {
|
|
35381
35779
|
try {
|
|
35382
|
-
const stats = await probe.stat(
|
|
35780
|
+
const stats = await probe.stat(path17);
|
|
35383
35781
|
if (!stats.isDirectory()) {
|
|
35384
35782
|
return {
|
|
35385
35783
|
ok: false,
|
|
35386
|
-
creatableFrom:
|
|
35387
|
-
blockingPath:
|
|
35784
|
+
creatableFrom: path17,
|
|
35785
|
+
blockingPath: path17
|
|
35388
35786
|
};
|
|
35389
35787
|
}
|
|
35390
|
-
await probe.access(
|
|
35788
|
+
await probe.access(path17, directoryAccessMode(platform));
|
|
35391
35789
|
return {
|
|
35392
35790
|
ok: true,
|
|
35393
|
-
creatableFrom:
|
|
35791
|
+
creatableFrom: path17
|
|
35394
35792
|
};
|
|
35395
35793
|
} catch (error2) {
|
|
35396
35794
|
if (!isMissingPathError2(error2)) {
|
|
35397
35795
|
return {
|
|
35398
35796
|
ok: false,
|
|
35399
|
-
creatableFrom:
|
|
35400
|
-
blockingPath:
|
|
35797
|
+
creatableFrom: path17,
|
|
35798
|
+
blockingPath: path17
|
|
35401
35799
|
};
|
|
35402
35800
|
}
|
|
35403
|
-
const parent = dirname13(
|
|
35404
|
-
if (parent ===
|
|
35801
|
+
const parent = dirname13(path17);
|
|
35802
|
+
if (parent === path17) {
|
|
35405
35803
|
return {
|
|
35406
35804
|
ok: false,
|
|
35407
|
-
creatableFrom:
|
|
35408
|
-
blockingPath:
|
|
35805
|
+
creatableFrom: path17,
|
|
35806
|
+
blockingPath: path17
|
|
35409
35807
|
};
|
|
35410
35808
|
}
|
|
35411
35809
|
const parentCheck = await checkCreatableAncestorDirectory(parent, probe, platform);
|
|
@@ -35844,10 +36242,10 @@ var init_render_doctor = __esm(() => {
|
|
|
35844
36242
|
});
|
|
35845
36243
|
|
|
35846
36244
|
// src/doctor/doctor.ts
|
|
35847
|
-
import { homedir as
|
|
35848
|
-
import { join as
|
|
36245
|
+
import { homedir as homedir18 } from "node:os";
|
|
36246
|
+
import { join as join25 } from "node:path";
|
|
35849
36247
|
async function runDoctor(options = {}, deps = {}) {
|
|
35850
|
-
const home = deps.home ?? process.env.HOME ??
|
|
36248
|
+
const home = deps.home ?? process.env.HOME ?? homedir18();
|
|
35851
36249
|
const runtimePaths = resolveDoctorRuntimePaths(home, deps.resolveRuntimePaths);
|
|
35852
36250
|
const sharedLoadConfig = createSharedLoadConfig(runtimePaths, deps.loadConfig ?? loadConfig);
|
|
35853
36251
|
const runners = [
|
|
@@ -36000,8 +36398,8 @@ function resolveDoctorRuntimePaths(home, resolver) {
|
|
|
36000
36398
|
return resolveRuntimePaths();
|
|
36001
36399
|
}
|
|
36002
36400
|
return {
|
|
36003
|
-
configPath:
|
|
36004
|
-
statePath:
|
|
36401
|
+
configPath: join25(coreHomeDir(home), "config.json"),
|
|
36402
|
+
statePath: join25(coreHomeDir(home), "state.json")
|
|
36005
36403
|
};
|
|
36006
36404
|
}
|
|
36007
36405
|
function depsUseExplicitRuntimeOverrides() {
|
|
@@ -36184,8 +36582,8 @@ var init_doctor2 = __esm(async () => {
|
|
|
36184
36582
|
// src/cli.ts
|
|
36185
36583
|
init_core_home();
|
|
36186
36584
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
36187
|
-
import { homedir as
|
|
36188
|
-
import { dirname as dirname14, join as
|
|
36585
|
+
import { homedir as homedir19 } from "node:os";
|
|
36586
|
+
import { dirname as dirname14, join as join26, sep as sep2 } from "node:path";
|
|
36189
36587
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
36190
36588
|
|
|
36191
36589
|
// src/runtime/migrate-core-home.ts
|
|
@@ -52439,7 +52837,7 @@ async function createCliScheduledTaskService() {
|
|
|
52439
52837
|
return new ScheduledTaskService(state, stateStore);
|
|
52440
52838
|
}
|
|
52441
52839
|
function resolveConfigPathForCurrentEnv() {
|
|
52442
|
-
return coreEnv("CONFIG") ??
|
|
52840
|
+
return coreEnv("CONFIG") ?? join26(coreHomeDir(requireHome2()), "config.json");
|
|
52443
52841
|
}
|
|
52444
52842
|
function resolveDaemonPathsForCurrentConfig() {
|
|
52445
52843
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
@@ -52790,7 +53188,7 @@ function decodeFirstRunOnboarding(raw) {
|
|
|
52790
53188
|
return null;
|
|
52791
53189
|
}
|
|
52792
53190
|
function requireHome2() {
|
|
52793
|
-
const home = process.env.HOME ??
|
|
53191
|
+
const home = process.env.HOME ?? homedir19();
|
|
52794
53192
|
if (!home) {
|
|
52795
53193
|
throw new Error("Unable to resolve the current user home directory");
|
|
52796
53194
|
}
|
|
@@ -52814,7 +53212,7 @@ function safeDaemonLogPaths() {
|
|
|
52814
53212
|
const configPath = resolveConfigPathForCurrentEnv();
|
|
52815
53213
|
const paths = resolveDaemonPathsForCurrentConfig();
|
|
52816
53214
|
return {
|
|
52817
|
-
appLog:
|
|
53215
|
+
appLog: join26(dirname14(configPath), "runtime", "app.log"),
|
|
52818
53216
|
stderrLog: paths.stderrLog
|
|
52819
53217
|
};
|
|
52820
53218
|
} catch {
|