@ganglion/xacpx 0.20.3 → 0.22.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/dist/cli.js CHANGED
@@ -927,6 +927,7 @@ var init_channel_cli = __esm(() => {
927
927
  channelAdded: (type) => `Channel ${type} added`,
928
928
  cannotRemoveLastEnabled: "Cannot remove the last enabled channel.",
929
929
  channelRemoved: (id) => `Channel ${id} removed`,
930
+ channelRetirementFailed: (id, error) => `Channel ${id} retirement cleanup failed: ${error}`,
930
931
  channelCredentialsCleared: (id) => `Removed stored credentials for channel ${id}`,
931
932
  channelCredentialsClearFailed: (id, error) => `Channel ${id} removed, but clearing its stored credentials failed: ${error}`,
932
933
  channelCredentialsKept: (id) => `Kept stored credentials for channel ${id} (--keep-credentials)`,
@@ -2053,6 +2054,7 @@ var init_channel_cli2 = __esm(() => {
2053
2054
  channelAdded: (type) => `频道 ${type} 已添加`,
2054
2055
  cannotRemoveLastEnabled: "不能删除最后一个启用的频道。",
2055
2056
  channelRemoved: (id) => `频道 ${id} 已删除`,
2057
+ channelRetirementFailed: (id, error) => `频道 ${id} 退役清理失败:${error}`,
2056
2058
  channelCredentialsCleared: (id) => `已移除频道 ${id} 的存储凭证`,
2057
2059
  channelCredentialsClearFailed: (id, error) => `频道 ${id} 已删除,但清除其存储凭证失败:${error}`,
2058
2060
  channelCredentialsKept: (id) => `已保留频道 ${id} 的存储凭证(--keep-credentials)`,
@@ -4690,7 +4692,9 @@ var LOCAL_AGENT_BINS;
4690
4692
  var init_local_agent_bin = __esm(() => {
4691
4693
  LOCAL_AGENT_BINS = {
4692
4694
  opencode: { bin: "opencode", args: ["acp"] },
4693
- kilocode: { bin: "kilocode", args: ["acp"] }
4695
+ kilocode: { bin: "kilocode", args: ["acp"] },
4696
+ reasonix: { bin: "reasonix", args: ["acp"] },
4697
+ omp: { bin: "omp", args: ["acp"] }
4694
4698
  };
4695
4699
  });
4696
4700
 
@@ -6721,6 +6725,9 @@ var init_agent_templates = __esm(() => {
6721
6725
  mux: {
6722
6726
  driver: "mux"
6723
6727
  },
6728
+ omp: {
6729
+ driver: "omp"
6730
+ },
6724
6731
  opencode: {
6725
6732
  driver: "opencode"
6726
6733
  },
@@ -6733,6 +6740,9 @@ var init_agent_templates = __esm(() => {
6733
6740
  qwen: {
6734
6741
  driver: "qwen"
6735
6742
  },
6743
+ reasonix: {
6744
+ driver: "reasonix"
6745
+ },
6736
6746
  trae: {
6737
6747
  driver: "trae"
6738
6748
  },
@@ -8203,7 +8213,7 @@ function createCoreRuntimeLock(options) {
8203
8213
  try {
8204
8214
  if (isBunRuntime() && !options.loadFsExt) {
8205
8215
  await handle.close();
8206
- posixHelper = await acquireFlockHelper(options.lockFilePath, options.helperAcquireTimeoutMs, options.helperHandshakeDelayMs);
8216
+ posixHelper = await acquireFlockHelper(options.lockFilePath, options.helperAcquireTimeoutMs, options.helperHandshakeDelayMs, onDiagnostic);
8207
8217
  await emitDiagnostic(onDiagnostic, "lock_helper_started", {
8208
8218
  lockFilePath: options.lockFilePath,
8209
8219
  helperPid: posixHelper.pid
@@ -8301,11 +8311,17 @@ async function releasePosixLock(options, handle, helper) {
8301
8311
  function isBunRuntime() {
8302
8312
  return Boolean(globalThis.Bun);
8303
8313
  }
8304
- async function acquireFlockHelper(lockFilePath, timeoutMs = 1e4, handshakeDelayMs = 0) {
8314
+ async function acquireFlockHelper(lockFilePath, timeoutMs = 1e4, handshakeDelayMs = 0, onDiagnostic) {
8305
8315
  const fsExtPath = require2.resolve("fs-ext");
8306
8316
  const child = spawn7("node", ["-e", FLOCK_HELPER_SOURCE, fsExtPath, lockFilePath, String(handshakeDelayMs)], {
8307
8317
  stdio: ["pipe", "pipe", "pipe"]
8308
8318
  });
8319
+ if (child.pid) {
8320
+ await emitDiagnostic(onDiagnostic, "lock_helper_spawned", {
8321
+ lockFilePath,
8322
+ helperPid: child.pid
8323
+ });
8324
+ }
8309
8325
  const line = await new Promise((resolve5, reject) => {
8310
8326
  let stdout = "";
8311
8327
  let stderr = "";
@@ -29367,6 +29383,7 @@ function createEmptyState() {
29367
29383
  var init_types4 = () => {};
29368
29384
 
29369
29385
  // src/state/state-store.ts
29386
+ import { randomUUID as randomUUID6 } from "node:crypto";
29370
29387
  import { readFile as readFile10, rename as rename3, writeFile as writeFile4 } from "node:fs/promises";
29371
29388
  function isRecord3(value) {
29372
29389
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -29619,6 +29636,9 @@ function parseOrchestrationState(raw, dropped) {
29619
29636
  function isReplyMode2(value) {
29620
29637
  return value === "stream" || value === "final" || value === "verbose";
29621
29638
  }
29639
+ function isLogicalSessionId(value) {
29640
+ return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
29641
+ }
29622
29642
  function isSessionSource(value) {
29623
29643
  return value === undefined || value === "weacpx" || value === "xacpx" || value === "agent-side";
29624
29644
  }
@@ -29626,16 +29646,26 @@ function isSessionRecord(value) {
29626
29646
  if (!isRecord3(value)) {
29627
29647
  return false;
29628
29648
  }
29629
- return isString(value.alias) && isString(value.agent) && isString(value.workspace) && isString(value.transport_session) && isSessionSource(value.source) && isOptionalString(value.agent_session_id) && isOptionalString(value.agent_session_title) && isOptionalString(value.agent_session_updated_at) && isOptionalString(value.attached_at) && isOptionalString(value.transport_agent_command) && isOptionalString(value.transport_acpx_agent) && (value.transport_agent_argv === undefined || isStringArray(value.transport_agent_argv)) && isOptionalString(value.mode_id) && isOptionalString(value.effort) && (value.reply_mode === undefined || isReplyMode2(value.reply_mode)) && isString(value.created_at) && isString(value.last_used_at);
29649
+ return isString(value.alias) && isString(value.agent) && isString(value.workspace) && isString(value.transport_session) && (value.logical_session_id === undefined || isLogicalSessionId(value.logical_session_id)) && isSessionSource(value.source) && isOptionalString(value.agent_session_id) && isOptionalString(value.agent_session_title) && isOptionalString(value.agent_session_updated_at) && isOptionalString(value.attached_at) && isOptionalString(value.transport_agent_command) && isOptionalString(value.transport_acpx_agent) && (value.transport_agent_argv === undefined || isStringArray(value.transport_agent_argv)) && isOptionalString(value.mode_id) && isOptionalString(value.effort) && (value.reply_mode === undefined || isReplyMode2(value.reply_mode)) && isString(value.created_at) && isString(value.last_used_at);
29630
29650
  }
29631
- function parseSessions(raw, dropped) {
29651
+ function parseSessions(raw, dropped, migrated) {
29632
29652
  const sessions = {};
29633
29653
  for (const [alias, value] of Object.entries(raw)) {
29634
29654
  if (!isSessionRecord(value)) {
29635
29655
  dropped.push({ section: "sessions", key: alias, reason: "malformed session record" });
29636
29656
  continue;
29637
29657
  }
29638
- sessions[alias] = value;
29658
+ if (value.logical_session_id === undefined) {
29659
+ migrated.push({
29660
+ section: "sessions",
29661
+ key: alias,
29662
+ reason: "legacy record missing logical_session_id; assigned a new UUIDv4"
29663
+ });
29664
+ }
29665
+ sessions[alias] = {
29666
+ ...value,
29667
+ logical_session_id: value.logical_session_id ?? randomUUID6()
29668
+ };
29639
29669
  }
29640
29670
  return sessions;
29641
29671
  }
@@ -29700,11 +29730,11 @@ function parseScheduledTasks(raw, dropped) {
29700
29730
  }
29701
29731
  return tasks;
29702
29732
  }
29703
- function parseState(raw, path3, dropped = []) {
29733
+ function parseState(raw, path3, dropped = [], migrated = []) {
29704
29734
  if (!isRecord3(raw)) {
29705
29735
  throw new Error(`state file "${path3}" must contain a JSON object`);
29706
29736
  }
29707
- const parsedSessions = parseSessions(sectionRecord(raw.sessions, "sessions", dropped), dropped);
29737
+ const parsedSessions = parseSessions(sectionRecord(raw.sessions, "sessions", dropped), dropped, migrated);
29708
29738
  const orchestration3 = parseOrchestrationState(raw.orchestration, dropped);
29709
29739
  repairExternalCoordinatorIdentityCollisions(parsedSessions, orchestration3, dropped);
29710
29740
  return {
@@ -29765,20 +29795,31 @@ class StateStore {
29765
29795
  if (read.kind === "corrupt") {
29766
29796
  return await this.recoverFromCorruptFile(read.reason);
29767
29797
  }
29768
- if (read.dropped.length === 0) {
29798
+ if (read.dropped.length === 0 && read.migrated.length === 0) {
29769
29799
  return read.state;
29770
29800
  }
29771
29801
  const report = { dropped: read.dropped };
29772
- const quarantinePath = `${this.path}.quarantine-${this.fileTimestamp()}`;
29773
- try {
29774
- const written = await (this.options.writeBackup ?? defaultWriteBackup)(quarantinePath, read.content);
29775
- report.quarantinePath = typeof written === "string" ? written : quarantinePath;
29776
- } catch (error2) {
29777
- report.backupError = error2 instanceof Error ? error2.message : String(error2);
29802
+ if (read.migrated.length > 0) {
29803
+ report.migrated = read.migrated;
29804
+ }
29805
+ if (read.dropped.length > 0) {
29806
+ const quarantinePath = `${this.path}.quarantine-${this.fileTimestamp()}`;
29807
+ try {
29808
+ const written = await (this.options.writeBackup ?? defaultWriteBackup)(quarantinePath, read.content);
29809
+ report.quarantinePath = typeof written === "string" ? written : quarantinePath;
29810
+ } catch (error2) {
29811
+ report.backupError = error2 instanceof Error ? error2.message : String(error2);
29812
+ }
29813
+ }
29814
+ if (read.migrated.length > 0) {
29815
+ await this.persistMigration(read.state);
29778
29816
  }
29779
29817
  this.loadReport = report;
29780
29818
  return read.state;
29781
29819
  }
29820
+ async persistMigration(state) {
29821
+ await (this.options.writeMigration ?? ((next) => this.save(next)))(state);
29822
+ }
29782
29823
  async inspect() {
29783
29824
  const read = await this.readAndParse();
29784
29825
  if (read.kind === "absent") {
@@ -29792,7 +29833,10 @@ class StateStore {
29792
29833
  }
29793
29834
  return {
29794
29835
  state: read.state,
29795
- report: read.dropped.length > 0 ? { dropped: read.dropped } : null
29836
+ report: read.dropped.length > 0 || read.migrated.length > 0 ? {
29837
+ dropped: read.dropped,
29838
+ ...read.migrated.length > 0 ? { migrated: read.migrated } : {}
29839
+ } : null
29796
29840
  };
29797
29841
  }
29798
29842
  async readAndParse() {
@@ -29821,7 +29865,8 @@ class StateStore {
29821
29865
  return { kind: "corrupt", reason: "top-level value is not an object" };
29822
29866
  }
29823
29867
  const dropped = [];
29824
- return { kind: "parsed", state: parseState(parsed, this.path, dropped), dropped, content };
29868
+ const migrated = [];
29869
+ return { kind: "parsed", state: parseState(parsed, this.path, dropped, migrated), dropped, migrated, content };
29825
29870
  }
29826
29871
  async save(state) {
29827
29872
  await writePrivateFileAtomic(this.path, JSON.stringify({ version: STATE_FILE_VERSION, ...state }, null, 2));
@@ -30404,6 +30449,27 @@ var init_plugin_home = __esm(() => {
30404
30449
  import { spawn as spawn8 } from "node:child_process";
30405
30450
  import { rm as rm6 } from "node:fs/promises";
30406
30451
  import { join as join15 } from "node:path";
30452
+ function pluginRegistryInstallArgs(registry2 = effectivePluginRegistry()) {
30453
+ return [
30454
+ `--registry=${registry2}`,
30455
+ `--@ganglion:registry=${registry2}`
30456
+ ];
30457
+ }
30458
+ function effectivePluginRegistry(env = process.env) {
30459
+ const raw = coreEnv("PLUGIN_REGISTRY", env)?.trim();
30460
+ if (!raw)
30461
+ return DEFAULT_PLUGIN_REGISTRY;
30462
+ let url;
30463
+ try {
30464
+ url = new URL(raw);
30465
+ } catch {
30466
+ throw new Error("XACPX_PLUGIN_REGISTRY must be a valid http(s) URL");
30467
+ }
30468
+ if (url.protocol !== "https:" && url.protocol !== "http:" || !url.hostname) {
30469
+ throw new Error("XACPX_PLUGIN_REGISTRY must be a valid http(s) URL");
30470
+ }
30471
+ return url.toString().replace(/\/+$/, "");
30472
+ }
30407
30473
  function shellSpawnPlan(args) {
30408
30474
  const shell = process.platform === "win32";
30409
30475
  return { shell, args: shell ? args.map((arg) => `"${arg}"`) : args };
@@ -30454,11 +30520,12 @@ async function installPluginPackage(input) {
30454
30520
  await rm6(join15(input.pluginHome, "bun.lock"), { force: true }).catch(() => {});
30455
30521
  }
30456
30522
  const spec = input.version ? `${input.packageName}@${input.version}` : input.packageName;
30523
+ const registryArgs = pluginRegistryInstallArgs();
30457
30524
  if (packageManager === "bun") {
30458
- await runCommand("bun", ["add", spec], { cwd: input.pluginHome });
30525
+ await runCommand("bun", ["add", spec, ...registryArgs], { cwd: input.pluginHome });
30459
30526
  return;
30460
30527
  }
30461
- await runCommand("npm", ["install", spec], { cwd: input.pluginHome });
30528
+ await runCommand("npm", ["install", spec, ...registryArgs], { cwd: input.pluginHome });
30462
30529
  }
30463
30530
  async function updatePluginPackage(input) {
30464
30531
  await installPluginPackage(input);
@@ -30472,6 +30539,7 @@ async function removePluginPackage(input) {
30472
30539
  }
30473
30540
  await runCommand("npm", ["uninstall", input.packageName], { cwd: input.pluginHome });
30474
30541
  }
30542
+ var DEFAULT_PLUGIN_REGISTRY = "https://registry.npmjs.org";
30475
30543
  var init_package_manager = __esm(() => {
30476
30544
  init_plugin_home();
30477
30545
  });
@@ -32272,7 +32340,7 @@ var require_main = __commonJS((exports, module) => {
32272
32340
  });
32273
32341
 
32274
32342
  // src/weixin/auth/login-qr.ts
32275
- import { randomUUID as randomUUID6 } from "node:crypto";
32343
+ import { randomUUID as randomUUID7 } from "node:crypto";
32276
32344
  function isLoginFresh(login3) {
32277
32345
  return Date.now() - login3.startedAt < ACTIVE_LOGIN_TTL_MS;
32278
32346
  }
@@ -32375,7 +32443,7 @@ async function readVerifyCodeFromStdin(prompt) {
32375
32443
  });
32376
32444
  }
32377
32445
  async function startWeixinLoginWithQr(opts) {
32378
- const sessionKey = opts.accountId || randomUUID6();
32446
+ const sessionKey = opts.accountId || randomUUID7();
32379
32447
  purgeExpiredLogins();
32380
32448
  const existing = activeLogins.get(sessionKey);
32381
32449
  if (!opts.force && existing && isLoginFresh(existing) && existing.qrcodeUrl) {
@@ -32398,7 +32466,7 @@ async function startWeixinLoginWithQr(opts) {
32398
32466
  });
32399
32467
  const login3 = {
32400
32468
  sessionKey,
32401
- id: randomUUID6(),
32469
+ id: randomUUID7(),
32402
32470
  qrcode: qrResponse.qrcode,
32403
32471
  qrcodeUrl: qrResponse.qrcode_img_content,
32404
32472
  startedAt: Date.now()
@@ -36388,7 +36456,7 @@ var init_scheduled_turn = __esm(() => {
36388
36456
  });
36389
36457
 
36390
36458
  // src/weixin/monitor/consumer-lock.ts
36391
- import { randomUUID as randomUUID7 } from "node:crypto";
36459
+ import { randomUUID as randomUUID8 } from "node:crypto";
36392
36460
  import { mkdir as mkdir10, open as open6, readFile as readFile12, rm as rm9, writeFile as writeFile7 } from "node:fs/promises";
36393
36461
  import { dirname as dirname15, join as join17 } from "node:path";
36394
36462
  import { homedir as homedir6 } from "node:os";
@@ -36412,7 +36480,7 @@ function createWeixinConsumerLock(options = {}) {
36412
36480
  const existing = await loadLockMetadata(lockFilePath) ?? meta2;
36413
36481
  throw new ActiveWeixinConsumerLockError(lockFilePath, existing);
36414
36482
  }
36415
- windowsLockId = meta2.lockId ?? randomUUID7();
36483
+ windowsLockId = meta2.lockId ?? randomUUID8();
36416
36484
  const metadata = {
36417
36485
  ...meta2,
36418
36486
  schemaVersion: 2,
@@ -36432,7 +36500,7 @@ function createWeixinConsumerLock(options = {}) {
36432
36500
  return;
36433
36501
  }
36434
36502
  const requestedIdentity = await (options.probeProcessIdentity ?? probePosixProcessIdentity)(meta2.pid);
36435
- posixLockId = meta2.lockId ?? randomUUID7();
36503
+ posixLockId = meta2.lockId ?? randomUUID8();
36436
36504
  const requested = requestedIdentity.status === "found" ? { ...meta2, schemaVersion: 2, lockId: posixLockId, processStartedAtMs: requestedIdentity.identity.startedAtMs } : { ...meta2, schemaVersion: 2, lockId: posixLockId };
36437
36505
  while (true) {
36438
36506
  try {
@@ -36916,6 +36984,12 @@ var init_registry = __esm(() => {
36916
36984
  });
36917
36985
 
36918
36986
  // src/channels/plugin.ts
36987
+ var exports_plugin = {};
36988
+ __export(exports_plugin, {
36989
+ registerChannelPlugin: () => registerChannelPlugin,
36990
+ invokeChannelRetireHook: () => invokeChannelRetireHook,
36991
+ getChannelRetireHook: () => getChannelRetireHook
36992
+ });
36919
36993
  function registerChannelPlugin(plugin) {
36920
36994
  bootstrapBuiltinChannelFactories();
36921
36995
  bootstrapBuiltinChannelCliProviders();
@@ -36930,10 +37004,23 @@ function registerChannelPlugin(plugin) {
36930
37004
  registerChannelFactory(plugin.type, plugin.factory);
36931
37005
  if (plugin.cliProvider)
36932
37006
  registerChannelCliProvider(plugin.cliProvider);
37007
+ if (plugin.retireChannel)
37008
+ retireHooks.set(channelType, plugin.retireChannel);
36933
37009
  }
37010
+ function getChannelRetireHook(type) {
37011
+ return retireHooks.get(type);
37012
+ }
37013
+ async function invokeChannelRetireHook(ctx) {
37014
+ const hook = retireHooks.get(ctx.channel.type);
37015
+ if (!hook)
37016
+ return;
37017
+ await hook(ctx);
37018
+ }
37019
+ var retireHooks;
36934
37020
  var init_plugin = __esm(() => {
36935
37021
  init_create_channel();
36936
37022
  init_registry();
37023
+ retireHooks = new Map;
36937
37024
  });
36938
37025
 
36939
37026
  // src/plugins/compatibility.ts
@@ -38373,6 +38460,13 @@ async function removeChannel(type, rawArgs, deps) {
38373
38460
  deps.print(t().channelCli.cannotRemoveLastEnabled);
38374
38461
  return 1;
38375
38462
  }
38463
+ if (deps.retireChannel) {
38464
+ try {
38465
+ await deps.retireChannel(channel, "removed");
38466
+ } catch (error2) {
38467
+ deps.print(t().channelCli.channelRetirementFailed(channel.id, error2 instanceof Error ? error2.message : String(error2)));
38468
+ }
38469
+ }
38376
38470
  config4.channels = config4.channels.filter((entry) => entry.id !== channel.id);
38377
38471
  await deps.saveChannels(config4.channels);
38378
38472
  deps.print(t().channelCli.channelRemoved(channel.id));
@@ -38405,6 +38499,13 @@ async function setChannelEnabled(type, enabled, rawArgs, deps) {
38405
38499
  deps.print(t().channelCli.cannotDisableLastEnabled);
38406
38500
  return 1;
38407
38501
  }
38502
+ if (!enabled && channel.enabled && deps.retireChannel) {
38503
+ try {
38504
+ await deps.retireChannel(channel, "disabled");
38505
+ } catch (error2) {
38506
+ deps.print(t().channelCli.channelRetirementFailed(channel.id, error2 instanceof Error ? error2.message : String(error2)));
38507
+ }
38508
+ }
38408
38509
  channel.enabled = enabled;
38409
38510
  await deps.saveChannels(config4.channels);
38410
38511
  deps.print(t().channelCli.channelEnabledToggled(channel.id, enabled));
@@ -38770,6 +38871,16 @@ async function inspectPlugins(input) {
38770
38871
  message: configPlugin.enabled ? `plugin is installed and valid; channels: ${channelTypes.length > 0 ? channelTypes.join(", ") : "none"}` : `plugin is installed and valid but disabled; run xacpx plugin enable ${configPlugin.name}`,
38771
38872
  ...configPlugin.enabled ? {} : { suggestion: `xacpx plugin enable ${configPlugin.name}` }
38772
38873
  });
38874
+ if (configPlugin.enabled) {
38875
+ for (const finding of await collectChannelDiagnoseFindings(plugin.channels ?? [], input.config)) {
38876
+ pushIfRelevant({
38877
+ level: finding.level === "skip" ? "ok" : finding.level,
38878
+ plugin: configPlugin.name,
38879
+ message: finding.level === "skip" ? `${finding.code}: ${finding.message}` : `${finding.code}: ${finding.message}`,
38880
+ ...finding.suggestion ? { suggestion: finding.suggestion } : {}
38881
+ });
38882
+ }
38883
+ }
38773
38884
  } catch (error2) {
38774
38885
  const message = error2 instanceof Error ? error2.message : String(error2);
38775
38886
  pushIfRelevant({ level: "error", plugin: configPlugin.name, message });
@@ -38804,6 +38915,28 @@ async function inspectPlugins(input) {
38804
38915
  }
38805
38916
  return issues;
38806
38917
  }
38918
+ async function collectChannelDiagnoseFindings(channelDefs, config4) {
38919
+ const findings = [];
38920
+ for (const def of channelDefs) {
38921
+ const diagnose = def.cliProvider?.diagnose;
38922
+ if (!diagnose)
38923
+ continue;
38924
+ const runtimeChannels = (config4.channels ?? []).filter((channel) => channel.type === def.type && channel.enabled !== false);
38925
+ for (const channel of runtimeChannels) {
38926
+ try {
38927
+ const result = await diagnose(channel);
38928
+ findings.push(...result);
38929
+ } catch (error2) {
38930
+ findings.push({
38931
+ level: "error",
38932
+ code: "channel-diagnose-failed",
38933
+ message: `channel ${channel.type} diagnose hook failed: ${error2 instanceof Error ? error2.message : String(error2)}`
38934
+ });
38935
+ }
38936
+ }
38937
+ }
38938
+ return findings;
38939
+ }
38807
38940
  var init_plugin_doctor = __esm(() => {
38808
38941
  init_channel_scope();
38809
38942
  init_plugin_loader();
@@ -49610,7 +49743,7 @@ function replaceRuntimeState(target, source) {
49610
49743
  }
49611
49744
 
49612
49745
  // src/sessions/session-service.ts
49613
- import { randomBytes as randomBytes2 } from "node:crypto";
49746
+ import { randomBytes as randomBytes2, randomUUID as randomUUID9 } from "node:crypto";
49614
49747
  import { dirname as dirname19 } from "node:path";
49615
49748
 
49616
49749
  class SessionService {
@@ -49622,6 +49755,7 @@ class SessionService {
49622
49755
  platform;
49623
49756
  runtimeRoot;
49624
49757
  pendingSessionAliasOperations = new Set;
49758
+ lifecyclePublisher;
49625
49759
  constructor(config4, stateStore, state, options = {}) {
49626
49760
  this.config = config4;
49627
49761
  this.stateStore = stateStore;
@@ -49667,6 +49801,7 @@ class SessionService {
49667
49801
  agent: agent3,
49668
49802
  workspace: workspace3,
49669
49803
  transport_session: transportSession,
49804
+ logical_session_id: existing?.logical_session_id ?? randomUUID9(),
49670
49805
  transport_agent_command: sameAgentExisting?.transport_agent_command,
49671
49806
  transport_acpx_agent: sameAgentExisting?.transport_acpx_agent,
49672
49807
  transport_agent_argv: sameAgentExisting?.transport_agent_argv,
@@ -49740,30 +49875,42 @@ class SessionService {
49740
49875
  if (!session3) {
49741
49876
  throw new Error(`session "${alias}" does not exist`);
49742
49877
  }
49743
- const prevCtx = this.state.chat_contexts[chatKey];
49744
- const previousCurrent = prevCtx?.current_session;
49745
- const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
49746
- session3.last_used_at = new Date().toISOString();
49747
49878
  if (session3.archived) {
49748
- delete session3.archived;
49749
- delete session3.archived_at;
49879
+ const nextState = structuredClone(this.state);
49880
+ const result2 = this.applyUseSession(nextState, chatKey, internalAlias);
49881
+ await this.commitLifecycleTransition(nextState);
49882
+ this.publishLifecycleEvent("restored", nextState.sessions[internalAlias]);
49883
+ return result2;
49750
49884
  }
49751
- const nextCtx = { ...prevCtx, current_session: internalAlias };
49752
- if (carriedPrevious) {
49753
- nextCtx.previous_session = carriedPrevious;
49754
- } else {
49755
- delete nextCtx.previous_session;
49756
- }
49757
- this.state.chat_contexts[chatKey] = nextCtx;
49885
+ const result = this.applyUseSession(this.state, chatKey, internalAlias);
49758
49886
  await this.persist();
49759
- return {
49760
- alias: toDisplaySessionAlias(session3.alias),
49761
- agent: session3.agent,
49762
- workspace: session3.workspace,
49763
- previousAlias: carriedPrevious ? toDisplaySessionAlias(carriedPrevious) : undefined
49764
- };
49887
+ return result;
49765
49888
  });
49766
49889
  }
49890
+ applyUseSession(state, chatKey, internalAlias) {
49891
+ const session3 = state.sessions[internalAlias];
49892
+ const prevCtx = state.chat_contexts[chatKey];
49893
+ const previousCurrent = prevCtx?.current_session;
49894
+ const carriedPrevious = previousCurrent && previousCurrent !== internalAlias ? previousCurrent : prevCtx?.previous_session;
49895
+ session3.last_used_at = new Date().toISOString();
49896
+ if (session3.archived) {
49897
+ delete session3.archived;
49898
+ delete session3.archived_at;
49899
+ }
49900
+ const nextCtx = { ...prevCtx, current_session: internalAlias };
49901
+ if (carriedPrevious) {
49902
+ nextCtx.previous_session = carriedPrevious;
49903
+ } else {
49904
+ delete nextCtx.previous_session;
49905
+ }
49906
+ state.chat_contexts[chatKey] = nextCtx;
49907
+ return {
49908
+ alias: toDisplaySessionAlias(session3.alias),
49909
+ agent: session3.agent,
49910
+ workspace: session3.workspace,
49911
+ previousAlias: carriedPrevious ? toDisplaySessionAlias(carriedPrevious) : undefined
49912
+ };
49913
+ }
49767
49914
  async usePreviousSession(chatKey) {
49768
49915
  return await this.mutate(async () => {
49769
49916
  const ctx = this.state.chat_contexts[chatKey];
@@ -49927,6 +50074,12 @@ class SessionService {
49927
50074
  listInternalAliases() {
49928
50075
  return Object.keys(this.state.sessions);
49929
50076
  }
50077
+ getLogicalSessionRecord(alias) {
50078
+ return this.state.sessions[alias] ?? null;
50079
+ }
50080
+ listLogicalSessionRecords() {
50081
+ return Object.values(this.state.sessions);
50082
+ }
49930
50083
  async setCurrentSessionMode(chatKey, modeId) {
49931
50084
  await this.mutate(async () => {
49932
50085
  const currentAlias = this.state.chat_contexts[chatKey]?.current_session;
@@ -50005,20 +50158,37 @@ class SessionService {
50005
50158
  }
50006
50159
  return count;
50007
50160
  }
50161
+ setSessionResourceLifecyclePublisher(publish) {
50162
+ this.lifecyclePublisher = publish;
50163
+ }
50164
+ publishLifecycleEvent(type, record3) {
50165
+ this.lifecyclePublisher?.({ type, record: record3 });
50166
+ }
50167
+ async commitLifecycleTransition(nextState) {
50168
+ await this.stateStore.saveNow(nextState);
50169
+ replaceRuntimeState(this.state, nextState);
50170
+ }
50008
50171
  async setArchived(alias, archived) {
50009
50172
  await this.mutate(async () => {
50010
50173
  const session3 = this.state.sessions[alias];
50011
50174
  if (!session3) {
50012
50175
  throw new Error(`session "${alias}" does not exist`);
50013
50176
  }
50177
+ const wasArchived = session3.archived === true;
50178
+ if (wasArchived === archived) {
50179
+ return;
50180
+ }
50181
+ const nextState = structuredClone(this.state);
50182
+ const nextSession = nextState.sessions[alias];
50014
50183
  if (archived) {
50015
- session3.archived = true;
50016
- session3.archived_at = new Date(this.now()).toISOString();
50184
+ nextSession.archived = true;
50185
+ nextSession.archived_at = new Date(this.now()).toISOString();
50017
50186
  } else {
50018
- delete session3.archived;
50019
- delete session3.archived_at;
50187
+ delete nextSession.archived;
50188
+ delete nextSession.archived_at;
50020
50189
  }
50021
- await this.persist();
50190
+ await this.commitLifecycleTransition(nextState);
50191
+ this.publishLifecycleEvent(archived ? "archived" : "restored", nextSession);
50022
50192
  });
50023
50193
  }
50024
50194
  async removeSession(alias) {
@@ -50027,9 +50197,11 @@ class SessionService {
50027
50197
  if (!session3) {
50028
50198
  throw new Error(`session "${alias}" does not exist`);
50029
50199
  }
50030
- const wasActive = Object.values(this.state.chat_contexts).some((ctx) => ctx.current_session === alias);
50031
- delete this.state.sessions[alias];
50032
- for (const [chatKey, ctx] of Object.entries(this.state.chat_contexts)) {
50200
+ const snapshot = structuredClone(session3);
50201
+ const nextState = structuredClone(this.state);
50202
+ const wasActive = Object.values(nextState.chat_contexts).some((ctx) => ctx.current_session === alias);
50203
+ delete nextState.sessions[alias];
50204
+ for (const [chatKey, ctx] of Object.entries(nextState.chat_contexts)) {
50033
50205
  if (ctx.previous_session === alias) {
50034
50206
  delete ctx.previous_session;
50035
50207
  }
@@ -50048,10 +50220,11 @@ class SessionService {
50048
50220
  }
50049
50221
  }
50050
50222
  if (!ctx.current_session && !ctx.previous_session && !ctx.background_results) {
50051
- delete this.state.chat_contexts[chatKey];
50223
+ delete nextState.chat_contexts[chatKey];
50052
50224
  }
50053
50225
  }
50054
- await this.persist();
50226
+ await this.commitLifecycleTransition(nextState);
50227
+ this.publishLifecycleEvent("removed", snapshot);
50055
50228
  return { wasActive };
50056
50229
  });
50057
50230
  }
@@ -50309,6 +50482,7 @@ class SessionService {
50309
50482
  agent: agent3,
50310
50483
  workspace: workspace3,
50311
50484
  transport_session: transportSession,
50485
+ logical_session_id: randomUUID9(),
50312
50486
  source: native?.source,
50313
50487
  agent_session_id: native?.agentSessionId,
50314
50488
  agent_session_title: native?.title ?? undefined,
@@ -50358,6 +50532,89 @@ var init_session_service = __esm(() => {
50358
50532
  init_channel_scope();
50359
50533
  });
50360
50534
 
50535
+ // src/sessions/session-resource-catalog.ts
50536
+ class CoreSessionResourceCatalog {
50537
+ deps;
50538
+ listeners = new Set;
50539
+ constructor(deps) {
50540
+ this.deps = deps;
50541
+ }
50542
+ async resolve(chatKey, alias) {
50543
+ let internalAlias;
50544
+ try {
50545
+ internalAlias = await this.deps.sessions.resolveAliasForChat(chatKey, alias);
50546
+ } catch {
50547
+ return null;
50548
+ }
50549
+ const record3 = this.deps.sessions.getLogicalSessionRecord(internalAlias);
50550
+ if (!record3) {
50551
+ return null;
50552
+ }
50553
+ const channelId = getChannelIdFromChatKey(chatKey);
50554
+ if (!isSessionAliasVisibleInChannel(record3.alias, channelId)) {
50555
+ return null;
50556
+ }
50557
+ return this.toDescriptor(record3);
50558
+ }
50559
+ async list(channelId) {
50560
+ const descriptors = [];
50561
+ for (const record3 of this.deps.sessions.listLogicalSessionRecords()) {
50562
+ if (!isSessionAliasVisibleInChannel(record3.alias, channelId)) {
50563
+ continue;
50564
+ }
50565
+ const descriptor = this.toDescriptor(record3);
50566
+ if (descriptor) {
50567
+ descriptors.push(descriptor);
50568
+ }
50569
+ }
50570
+ return descriptors;
50571
+ }
50572
+ subscribe(listener) {
50573
+ this.listeners.add(listener);
50574
+ return () => {
50575
+ this.listeners.delete(listener);
50576
+ };
50577
+ }
50578
+ publishLifecycleEvent(input) {
50579
+ const descriptor = this.toDescriptor(input.record);
50580
+ if (!descriptor) {
50581
+ return;
50582
+ }
50583
+ this.emit({ type: input.type, session: descriptor });
50584
+ }
50585
+ emit(event) {
50586
+ for (const listener of [...this.listeners]) {
50587
+ try {
50588
+ listener(event);
50589
+ } catch (error2) {
50590
+ this.deps.logger.error("sessions.resource_catalog.listener_failed", "session resource listener threw; delivery continues", {
50591
+ message: error2 instanceof Error ? error2.message : String(error2),
50592
+ eventType: event.type,
50593
+ internalAlias: event.session.internalAlias
50594
+ }).catch(() => {});
50595
+ }
50596
+ }
50597
+ }
50598
+ toDescriptor(record3) {
50599
+ const workspace3 = this.deps.config.workspaces[record3.workspace];
50600
+ if (!workspace3) {
50601
+ return null;
50602
+ }
50603
+ return {
50604
+ logicalSessionId: record3.logical_session_id,
50605
+ channelId: getChannelIdFromChatKey(record3.alias),
50606
+ internalAlias: record3.alias,
50607
+ displayAlias: toDisplaySessionAlias(record3.alias),
50608
+ workspace: record3.workspace,
50609
+ cwd: workspace3.cwd,
50610
+ archived: record3.archived === true
50611
+ };
50612
+ }
50613
+ }
50614
+ var init_session_resource_catalog = __esm(() => {
50615
+ init_channel_scope();
50616
+ });
50617
+
50361
50618
  // src/sessions/active-turn-registry.ts
50362
50619
  function createActiveTurnRegistry() {
50363
50620
  const byChat = new Map;
@@ -50619,7 +50876,7 @@ async function deleteAcpxSessionFiles(options) {
50619
50876
  var init_acpx_session_files = () => {};
50620
50877
 
50621
50878
  // src/transport/acpx-queue-owner-launcher.ts
50622
- import { createHash as createHash9, randomUUID as randomUUID8 } from "node:crypto";
50879
+ import { createHash as createHash9, randomUUID as randomUUID10 } from "node:crypto";
50623
50880
  import { spawn as spawn13 } from "node:child_process";
50624
50881
  import { access as access6, readFile as readFile18, unlink as unlink2 } from "node:fs/promises";
50625
50882
  import { join as join27 } from "node:path";
@@ -50694,7 +50951,7 @@ class AcpxQueueOwnerLauncher {
50694
50951
  this.probeSpawnedProcess = options.probeSpawnedProcess ?? defaultProbeSpawnedProcess;
50695
50952
  this.handshakeTimeoutMs = options.handshakeTimeoutMs ?? 1e4;
50696
50953
  this.handshakePollMs = options.handshakePollMs ?? 50;
50697
- this.uuid = options.uuid ?? randomUUID8;
50954
+ this.uuid = options.uuid ?? randomUUID10;
50698
50955
  this.sleep = options.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
50699
50956
  }
50700
50957
  async launch(input) {
@@ -56556,7 +56813,7 @@ var init_session_turn_runner = __esm(() => {
56556
56813
  });
56557
56814
 
56558
56815
  // src/control/turn-queue.ts
56559
- import { randomUUID as randomUUID9 } from "node:crypto";
56816
+ import { randomUUID as randomUUID11 } from "node:crypto";
56560
56817
 
56561
56818
  class TurnQueue {
56562
56819
  deps;
@@ -56600,7 +56857,7 @@ class TurnQueue {
56600
56857
  if (q.length >= QUEUE_MAX_DEPTH) {
56601
56858
  return { ok: false, errorMessage: "queue-full" };
56602
56859
  }
56603
- const id = randomUUID9();
56860
+ const id = randomUUID11();
56604
56861
  const item = {
56605
56862
  id,
56606
56863
  text: params.text,
@@ -57403,7 +57660,7 @@ function warmthKey(session3) {
57403
57660
  }
57404
57661
 
57405
57662
  // src/control/terminal-service.ts
57406
- import { randomUUID as randomUUID10 } from "node:crypto";
57663
+ import { randomUUID as randomUUID12 } from "node:crypto";
57407
57664
  import { createRequire as createRequire8 } from "node:module";
57408
57665
  import { statSync as statSync4 } from "node:fs";
57409
57666
  import { spawn as spawnPty2 } from "node-pty";
@@ -57514,7 +57771,7 @@ function createTerminalService(deps) {
57514
57771
  return {
57515
57772
  create({ cwd, cols, rows }) {
57516
57773
  const shell = resolveShell({ platform, env: process.env, shellOverride: deps.shell?.() });
57517
- const terminalId = randomUUID10();
57774
+ const terminalId = randomUUID12();
57518
57775
  const handle = spawn18(shell, [], { name: "xterm-256color", cols, rows, cwd, env: scrubEnv() });
57519
57776
  const session3 = { handle, seq: 0, idleTimer: null, buffer: "", bufBytes: 0, pending: "", pendingBytes: 0, flushTimer: null };
57520
57777
  sessions.set(terminalId, session3);
@@ -58260,7 +58517,7 @@ __export(exports_main, {
58260
58517
  main: () => main,
58261
58518
  buildApp: () => buildApp
58262
58519
  });
58263
- import { randomUUID as randomUUID11 } from "node:crypto";
58520
+ import { randomUUID as randomUUID13 } from "node:crypto";
58264
58521
  import { homedir as homedir17 } from "node:os";
58265
58522
  import { dirname as dirname24, join as join35 } from "node:path";
58266
58523
  import { fileURLToPath as fileURLToPath6 } from "node:url";
@@ -58376,6 +58633,14 @@ async function buildApp(paths, deps = {}) {
58376
58633
  message: stateLoadReport.backupError
58377
58634
  });
58378
58635
  }
58636
+ for (const record3 of stateLoadReport.migrated ?? []) {
58637
+ await logger.info("state.session_id_migrated", "assigned a new logical_session_id to a legacy session record", {
58638
+ statePath: paths.statePath,
58639
+ section: record3.section,
58640
+ key: record3.key,
58641
+ reason: record3.reason
58642
+ });
58643
+ }
58379
58644
  }
58380
58645
  const stateMutex = new AsyncMutex;
58381
58646
  const debouncedStateStore = new DebouncedStateStore({
@@ -58389,11 +58654,13 @@ async function buildApp(paths, deps = {}) {
58389
58654
  });
58390
58655
  const runtimeRoot = dirname24(paths.configPath);
58391
58656
  const sessions = new SessionService(config4, debouncedStateStore, state, { stateMutex, runtimeRoot });
58657
+ const sessionResources = new CoreSessionResourceCatalog({ sessions, config: config4, logger });
58658
+ sessions.setSessionResourceLifecyclePublisher((transition) => sessionResources.publishLifecycleEvent(transition));
58392
58659
  const launchIntentCoordinator = new LaunchIntentCoordinator({
58393
58660
  platform: process.platform,
58394
58661
  runtimeRoot,
58395
58662
  configRoot: runtimeRoot,
58396
- generationId: deps.daemonIdentity?.generationId ?? randomUUID11(),
58663
+ generationId: deps.daemonIdentity?.generationId ?? randomUUID13(),
58397
58664
  ...deps.orphanRegistry ? { registry: deps.orphanRegistry } : {},
58398
58665
  classifyAdapter: (command) => classifyPreinstalledAdapterCommandShape(command),
58399
58666
  resolveAdapter: async (command) => (await validateAndReResolveAdapterCommand(runtimeRoot, command)).agentCommand,
@@ -58735,7 +59002,7 @@ async function buildApp(paths, deps = {}) {
58735
59002
  };
58736
59003
  orchestration3 = new OrchestrationService({
58737
59004
  now: deps.loggerNow ?? (() => new Date),
58738
- createId: () => randomUUID11(),
59005
+ createId: () => randomUUID13(),
58739
59006
  config: config4,
58740
59007
  loadState: async () => JSON.parse(JSON.stringify(state)),
58741
59008
  saveState: async (nextState) => {
@@ -59008,6 +59275,7 @@ async function buildApp(paths, deps = {}) {
59008
59275
  agent: agent3,
59009
59276
  router: router3,
59010
59277
  sessions,
59278
+ sessionResources,
59011
59279
  activeTurns,
59012
59280
  stateStore,
59013
59281
  configStore,
@@ -59144,6 +59412,7 @@ var init_main = __esm(() => {
59144
59412
  init_scheduled_route_create();
59145
59413
  init_scheduled_route_manage();
59146
59414
  init_session_service();
59415
+ init_session_resource_catalog();
59147
59416
  init_state_store();
59148
59417
  init_acpx_bridge_client();
59149
59418
  init_acpx_bridge_transport();
@@ -59344,6 +59613,7 @@ async function runConsole(paths, deps) {
59344
59613
  abortSignal: shutdownController.signal,
59345
59614
  quota: runtime.quota,
59346
59615
  sessions: runtime.sessions,
59616
+ sessionResources: runtime.sessionResources,
59347
59617
  activeTurns: runtime.activeTurns,
59348
59618
  logger: runtime.logger,
59349
59619
  perfTracer: runtime.perfTracer,
@@ -59509,14 +59779,14 @@ class MessageChannelRegistry {
59509
59779
  throw new Error("all channels failed to start");
59510
59780
  }
59511
59781
  }
59512
- async stopAll() {
59782
+ async stopAll(reason = "shutdown") {
59513
59783
  let firstError;
59514
59784
  for (const channel of this.channels.values()) {
59515
59785
  try {
59516
59786
  if (channel.stop) {
59517
- await channel.stop();
59787
+ await channel.stop(reason);
59518
59788
  } else {
59519
- channel.logout();
59789
+ await channel.logout();
59520
59790
  }
59521
59791
  } catch (error2) {
59522
59792
  firstError ??= error2;
@@ -61232,11 +61502,20 @@ function applyStateInspectionReport(result, report, statePath, daemonRunning, is
61232
61502
  if (!report) {
61233
61503
  return result;
61234
61504
  }
61505
+ const migrated = report.migrated ?? [];
61506
+ const migrationDetails = migrated.map((record3) => `legacy session record ${record3.section}["${record3.key}"]: ${record3.reason} (applied automatically at next daemon startup)`);
61507
+ if (report.dropped.length === 0) {
61508
+ return {
61509
+ ...result,
61510
+ details: [...result.details ?? [], `state path: ${statePath}`, ...migrationDetails]
61511
+ };
61512
+ }
61235
61513
  const fileCorrupt = report.dropped.some((record3) => record3.section === "file");
61236
61514
  const details = [
61237
61515
  ...result.details ?? [],
61238
61516
  `state path: ${statePath}`,
61239
- ...report.dropped.map((record3) => record3.section === "file" ? `state.json is unreadable: ${record3.reason}` : `invalid state record ${record3.section}["${record3.key}"]: ${record3.reason}`)
61517
+ ...report.dropped.map((record3) => record3.section === "file" ? `state.json is unreadable: ${record3.reason}` : `invalid state record ${record3.section}["${record3.key}"]: ${record3.reason}`),
61518
+ ...migrationDetails
61240
61519
  ];
61241
61520
  return {
61242
61521
  ...result,
@@ -61272,9 +61551,11 @@ function createStateQuarantineFix(statePath, daemonRunning, isDaemonRunning) {
61272
61551
  return { ok: true, message: `state.json was unreadable; renamed to ${report.corruptPath} and reset` };
61273
61552
  }
61274
61553
  const backup = report.quarantinePath ? ` (original backed up to ${report.quarantinePath})` : "";
61554
+ const migratedCount = report.migrated?.length ?? 0;
61555
+ const migration = migratedCount > 0 ? `; assigned logical_session_id to ${migratedCount} legacy session record(s)` : "";
61275
61556
  return {
61276
61557
  ok: true,
61277
- message: `quarantined ${report.dropped.length} invalid state.json record(s)${backup}`
61558
+ message: `quarantined ${report.dropped.length} invalid state.json record(s)${backup}${migration}`
61278
61559
  };
61279
61560
  }
61280
61561
  };
@@ -61354,7 +61635,7 @@ __export(exports_cli, {
61354
61635
  getUsageText: () => getUsageText,
61355
61636
  createMcpStdioIdentityResolver: () => createMcpStdioIdentityResolver
61356
61637
  });
61357
- import { randomUUID as randomUUID12 } from "node:crypto";
61638
+ import { randomUUID as randomUUID14 } from "node:crypto";
61358
61639
  import { homedir as homedir23 } from "node:os";
61359
61640
  import { dirname as dirname26, join as join39, sep as sep6 } from "node:path";
61360
61641
  import { fileURLToPath as fileURLToPath8 } from "node:url";
@@ -61400,7 +61681,7 @@ async function prepareMcpCoordinatorStartup(input) {
61400
61681
  return { kind: "external-coordinator" };
61401
61682
  }
61402
61683
  function createMcpStdioIdentityResolver(input) {
61403
- const instanceId = randomUUID12().slice(0, 8);
61684
+ const instanceId = randomUUID14().slice(0, 8);
61404
61685
  return async (context) => {
61405
61686
  const parsedCoordinatorSession = input.parsedCoordinatorSession?.trim() || null;
61406
61687
  const workspace3 = input.workspace?.trim() || null;
@@ -61745,6 +62026,10 @@ function warnStateLoadReport(store, writeStderr = (text) => process.stderr.write
61745
62026
  }
61746
62027
  if (report.backupError) {
61747
62028
  writeStderr(`[xacpx] state.quarantine_backup_failed ${report.backupError}
62029
+ `);
62030
+ }
62031
+ for (const record3 of report.migrated ?? []) {
62032
+ writeStderr(`[xacpx] state.session_id_migrated section=${record3.section}${record3.key ? ` key=${record3.key}` : ""} reason=${record3.reason}
61748
62033
  `);
61749
62034
  }
61750
62035
  }
@@ -62064,7 +62349,7 @@ async function defaultLogin() {
62064
62349
  }
62065
62350
  async function defaultLogout() {
62066
62351
  const channel = await resolveLoginChannelForCli();
62067
- channel.logout();
62352
+ await channel.logout();
62068
62353
  }
62069
62354
  async function defaultLoadConfiguredPluginsForChannelCli() {
62070
62355
  const store = await createCliConfigStore();
@@ -62294,7 +62579,23 @@ async function createChannelCliDeps(input) {
62294
62579
  restartDaemon: async () => await restartDaemonCli(controller, input.print),
62295
62580
  clearChannelCredentials: async (channel) => {
62296
62581
  const { createMessageChannel: createMessageChannel2 } = await Promise.resolve().then(() => (init_create_channel(), exports_create_channel));
62297
- createMessageChannel2(channel.type, channel).logout();
62582
+ await createMessageChannel2(channel.type, channel).logout();
62583
+ },
62584
+ retireChannel: async (channel, reason) => {
62585
+ const { invokeChannelRetireHook: invokeChannelRetireHook2 } = await Promise.resolve().then(() => (init_plugin(), exports_plugin));
62586
+ await invokeChannelRetireHook2({
62587
+ channel,
62588
+ reason,
62589
+ print: input.print,
62590
+ getDaemonStatus: async () => {
62591
+ const status = await controller.getStatus();
62592
+ if (status.state === "running")
62593
+ return { state: "running" };
62594
+ if (status.state === "indeterminate")
62595
+ return { state: "indeterminate" };
62596
+ return { state: "stopped" };
62597
+ }
62598
+ });
62298
62599
  }
62299
62600
  };
62300
62601
  return { ...base, ...input.overrides };