@songsid/agend 2.1.8-beta.6 → 2.1.8-beta.8

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.
Files changed (39) hide show
  1. package/dist/backend/credential-profile.d.ts +9 -0
  2. package/dist/backend/credential-profile.js +20 -0
  3. package/dist/backend/credential-profile.js.map +1 -1
  4. package/dist/backend/muse.d.ts +6 -0
  5. package/dist/backend/muse.js +7 -1
  6. package/dist/backend/muse.js.map +1 -1
  7. package/dist/cli.js +91 -46
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.d.ts +1 -0
  10. package/dist/config.js +1 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/cross-instance-envelope.d.ts +7 -0
  13. package/dist/cross-instance-envelope.js +19 -1
  14. package/dist/cross-instance-envelope.js.map +1 -1
  15. package/dist/daemon.d.ts +16 -1
  16. package/dist/daemon.js +40 -2
  17. package/dist/daemon.js.map +1 -1
  18. package/dist/fleet-manager.d.ts +10 -1
  19. package/dist/fleet-manager.js +44 -12
  20. package/dist/fleet-manager.js.map +1 -1
  21. package/dist/forged-envelope-scan.d.ts +160 -0
  22. package/dist/forged-envelope-scan.js +312 -0
  23. package/dist/forged-envelope-scan.js.map +1 -0
  24. package/dist/instance-lifecycle.d.ts +3 -1
  25. package/dist/instance-lifecycle.js +16 -5
  26. package/dist/instance-lifecycle.js.map +1 -1
  27. package/dist/locale.js +10 -4
  28. package/dist/locale.js.map +1 -1
  29. package/dist/ls-rows.d.ts +17 -0
  30. package/dist/ls-rows.js +73 -0
  31. package/dist/ls-rows.js.map +1 -1
  32. package/dist/paths.js +4 -2
  33. package/dist/paths.js.map +1 -1
  34. package/dist/topic-commands.js +23 -19
  35. package/dist/topic-commands.js.map +1 -1
  36. package/dist/transcript-sources.d.ts +30 -0
  37. package/dist/transcript-sources.js +78 -0
  38. package/dist/transcript-sources.js.map +1 -1
  39. package/package.json +1 -1
@@ -967,6 +967,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
967
967
  private handleDecisionCrud;
968
968
  /** Resolve display name for an instance, fallback to instance name. */
969
969
  resolveDisplayName(instanceName: string): string;
970
+ /** Inherited keys such as __proto__ must never resolve to a mutable instance. */
971
+ private ownInstanceConfig;
970
972
  /** Persist identity to the instance's actual config store. Classic instances
971
973
  * are registry rows in classicBot.yaml, not fleet.yaml instance entries. */
972
974
  private setInstanceDisplayName;
@@ -1672,7 +1674,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1672
1674
  /** `/install-cli cancel` — abort the active install and remove its window. */
1673
1675
  cancelInstallSession(): Promise<string>;
1674
1676
  /** `command -v` on a login shell, so PATH additions from rc files count. */
1675
- private verifyBinaryOnLoginShell;
1677
+ /**
1678
+ * The absolute path a fresh login shell resolves `binary` to, or null. Only
1679
+ * a real executable file counts: `command -v` also answers with an alias
1680
+ * definition or a function name, neither of which a spawn could run.
1681
+ */
1682
+ private locateBinaryOnLoginShell;
1683
+ /** Put an installed binary's directory on the fleet's PATH (#1059), once. */
1684
+ private adoptBinaryDirectory;
1676
1685
  /** "Sign in now?" button after a successful install. */
1677
1686
  private handleInstallLoginConfirm;
1678
1687
  /** Discord native `/login` slash — shared by every adapter dispatch block. */
@@ -1,10 +1,10 @@
1
- import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, rmSync, readdirSync, renameSync, copyFileSync, chmodSync, statSync } from "node:fs";
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, rmSync, readdirSync, renameSync, copyFileSync, chmodSync, statSync, accessSync, constants as fsConstants } from "node:fs";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { freemem, totalmem, cpus } from "node:os";
5
5
  import { createServer } from "node:http";
6
6
  import { Worker } from "node:worker_threads";
7
- import { join, dirname, basename } from "node:path";
7
+ import { join, dirname, basename, delimiter, isAbsolute } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { getAgendHome, ensureWorkspaceGit } from "./paths.js";
10
10
  import { beginFullRestartProgress as persistFullRestartProgress, beginUpdateProgress as persistUpdateProgress, clearUpdateMarker, isUpdateInProgress, readUpdateProgress, setUpdateProgressStage, updateProgressOperation, } from "./update-marker.js";
@@ -6002,10 +6002,15 @@ export class FleetManager {
6002
6002
  ?? this.classicChannels?.getAll().find(ch => ch.instanceName === instanceName)?.displayName
6003
6003
  ?? instanceName;
6004
6004
  }
6005
+ /** Inherited keys such as __proto__ must never resolve to a mutable instance. */
6006
+ ownInstanceConfig(instanceName) {
6007
+ const instances = this.fleetConfig?.instances;
6008
+ return instances && Object.hasOwn(instances, instanceName) ? instances[instanceName] : undefined;
6009
+ }
6005
6010
  /** Persist identity to the instance's actual config store. Classic instances
6006
6011
  * are registry rows in classicBot.yaml, not fleet.yaml instance entries. */
6007
6012
  setInstanceDisplayName(instanceName, displayName) {
6008
- const fleetInstance = this.fleetConfig?.instances[instanceName];
6013
+ const fleetInstance = this.ownInstanceConfig(instanceName);
6009
6014
  if (fleetInstance) {
6010
6015
  fleetInstance.display_name = displayName;
6011
6016
  this.saveFleetConfig();
@@ -6014,7 +6019,7 @@ export class FleetManager {
6014
6019
  return this.classicChannels?.setDisplayNameByInstance(instanceName, displayName) ?? false;
6015
6020
  }
6016
6021
  setInstanceDescription(instanceName, description) {
6017
- const fleetInstance = this.fleetConfig?.instances[instanceName];
6022
+ const fleetInstance = this.ownInstanceConfig(instanceName);
6018
6023
  if (fleetInstance) {
6019
6024
  fleetInstance.description = description;
6020
6025
  this.saveFleetConfig();
@@ -7110,7 +7115,7 @@ export class FleetManager {
7110
7115
  * An empty emoji removes that status's override.
7111
7116
  */
7112
7117
  async setPersonaEmoji(instanceName, args) {
7113
- const self = this.fleetConfig?.instances[instanceName];
7118
+ const self = this.ownInstanceConfig(instanceName);
7114
7119
  if (!self)
7115
7120
  return { error: this.personaEmojiMissing(instanceName) };
7116
7121
  const status = (args.status ?? "delivered");
@@ -7155,8 +7160,9 @@ export class FleetManager {
7155
7160
  // Re-read after the validation above, which may have waited on Discord:
7156
7161
  // another set_persona_emoji or a Settings save can have changed another
7157
7162
  // status, or replaced this instance's config object, meanwhile. Change
7158
- // only this one key on what is there now.
7159
- const current = this.fleetConfig?.instances[instanceName];
7163
+ // only this one key on what is there now. Ownership must still hold:
7164
+ // removal during the await must not fall through to an inherited entry.
7165
+ const current = this.ownInstanceConfig(instanceName);
7160
7166
  if (!current)
7161
7167
  return { error: this.personaEmojiMissing(instanceName) };
7162
7168
  const map = { ...(current.status_emojis ?? {}) };
@@ -9683,10 +9689,16 @@ export class FleetManager {
9683
9689
  }
9684
9690
  // The installer may only have added the binary to a profile PATH; a
9685
9691
  // fresh login shell sees that, the fleet process's PATH may not.
9686
- if (!this.verifyBinaryOnLoginShell(info.binary)) {
9692
+ const installedAt = this.locateBinaryOnLoginShell(info.binary);
9693
+ if (!installedAt) {
9687
9694
  await chat.adapter.sendText(chat.chatId, t("install.verify_failed", backend, info.binary), { threadId: chat.threadId }).catch(() => { });
9688
9695
  return;
9689
9696
  }
9697
+ // #1059: make it reachable from this process too. `/login` lists what
9698
+ // `which` finds on the fleet's own PATH, and instances resolve their
9699
+ // binary the same way, so a binary only a login shell could see was
9700
+ // installed, reported as verified, and then offered nowhere.
9701
+ this.adoptBinaryDirectory(installedAt, backend);
9690
9702
  if (!LOGIN_FLOWS[backend]) {
9691
9703
  await chat.adapter.sendText(chat.chatId, t("install.success_no_login", backend), { threadId: chat.threadId }).catch(() => { });
9692
9704
  return;
@@ -9746,15 +9758,35 @@ export class FleetManager {
9746
9758
  return t("install.cancelled", backend);
9747
9759
  }
9748
9760
  /** `command -v` on a login shell, so PATH additions from rc files count. */
9749
- verifyBinaryOnLoginShell(binary) {
9761
+ /**
9762
+ * The absolute path a fresh login shell resolves `binary` to, or null. Only
9763
+ * a real executable file counts: `command -v` also answers with an alias
9764
+ * definition or a function name, neither of which a spawn could run.
9765
+ */
9766
+ locateBinaryOnLoginShell(binary) {
9750
9767
  try {
9751
- const result = spawnSync("bash", ["-lc", `command -v ${binary}`], { timeout: 10_000, stdio: "pipe" });
9752
- return result.status === 0;
9768
+ const result = spawnSync("bash", ["-lc", `command -v ${binary}`], { timeout: 10_000, stdio: "pipe", encoding: "utf8" });
9769
+ if (result.status !== 0)
9770
+ return null;
9771
+ const path = String(result.stdout ?? "").trim().split("\n").pop()?.trim() ?? "";
9772
+ if (!isAbsolute(path))
9773
+ return null;
9774
+ accessSync(path, fsConstants.X_OK);
9775
+ return statSync(path).isFile() ? path : null;
9753
9776
  }
9754
9777
  catch {
9755
- return false;
9778
+ return null;
9756
9779
  }
9757
9780
  }
9781
+ /** Put an installed binary's directory on the fleet's PATH (#1059), once. */
9782
+ adoptBinaryDirectory(binaryPath, backend) {
9783
+ const dir = dirname(binaryPath);
9784
+ const entries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
9785
+ if (entries.includes(dir))
9786
+ return;
9787
+ process.env.PATH = [dir, ...entries].join(delimiter);
9788
+ this.logger.info({ backend, dir }, "Added the installed CLI's directory to the fleet PATH");
9789
+ }
9758
9790
  /** "Sign in now?" button after a successful install. */
9759
9791
  async handleInstallLoginConfirm(data, callbackAdapterId, receivingAdapter) {
9760
9792
  const claimed = this.consumeNonceCallback(INSTALL_LOGIN_CALLBACK_PREFIX, /^install-login:([0-9a-f]+):(go|later)$/, data, callbackAdapterId, receivingAdapter);