@ametyst/cli 0.3.5 → 0.3.6

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 (2) hide show
  1. package/dist/index.js +564 -173
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -106446,7 +106446,12 @@ function loadConfig() {
106446
106446
  // pbi-wallet-3 — projected, or the cached stance would survive exactly one
106447
106447
  // process and then vanish (the whitelist warning above).
106448
106448
  ...typeof p.requireWalletPassphrase === "boolean" ? { requireWalletPassphrase: p.requireWalletPassphrase } : {},
106449
- ...typeof p.requireWalletPassphraseObservedAt === "string" ? { requireWalletPassphraseObservedAt: p.requireWalletPassphraseObservedAt } : {}
106449
+ ...typeof p.requireWalletPassphraseObservedAt === "string" ? { requireWalletPassphraseObservedAt: p.requireWalletPassphraseObservedAt } : {},
106450
+ // The workspace binding — projected, or a switch could never be detected:
106451
+ // the key would survive exactly one process and then vanish (the whitelist
106452
+ // warning above), making every login look like a first binding.
106453
+ ...typeof p.workspaceId === "string" ? { workspaceId: p.workspaceId } : {},
106454
+ ...typeof p.workspaceIdObservedAt === "string" ? { workspaceIdObservedAt: p.workspaceIdObservedAt } : {}
106450
106455
  };
106451
106456
  } catch {
106452
106457
  return null;
@@ -111782,7 +111787,8 @@ __export(core_exports, {
111782
111787
  reencryptPlaintextVault: () => reencryptPlaintextVault,
111783
111788
  safeVaultErrorMessage: () => safeVaultErrorMessage,
111784
111789
  saveWallet: () => saveWallet,
111785
- wipeVault: () => wipeVault
111790
+ wipeVault: () => wipeVault,
111791
+ writeSecretFileAtomic: () => writeSecretFileAtomic
111786
111792
  });
111787
111793
  import {
111788
111794
  chmodSync as chmodSync2,
@@ -111873,8 +111879,11 @@ function writeAllSync(fd, buf) {
111873
111879
  }
111874
111880
  function writeVaultFileAtomic(contents) {
111875
111881
  ensureDirectories();
111876
- const dir = dirname5(VAULT_PATH);
111877
- const tmp = join5(dir, `.wallet.json.tmp-${process.pid}-${randomBytes3(8).toString("hex")}`);
111882
+ writeSecretFileAtomic(VAULT_PATH, contents);
111883
+ }
111884
+ function writeSecretFileAtomic(path2, contents) {
111885
+ const dir = dirname5(path2);
111886
+ const tmp = join5(dir, `.${basename3(path2)}.tmp-${process.pid}-${randomBytes3(8).toString("hex")}`);
111878
111887
  let fd;
111879
111888
  try {
111880
111889
  fd = openSync(tmp, "wx", 384);
@@ -111883,7 +111892,7 @@ function writeVaultFileAtomic(contents) {
111883
111892
  fsyncSync(fd);
111884
111893
  closeSync(fd);
111885
111894
  fd = void 0;
111886
- renameSync(tmp, VAULT_PATH);
111895
+ renameSync(tmp, path2);
111887
111896
  fsyncDir(dir);
111888
111897
  } catch (e) {
111889
111898
  if (fd !== void 0) {
@@ -112013,6 +112022,212 @@ var init_core = __esm({
112013
112022
  }
112014
112023
  });
112015
112024
 
112025
+ // src/wallet/grant-keystore.ts
112026
+ var grant_keystore_exports = {};
112027
+ __export(grant_keystore_exports, {
112028
+ GRANT_ENTRY_FORMAT: () => GRANT_ENTRY_FORMAT,
112029
+ buildGrantVault: () => buildGrantVault,
112030
+ createGrantKeyIssuer: () => createGrantKeyIssuer,
112031
+ deleteGrantEntry: () => deleteGrantEntry,
112032
+ ensureGrantsDir: () => ensureGrantsDir,
112033
+ generateGrantKey: () => generateGrantKey,
112034
+ grantEntryPath: () => grantEntryPath,
112035
+ grantsDir: () => grantsDir,
112036
+ listGrantAddresses: () => listGrantAddresses,
112037
+ listGrantEntries: () => listGrantEntries,
112038
+ missingGrantKeyMessage: () => missingGrantKeyMessage,
112039
+ normalizeGrantAddress: () => normalizeGrantAddress,
112040
+ parseGrantEntry: () => parseGrantEntry,
112041
+ persistGrantKey: () => persistGrantKey,
112042
+ readGrantEntry: () => readGrantEntry,
112043
+ recordGrantRowId: () => recordGrantRowId,
112044
+ resolveGrantSigningKey: () => resolveGrantSigningKey
112045
+ });
112046
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
112047
+ import { join as join6 } from "path";
112048
+ function grantsDir() {
112049
+ return join6(WALLETS_DIR, "grants");
112050
+ }
112051
+ function resolveDir(dir) {
112052
+ return dir ?? grantsDir();
112053
+ }
112054
+ function ensureGrantsDir(dir) {
112055
+ const target = resolveDir(dir);
112056
+ if (target === grantsDir()) ensureDirectories();
112057
+ if (!existsSync7(target)) mkdirSync6(target, { mode: 448, recursive: true });
112058
+ return target;
112059
+ }
112060
+ function normalizeGrantAddress(address) {
112061
+ if (typeof address !== "string" || !ADDRESS_RE2.test(address)) {
112062
+ throw new Error(`Not a 0x-hex EOA address: ${JSON.stringify(address)}`);
112063
+ }
112064
+ return address.toLowerCase();
112065
+ }
112066
+ function grantEntryPath(address, dir) {
112067
+ return join6(resolveDir(dir), `${normalizeGrantAddress(address)}.json`);
112068
+ }
112069
+ function generateGrantKey() {
112070
+ const derived = derivePlaintextWallet(generatePrivateKey());
112071
+ return { address: derived.address.toLowerCase(), privateKey: derived.privateKey };
112072
+ }
112073
+ function buildGrantVault(address, privateKey, options) {
112074
+ if (options.encrypted) {
112075
+ if (!options.passphrase) {
112076
+ throw new Error(
112077
+ "Refusing to store a session key unencrypted: this machine's wallet is passphrase-protected but no passphrase is in memory. Call start_session({ passphrase }) first."
112078
+ );
112079
+ }
112080
+ return JSON.parse(importWallet(privateKey, options.passphrase));
112081
+ }
112082
+ return JSON.parse(serializePlaintextVault(address, privateKey));
112083
+ }
112084
+ function persistGrantKey(params, dir) {
112085
+ const address = normalizeGrantAddress(params.address);
112086
+ const target = ensureGrantsDir(dir);
112087
+ const entry = {
112088
+ format: GRANT_ENTRY_FORMAT,
112089
+ address,
112090
+ createdAt: params.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
112091
+ workspaceId: params.workspaceId ?? null,
112092
+ policyId: params.policyId ?? null,
112093
+ backendWalletRowId: params.backendWalletRowId ?? null,
112094
+ vault: buildGrantVault(address, params.privateKey, {
112095
+ encrypted: params.encrypted,
112096
+ passphrase: params.passphrase
112097
+ })
112098
+ };
112099
+ writeSecretFileAtomic(join6(target, `${address}.json`), JSON.stringify(entry, null, 2));
112100
+ return entry;
112101
+ }
112102
+ function parseGrantEntry(json) {
112103
+ let parsed;
112104
+ try {
112105
+ parsed = JSON.parse(json);
112106
+ } catch {
112107
+ return null;
112108
+ }
112109
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
112110
+ const p = parsed;
112111
+ if (p.format !== GRANT_ENTRY_FORMAT) return null;
112112
+ if (typeof p.address !== "string" || !ADDRESS_RE2.test(p.address)) return null;
112113
+ if (p.vault === null || typeof p.vault !== "object" || Array.isArray(p.vault)) return null;
112114
+ return {
112115
+ format: GRANT_ENTRY_FORMAT,
112116
+ address: p.address.toLowerCase(),
112117
+ createdAt: typeof p.createdAt === "string" ? p.createdAt : "",
112118
+ workspaceId: typeof p.workspaceId === "string" ? p.workspaceId : null,
112119
+ policyId: typeof p.policyId === "number" ? p.policyId : null,
112120
+ backendWalletRowId: typeof p.backendWalletRowId === "string" ? p.backendWalletRowId : null,
112121
+ vault: p.vault
112122
+ };
112123
+ }
112124
+ function readGrantEntry(address, dir) {
112125
+ let path2;
112126
+ try {
112127
+ path2 = grantEntryPath(address, dir);
112128
+ } catch {
112129
+ return null;
112130
+ }
112131
+ try {
112132
+ return parseGrantEntry(readFileSync7(path2, "utf-8"));
112133
+ } catch {
112134
+ return null;
112135
+ }
112136
+ }
112137
+ function listGrantEntries(dir) {
112138
+ const target = resolveDir(dir);
112139
+ let names;
112140
+ try {
112141
+ names = readdirSync4(target);
112142
+ } catch {
112143
+ return [];
112144
+ }
112145
+ const entries = [];
112146
+ for (const name of names) {
112147
+ if (!name.endsWith(".json")) continue;
112148
+ let raw;
112149
+ try {
112150
+ raw = readFileSync7(join6(target, name), "utf-8");
112151
+ } catch {
112152
+ continue;
112153
+ }
112154
+ const entry = parseGrantEntry(raw);
112155
+ if (entry) entries.push(entry);
112156
+ }
112157
+ return entries;
112158
+ }
112159
+ function listGrantAddresses(options = {}, dir) {
112160
+ const active = options.workspaceId ?? null;
112161
+ return listGrantEntries(dir).filter((e) => active === null || e.workspaceId === null || e.workspaceId === active).map((e) => e.address);
112162
+ }
112163
+ function recordGrantRowId(address, rowId, dir) {
112164
+ const entry = readGrantEntry(address, dir);
112165
+ if (!entry) return false;
112166
+ try {
112167
+ const next = { ...entry, backendWalletRowId: rowId ?? null };
112168
+ writeSecretFileAtomic(grantEntryPath(address, dir), JSON.stringify(next, null, 2));
112169
+ return true;
112170
+ } catch {
112171
+ return false;
112172
+ }
112173
+ }
112174
+ function deleteGrantEntry(address, dir) {
112175
+ try {
112176
+ unlinkSync5(grantEntryPath(address, dir));
112177
+ } catch {
112178
+ }
112179
+ }
112180
+ function missingGrantKeyMessage(address) {
112181
+ return `no local key for grant address ${address} \u2014 this machine cannot sign for that grant. Run requestAccess again to mint a fresh session key and have your admin approve it.`;
112182
+ }
112183
+ function resolveGrantSigningKey(params) {
112184
+ const address = normalizeGrantAddress(params.address);
112185
+ const entry = readGrantEntry(address, params.dir);
112186
+ if (entry) {
112187
+ return readVaultPrivateKey(JSON.stringify(entry.vault), params.passphrase);
112188
+ }
112189
+ const vaultAddress = params.vaultAddress;
112190
+ if (params.vaultJson && typeof vaultAddress === "string" && vaultAddress.toLowerCase() === address) {
112191
+ return readVaultPrivateKey(params.vaultJson, params.passphrase);
112192
+ }
112193
+ throw new Error(missingGrantKeyMessage(address));
112194
+ }
112195
+ function createGrantKeyIssuer(options) {
112196
+ return {
112197
+ issue({ policyId }) {
112198
+ const { address, privateKey } = generateGrantKey();
112199
+ persistGrantKey(
112200
+ {
112201
+ address,
112202
+ privateKey,
112203
+ encrypted: options.encrypted,
112204
+ passphrase: options.passphrase,
112205
+ workspaceId: options.workspaceId ?? null,
112206
+ policyId
112207
+ },
112208
+ options.dir
112209
+ );
112210
+ return address;
112211
+ },
112212
+ recordRow(address, rowId) {
112213
+ recordGrantRowId(address, rowId, options.dir);
112214
+ }
112215
+ };
112216
+ }
112217
+ var GRANT_ENTRY_FORMAT, ADDRESS_RE2;
112218
+ var init_grant_keystore = __esm({
112219
+ "src/wallet/grant-keystore.ts"() {
112220
+ "use strict";
112221
+ init_esm_shims();
112222
+ init_accounts();
112223
+ init_paths();
112224
+ init_core();
112225
+ init_plaintext_vault();
112226
+ GRANT_ENTRY_FORMAT = "grant-key-v1";
112227
+ ADDRESS_RE2 = /^0x[0-9a-fA-F]{40}$/;
112228
+ }
112229
+ });
112230
+
112016
112231
  // src/commands/init-claim.ts
112017
112232
  var init_claim_exports = {};
112018
112233
  __export(init_claim_exports, {
@@ -112026,7 +112241,7 @@ __export(init_claim_exports, {
112026
112241
  vaultBackupPath: () => vaultBackupPath,
112027
112242
  vaultBackupSuffix: () => vaultBackupSuffix
112028
112243
  });
112029
- import { chmodSync as chmodSync3, constants as fsConstants, copyFileSync as copyFileSync3, existsSync as existsSync7 } from "fs";
112244
+ import { chmodSync as chmodSync3, constants as fsConstants, copyFileSync as copyFileSync3, existsSync as existsSync8 } from "fs";
112030
112245
  import { createDecipheriv as createDecipheriv2 } from "crypto";
112031
112246
  function claimErrorMessage(failure) {
112032
112247
  switch (failure.kind) {
@@ -112169,7 +112384,7 @@ function vaultBackupPath(vaultPath, at) {
112169
112384
  return `${vaultPath}.bak-${vaultBackupSuffix(at)}`;
112170
112385
  }
112171
112386
  function backupVaultFile(vaultPath, at = /* @__PURE__ */ new Date()) {
112172
- if (!existsSync7(vaultPath)) return null;
112387
+ if (!existsSync8(vaultPath)) return null;
112173
112388
  const target = vaultBackupPath(vaultPath, at);
112174
112389
  try {
112175
112390
  copyFileSync3(vaultPath, target, fsConstants.COPYFILE_EXCL);
@@ -112197,13 +112412,13 @@ var init_version5 = __esm({
112197
112412
  "src/version.ts"() {
112198
112413
  "use strict";
112199
112414
  init_esm_shims();
112200
- CLI_VERSION = true ? "0.3.5" : "0.0.0-dev";
112415
+ CLI_VERSION = true ? "0.3.6" : "0.0.0-dev";
112201
112416
  }
112202
112417
  });
112203
112418
 
112204
112419
  // src/connections/catalog.ts
112205
- import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
112206
- import { join as join6 } from "path";
112420
+ import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
112421
+ import { join as join7 } from "path";
112207
112422
  function declaredVerbsHeader() {
112208
112423
  return SUPPORTED_HTTP_METHODS.join(",");
112209
112424
  }
@@ -112459,8 +112674,8 @@ function parseConnectorsResponseDetailed(body, onWarn = warnToStderr) {
112459
112674
  function readCachedCatalogStatus() {
112460
112675
  let raw;
112461
112676
  try {
112462
- if (!existsSync8(CONNECTOR_CACHE_PATH)) return { state: "absent" };
112463
- raw = JSON.parse(readFileSync7(CONNECTOR_CACHE_PATH, "utf-8"));
112677
+ if (!existsSync9(CONNECTOR_CACHE_PATH)) return { state: "absent" };
112678
+ raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
112464
112679
  } catch (e) {
112465
112680
  return { state: "unreadable", reason: e instanceof Error ? e.message : String(e) };
112466
112681
  }
@@ -112496,8 +112711,8 @@ function readMirrorRowsFromDisk() {
112496
112711
  }
112497
112712
  function readMirrorContainer() {
112498
112713
  try {
112499
- if (!existsSync8(CONNECTOR_CACHE_PATH)) return { state: "absent" };
112500
- const raw = JSON.parse(readFileSync7(CONNECTOR_CACHE_PATH, "utf-8"));
112714
+ if (!existsSync9(CONNECTOR_CACHE_PATH)) return { state: "absent" };
112715
+ const raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
112501
112716
  if (!isRecord(raw)) {
112502
112717
  return { state: "unreadable", reason: "the mirror's top level is not an object" };
112503
112718
  }
@@ -112511,8 +112726,8 @@ function readMirrorContainer() {
112511
112726
  }
112512
112727
  function readMirrorFetchedAt() {
112513
112728
  try {
112514
- if (!existsSync8(CONNECTOR_CACHE_PATH)) return "";
112515
- const raw = JSON.parse(readFileSync7(CONNECTOR_CACHE_PATH, "utf-8"));
112729
+ if (!existsSync9(CONNECTOR_CACHE_PATH)) return "";
112730
+ const raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
112516
112731
  if (!isRecord(raw)) return "";
112517
112732
  return typeof raw.fetchedAt === "string" ? raw.fetchedAt : "";
112518
112733
  } catch {
@@ -112763,7 +112978,7 @@ var init_catalog = __esm({
112763
112978
  SOURCE_OPERATORS = ["provider", "third_party", "ametyst"];
112764
112979
  SOURCE_PREFERENCE_RULES = ["official-mcp"];
112765
112980
  CONNECTOR_FEED_PATH = "/api/v1/virtual-wallets/capabilities/connectors";
112766
- CONNECTOR_CACHE_PATH = join6(AMETYST_DIR, "connectors.json");
112981
+ CONNECTOR_CACHE_PATH = join7(AMETYST_DIR, "connectors.json");
112767
112982
  CatalogUnavailableError = class extends Error {
112768
112983
  constructor(reason, message) {
112769
112984
  super(message);
@@ -113501,15 +113716,15 @@ var init_call2 = __esm({
113501
113716
  });
113502
113717
 
113503
113718
  // src/connections/engine-store.ts
113504
- import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync11, mkdirSync as mkdirSync7, openSync as openSync2 } from "fs";
113505
- import { dirname as dirname7, join as join16 } from "path";
113719
+ import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync12, mkdirSync as mkdirSync8, openSync as openSync2 } from "fs";
113720
+ import { dirname as dirname7, join as join17 } from "path";
113506
113721
  function connectionsDbPath() {
113507
- return join16(AMETYST_DIR, CONNECTIONS_DB_FILENAME);
113722
+ return join17(AMETYST_DIR, CONNECTIONS_DB_FILENAME);
113508
113723
  }
113509
113724
  function ensureOwnerOnlyFile(path2) {
113510
113725
  if (path2 === IN_MEMORY_DB) return;
113511
- mkdirSync7(dirname7(path2), { recursive: true, mode: DIR_MODE });
113512
- if (!existsSync11(path2)) closeSync2(openSync2(path2, "a", CONNECTIONS_DB_FILE_MODE));
113726
+ mkdirSync8(dirname7(path2), { recursive: true, mode: DIR_MODE });
113727
+ if (!existsSync12(path2)) closeSync2(openSync2(path2, "a", CONNECTIONS_DB_FILE_MODE));
113513
113728
  chmodSync4(path2, CONNECTIONS_DB_FILE_MODE);
113514
113729
  }
113515
113730
  function sqliteWasmDriver(db, raw) {
@@ -114718,6 +114933,44 @@ function hostNudges(rows) {
114718
114933
 
114719
114934
  // src/commands/login.ts
114720
114935
  init_resolve();
114936
+
114937
+ // src/config/workspace-binding.ts
114938
+ init_esm_shims();
114939
+ init_config();
114940
+ function workspaceKeyFrom(profile) {
114941
+ const name = profile?.companyName;
114942
+ if (typeof name !== "string") return null;
114943
+ const trimmed = name.trim().toLowerCase();
114944
+ return trimmed === "" ? null : trimmed;
114945
+ }
114946
+ function isWorkspaceSwitch(previous, next) {
114947
+ if (previous === null || next === null) return false;
114948
+ return previous !== next;
114949
+ }
114950
+ function readBoundWorkspace() {
114951
+ const config = loadConfig();
114952
+ const key = config?.workspaceId;
114953
+ return typeof key === "string" && key.trim() !== "" ? key : null;
114954
+ }
114955
+ function recordBoundWorkspace(key, observedAt) {
114956
+ const config = loadConfig();
114957
+ if (!config) return false;
114958
+ const { apiKey: _injected, apiKeySource: _src, ...persisted } = config;
114959
+ saveConfig({ ...persisted, workspaceId: key, workspaceIdObservedAt: observedAt });
114960
+ return true;
114961
+ }
114962
+ function workspaceSwitchNotice(previous, next) {
114963
+ return `Workspace changed: "${previous}" -> "${next}". Dropping the cached session and wallet binding from the previous workspace \u2014 its session keys are kept on disk (they stay signable if you switch back) but are no longer selected here.`;
114964
+ }
114965
+ function applyObservedWorkspace(profile, observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
114966
+ const key = workspaceKeyFrom(profile);
114967
+ const previous = readBoundWorkspace();
114968
+ const switched = isWorkspaceSwitch(previous, key);
114969
+ if (key !== null && key !== previous) recordBoundWorkspace(key, observedAt);
114970
+ return { key, previous, switched };
114971
+ }
114972
+
114973
+ // src/commands/login.ts
114721
114974
  function lastObservedStanceLine(config) {
114722
114975
  const observed = config?.requireWalletPassphrase;
114723
114976
  if (typeof observed !== "boolean") {
@@ -114779,6 +115032,10 @@ async function loginCommand(options) {
114779
115032
  if (observedStance !== void 0) {
114780
115033
  setRequireWalletPassphrase(observedStance, (/* @__PURE__ */ new Date()).toISOString());
114781
115034
  }
115035
+ const workspace = applyObservedWorkspace(profile);
115036
+ if (workspace.switched && workspace.previous && workspace.key) {
115037
+ console.log(workspaceSwitchNotice(workspace.previous, workspace.key));
115038
+ }
114782
115039
  const stance = resolveWalletPassphraseStance(observedStance, config.requireWalletPassphrase);
114783
115040
  const who = profile ? `Logged in as ${profile.employeeName} @ ${profile.companyName ?? "(no company)"}.` : "Logged in.";
114784
115041
  console.log(who);
@@ -114926,8 +115183,9 @@ async function fetchAvailablePolicies(client, apiKey) {
114926
115183
  const response = await client.getPoliciesFromVirtualWalletsManager(apiKey);
114927
115184
  return normalizePoliciesResponse(response);
114928
115185
  }
114929
- async function submitAccessRequest(client, apiKey, eoaAddress, policyId) {
114930
- return client.createVirtualWallet(
115186
+ async function submitRotatedAccessRequest(client, apiKey, policyId, issuer) {
115187
+ const eoaAddress = await issuer.issue({ policyId });
115188
+ const result = await client.createVirtualWallet(
114931
115189
  apiKey,
114932
115190
  CLI_WALLET_NAME,
114933
115191
  CLI_WALLET_URL,
@@ -114935,6 +115193,11 @@ async function submitAccessRequest(client, apiKey, eoaAddress, policyId) {
114935
115193
  policyId,
114936
115194
  eoaAddress
114937
115195
  );
115196
+ try {
115197
+ issuer.recordRow?.(eoaAddress, result?.id ? String(result.id) : void 0);
115198
+ } catch {
115199
+ }
115200
+ return { ...result, eoaAddress };
114938
115201
  }
114939
115202
  function pickDefaultPolicyIndex(policies) {
114940
115203
  const i = policies.findIndex(
@@ -115133,6 +115396,7 @@ async function freshWalletRecovery(apiKey, options) {
115133
115396
  }
115134
115397
  }
115135
115398
  let address;
115399
+ let vaultPassphrase;
115136
115400
  if (options.noPassphrase) {
115137
115401
  const wallet = createPlaintextWallet();
115138
115402
  persistPlaintextVault(wallet.address, wallet.privateKey);
@@ -115153,6 +115417,7 @@ New wallet created. Address: ${address}`);
115153
115417
  process.exit(1);
115154
115418
  return false;
115155
115419
  }
115420
+ vaultPassphrase = passphrase;
115156
115421
  const keystoreJson = createWallet(passphrase);
115157
115422
  persistVault(keystoreJson);
115158
115423
  address = getWalletAddress(keystoreJson);
@@ -115201,8 +115466,15 @@ Create one in the Ametyst web app (admin panel \u2192 policies), then re-run 'am
115201
115466
  process.exit(1);
115202
115467
  return false;
115203
115468
  }
115469
+ let grantAddress;
115204
115470
  try {
115205
- await submitAccessRequest(client, apiKey, address, policyId);
115471
+ const { createGrantKeyIssuer: createGrantKeyIssuer2 } = await Promise.resolve().then(() => (init_grant_keystore(), grant_keystore_exports));
115472
+ const submitted = await submitRotatedAccessRequest(client, apiKey, policyId, createGrantKeyIssuer2({
115473
+ encrypted: !options.noPassphrase,
115474
+ passphrase: vaultPassphrase,
115475
+ workspaceId: readBoundWorkspace()
115476
+ }));
115477
+ grantAddress = submitted.eoaAddress;
115206
115478
  } catch (e) {
115207
115479
  const msg = e instanceof Error ? e.message : String(e);
115208
115480
  console.error(`
@@ -115214,8 +115486,9 @@ Could not submit the authorization request (${msg}).`);
115214
115486
  const policyName = typeof chosen.name === "string" && chosen.name ? chosen.name : `policy ${policyId}`;
115215
115487
  console.log(`
115216
115488
  Authorization request sent \u2014 PENDING approval.
115217
- Wallet: ${address}
115218
- Policy: ${policyName} (id ${policyId})
115489
+ Wallet: ${address}
115490
+ Session key: ${grantAddress}
115491
+ Policy: ${policyName} (id ${policyId})
115219
115492
 
115220
115493
  Next step: approve the request in the Ametyst web app admin panel (check your
115221
115494
  notifications there). Once it's approved, you're done \u2014 run 'ametyst status' to
@@ -115437,14 +115710,19 @@ function isNewerGrant(candidate, incumbent) {
115437
115710
  }
115438
115711
  return candidateIdUsable && !incumbentIdUsable;
115439
115712
  }
115440
- function selectNewestApprovedWallet(wallets, eoaAddress) {
115713
+ function selectNewestApprovedWalletAmong(wallets, addresses) {
115441
115714
  if (!Array.isArray(wallets)) return void 0;
115442
- const eoa = nonEmptyLowerCase(eoaAddress);
115443
- if (!eoa) return void 0;
115715
+ const candidates = /* @__PURE__ */ new Set();
115716
+ for (const address of addresses ?? []) {
115717
+ const normalized = nonEmptyLowerCase(address);
115718
+ if (normalized) candidates.add(normalized);
115719
+ }
115720
+ if (candidates.size === 0) return void 0;
115444
115721
  let newest;
115445
115722
  for (const wallet of wallets) {
115446
115723
  if (wallet?.status !== "approved") continue;
115447
- if (nonEmptyLowerCase(wallet?.address) !== eoa) continue;
115724
+ const rowAddress = nonEmptyLowerCase(wallet?.address);
115725
+ if (!rowAddress || !candidates.has(rowAddress)) continue;
115448
115726
  if (newest === void 0 || isNewerGrant(wallet, newest)) newest = wallet;
115449
115727
  }
115450
115728
  return newest;
@@ -115454,7 +115732,10 @@ function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
115454
115732
  if (pendingWalletId) {
115455
115733
  return wallets.find((w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved");
115456
115734
  }
115457
- return selectNewestApprovedWallet(wallets, eoaAddress);
115735
+ return selectNewestApprovedWalletAmong(
115736
+ wallets,
115737
+ Array.isArray(eoaAddress) ? eoaAddress : eoaAddress === void 0 ? [] : [eoaAddress]
115738
+ );
115458
115739
  }
115459
115740
 
115460
115741
  // src/mcp-server/start-session.ts
@@ -115594,7 +115875,10 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
115594
115875
  let activeWallet;
115595
115876
  let grantChanged = false;
115596
115877
  try {
115597
- activeWallet = selectNewestApprovedWallet(wallets, ctx.eoaAddress);
115878
+ activeWallet = selectNewestApprovedWalletAmong(wallets, [
115879
+ ctx.eoaAddress,
115880
+ ...ctx.candidateAddresses ?? []
115881
+ ]);
115598
115882
  if (activeWallet) {
115599
115883
  const newestId = String(activeWallet.id);
115600
115884
  const rowWalletAddress = activeWallet.walletAddress || activeWallet.kernelAccountAddress;
@@ -115614,6 +115898,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
115614
115898
  policyId = rowPolicyId ?? policyId;
115615
115899
  }
115616
115900
  virtualWalletId = newestId;
115901
+ if (typeof activeWallet.address === "string" && activeWallet.address.trim() !== "") {
115902
+ credentials.signerAddress = activeWallet.address;
115903
+ }
115617
115904
  credentials.walletAddress = walletAddress;
115618
115905
  credentials.virtualWalletId = virtualWalletId;
115619
115906
  credentials.paymentManagerAddress = paymentManagerAddress;
@@ -115646,7 +115933,8 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
115646
115933
  if (walletAddress && virtualWalletId && paymentManagerAddress && policyId) {
115647
115934
  let privateKey = null;
115648
115935
  try {
115649
- privateKey = readVaultPrivateKey(ctx.walletKeystoreJson, ctx.passphrase);
115936
+ const signerAddress = credentials.signerAddress ?? ctx.eoaAddress;
115937
+ privateKey = ctx.resolveSigningKey && signerAddress ? ctx.resolveSigningKey(signerAddress) : readVaultPrivateKey(ctx.walletKeystoreJson, ctx.passphrase);
115650
115938
  const { virtualWalletsManagers: virtualWalletsManagers2, financialAccounts: financialAccounts2 } = ctx.sdk;
115651
115939
  const walletData = await virtualWalletsManagers2.getVirtualWalletDataByApiKey(
115652
115940
  ctx.apiKey,
@@ -116946,13 +117234,13 @@ init_local_state();
116946
117234
  // src/mcp-server/materialize-task.ts
116947
117235
  init_esm_shims();
116948
117236
  init_paths();
116949
- import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
116950
- import { join as join8 } from "path";
117237
+ import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
117238
+ import { join as join9 } from "path";
116951
117239
 
116952
117240
  // src/loops/state-docs.ts
116953
117241
  init_esm_shims();
116954
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
116955
- import { join as join7 } from "path";
117242
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
117243
+ import { join as join8 } from "path";
116956
117244
 
116957
117245
  // src/loops/shipback.ts
116958
117246
  init_esm_shims();
@@ -117385,12 +117673,12 @@ function docMergeRefusal(boot, fresh, next, cleanAppend) {
117385
117673
  async function shipBackStateDocs(sdk, apiKey, slug, dir, boot) {
117386
117674
  const outcomes = [];
117387
117675
  for (const doc of boot) {
117388
- const path2 = join7(dir, doc.filename);
117389
- if (!existsSync9(path2)) {
117676
+ const path2 = join8(dir, doc.filename);
117677
+ if (!existsSync10(path2)) {
117390
117678
  outcomes.push({ key: doc.key, outcome: "unchanged" });
117391
117679
  continue;
117392
117680
  }
117393
- const materialized = readFileSync8(path2, "utf-8");
117681
+ const materialized = readFileSync9(path2, "utf-8");
117394
117682
  const reread = await readScoped(sdk, apiKey, slug, doc.key, doc.scope);
117395
117683
  const fresh = reread.status === "ok" ? reread.body : reread.status === "absent" && doc.body === "" ? "" : void 0;
117396
117684
  if (fresh === void 0) {
@@ -117460,20 +117748,20 @@ var TASK_DEFINITION_FILES = [
117460
117748
  ];
117461
117749
  function materializeTask(task, runId) {
117462
117750
  const dir = loopFireDir(task.slug, runId);
117463
- mkdirSync6(dir, { recursive: true, mode: 448 });
117751
+ mkdirSync7(dir, { recursive: true, mode: 448 });
117464
117752
  const files = {};
117465
117753
  const skipped = [];
117466
117754
  for (const [label2, filename, field] of TASK_DEFINITION_FILES) {
117467
117755
  const body = task[field];
117468
117756
  if (typeof body === "string" && body.length > 0) {
117469
- writeFileSync8(join8(dir, filename), body, { mode: 384 });
117757
+ writeFileSync8(join9(dir, filename), body, { mode: 384 });
117470
117758
  files[label2] = filename;
117471
117759
  } else {
117472
117760
  skipped.push(label2);
117473
117761
  }
117474
117762
  }
117475
117763
  writeFileSync8(
117476
- join8(dir, "STATUS.md"),
117764
+ join9(dir, "STATUS.md"),
117477
117765
  `# STATUS \u2014 ${task.slug}
117478
117766
 
117479
117767
  task_id: ${task.id}
@@ -117504,7 +117792,7 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
117504
117792
  notes.push(`memory doc '${f.key}' not materialized: ${f.reason}`);
117505
117793
  }
117506
117794
  for (const doc of fetched) {
117507
- writeFileSync8(join8(dir, doc.filename), doc.body, { mode: 384 });
117795
+ writeFileSync8(join9(dir, doc.filename), doc.body, { mode: 384 });
117508
117796
  files[`doc:${doc.key}`] = doc.filename;
117509
117797
  notes.push(describeSource(doc));
117510
117798
  }
@@ -117518,14 +117806,14 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
117518
117806
  }
117519
117807
 
117520
117808
  // src/mcp-server/index.ts
117521
- import { existsSync as existsSync12 } from "fs";
117809
+ import { existsSync as existsSync13 } from "fs";
117522
117810
 
117523
117811
  // src/loops/dashboard.ts
117524
117812
  init_esm_shims();
117525
117813
  import { createServer as createServer2 } from "http";
117526
117814
  import * as realFs from "fs";
117527
117815
  import { spawn as realSpawn } from "child_process";
117528
- import { join as join9 } from "path";
117816
+ import { join as join10 } from "path";
117529
117817
  var DEFAULT_PORT = 4477;
117530
117818
  var DASHBOARD_PORT_ENV = "AMETYST_LOOP_DASHBOARD_PORT";
117531
117819
  var DASHBOARD_NO_OPEN_ENV = "AMETYST_DASHBOARD_NO_OPEN";
@@ -117586,14 +117874,14 @@ function startDashboardServer(args) {
117586
117874
  const data = {};
117587
117875
  for (const name of files) {
117588
117876
  try {
117589
- const p = join9(args.loopDir, name);
117877
+ const p = join10(args.loopDir, name);
117590
117878
  if (fs.existsSync(p)) data[name] = fs.readFileSync(p, "utf-8");
117591
117879
  } catch {
117592
117880
  }
117593
117881
  }
117594
117882
  const state = {};
117595
117883
  try {
117596
- const p = join9(args.loopDir, ".state", "fires.jsonl");
117884
+ const p = join10(args.loopDir, ".state", "fires.jsonl");
117597
117885
  if (fs.existsSync(p)) state["fires.jsonl"] = fs.readFileSync(p, "utf-8");
117598
117886
  } catch {
117599
117887
  }
@@ -117992,7 +118280,7 @@ function injectDefaultDashboard(loop2) {
117992
118280
 
117993
118281
  // src/loops/memory-verbs.ts
117994
118282
  init_esm_shims();
117995
- import { readFileSync as readFileSync9 } from "fs";
118283
+ import { readFileSync as readFileSync10 } from "fs";
117996
118284
 
117997
118285
  // src/loops/sdk.ts
117998
118286
  init_esm_shims();
@@ -118252,7 +118540,7 @@ async function taskMemoryAppendVerb(rawSlug, opts) {
118252
118540
  const archived = opts.archived === true;
118253
118541
  const note = opts.note?.trim() || void 0;
118254
118542
  const scope = parseScopeFlag(opts.scope, false);
118255
- const content = opts.file !== void 0 ? readFileSync9(opts.file, "utf-8") : opts.content ?? "";
118543
+ const content = opts.file !== void 0 ? readFileSync10(opts.file, "utf-8") : opts.content ?? "";
118256
118544
  if (!content) {
118257
118545
  throw new Error("nothing to store: pass --content <text> or --file <path>");
118258
118546
  }
@@ -118389,14 +118677,14 @@ ${full.markdownBody ?? ""}`;
118389
118677
 
118390
118678
  // src/mcp-server/read-file-body.ts
118391
118679
  init_esm_shims();
118392
- import { readFileSync as readFileSync10 } from "fs";
118680
+ import { readFileSync as readFileSync11 } from "fs";
118393
118681
  import { resolve as resolvePath } from "path";
118394
118682
  function readMarkdownFile(filePath) {
118395
118683
  const raw = typeof filePath === "string" ? filePath.trim() : "";
118396
118684
  if (!raw) throw new Error("filePath is empty.");
118397
118685
  const abs = resolvePath(raw);
118398
118686
  try {
118399
- return readFileSync10(abs, "utf8");
118687
+ return readFileSync11(abs, "utf8");
118400
118688
  } catch (err) {
118401
118689
  const reason = err instanceof Error ? err.message : String(err);
118402
118690
  throw new Error(`Could not read file at "${abs}": ${reason}`);
@@ -118725,7 +119013,7 @@ function enforceResponseCap(payload) {
118725
119013
 
118726
119014
  // src/mcp-server/delegate-tools.ts
118727
119015
  init_esm_shims();
118728
- import { existsSync as existsSync10, statSync as statSync2 } from "fs";
119016
+ import { existsSync as existsSync11, statSync as statSync2 } from "fs";
118729
119017
  import { z as z3 } from "zod";
118730
119018
 
118731
119019
  // src/delegate/jobs.ts
@@ -118827,7 +119115,7 @@ import * as nodeFs from "fs";
118827
119115
  import { execFileSync } from "child_process";
118828
119116
  import { createHash } from "crypto";
118829
119117
  import { homedir as homedir5 } from "os";
118830
- import { join as join10 } from "path";
119118
+ import { join as join11 } from "path";
118831
119119
  var OPENCODE_VERSION = "1.18.13";
118832
119120
  var OPENCODE_CHECKSUMS = {
118833
119121
  "darwin-arm64": "6a85ae6de1aeb8e39ae4d977337b03f49168c2a827ee37b6f82c39471d711c63",
@@ -118849,10 +119137,10 @@ function resolveOpencodePlatform(platform, arch) {
118849
119137
  return void 0;
118850
119138
  }
118851
119139
  function opencodeBinDir(home = homedir5()) {
118852
- return join10(home, `.ametyst${ENV_SUFFIX}`, "bin");
119140
+ return join11(home, `.ametyst${ENV_SUFFIX}`, "bin");
118853
119141
  }
118854
119142
  function opencodeBinaryPath(home = homedir5()) {
118855
- return join10(opencodeBinDir(home), `opencode-${OPENCODE_VERSION}`);
119143
+ return join11(opencodeBinDir(home), `opencode-${OPENCODE_VERSION}`);
118856
119144
  }
118857
119145
  function opencodeSidecarPath(home = homedir5()) {
118858
119146
  return `${opencodeBinaryPath(home)}.sha256`;
@@ -118907,7 +119195,7 @@ async function ensureOpencodeBinary(deps = {}) {
118907
119195
  const bytes = new Uint8Array(await res.arrayBuffer());
118908
119196
  const expected = (deps.checksums ?? OPENCODE_CHECKSUMS)[key];
118909
119197
  const actual = sha256Hex(bytes);
118910
- const tmpArchive = join10(binDir, `.${artifact}.download-${process.pid}`);
119198
+ const tmpArchive = join11(binDir, `.${artifact}.download-${process.pid}`);
118911
119199
  fs.writeFileSync(tmpArchive, bytes);
118912
119200
  if (actual !== expected) {
118913
119201
  fs.rmSync(tmpArchive, { force: true });
@@ -118915,7 +119203,7 @@ async function ensureOpencodeBinary(deps = {}) {
118915
119203
  `opencode bootstrap: checksum mismatch for ${artifact} (expected ${expected}, got ${actual}). The download was deleted; refusing to install an unverified binary.`
118916
119204
  );
118917
119205
  }
118918
- const tmpExtractDir = join10(binDir, `.extract-${OPENCODE_VERSION}-${process.pid}`);
119206
+ const tmpExtractDir = join11(binDir, `.extract-${OPENCODE_VERSION}-${process.pid}`);
118919
119207
  fs.rmSync(tmpExtractDir, { recursive: true, force: true });
118920
119208
  fs.mkdirSync(tmpExtractDir, { recursive: true, mode: 448 });
118921
119209
  try {
@@ -118924,7 +119212,7 @@ async function ensureOpencodeBinary(deps = {}) {
118924
119212
  } else {
118925
119213
  exec("tar", ["-xzf", tmpArchive, "-C", tmpExtractDir]);
118926
119214
  }
118927
- const extracted = join10(tmpExtractDir, "opencode");
119215
+ const extracted = join11(tmpExtractDir, "opencode");
118928
119216
  if (!fs.existsSync(extracted)) {
118929
119217
  const entries = fs.readdirSync(tmpExtractDir).join(", ") || "<empty>";
118930
119218
  throw new Error(
@@ -118995,15 +119283,15 @@ import { randomUUID } from "crypto";
118995
119283
  init_esm_shims();
118996
119284
  init_paths();
118997
119285
  import { homedir as homedir6 } from "os";
118998
- import { join as join11 } from "path";
119286
+ import { join as join12 } from "path";
118999
119287
  function bridgeRunDir(home = homedir6()) {
119000
- return join11(home, `.ametyst${ENV_SUFFIX}`, "run");
119288
+ return join12(home, `.ametyst${ENV_SUFFIX}`, "run");
119001
119289
  }
119002
119290
  function bridgeSocketPath(pid, home = homedir6()) {
119003
- return join11(bridgeRunDir(home), `delegate-${pid}.sock`);
119291
+ return join12(bridgeRunDir(home), `delegate-${pid}.sock`);
119004
119292
  }
119005
119293
  function bridgeNoncePath(pid, home = homedir6()) {
119006
- return join11(bridgeRunDir(home), `delegate-${pid}.nonce`);
119294
+ return join12(bridgeRunDir(home), `delegate-${pid}.nonce`);
119007
119295
  }
119008
119296
  function bridgeNoncePathForSocket(socketPath) {
119009
119297
  return socketPath.replace(/\.sock$/, ".nonce");
@@ -119025,7 +119313,7 @@ function listBridgeSockets(fs, home = homedir6()) {
119025
119313
  const entries = [];
119026
119314
  for (const name of names) {
119027
119315
  if (pidFromSocketName(name) === void 0) continue;
119028
- const path2 = join11(dir, name);
119316
+ const path2 = join12(dir, name);
119029
119317
  try {
119030
119318
  entries.push({ path: path2, mtimeMs: fs.statSync(path2).mtimeMs });
119031
119319
  } catch {
@@ -119374,7 +119662,7 @@ async function startShim(deps) {
119374
119662
  init_esm_shims();
119375
119663
  import * as nodeFs3 from "fs";
119376
119664
  import { tmpdir } from "os";
119377
- import { join as join12 } from "path";
119665
+ import { join as join13 } from "path";
119378
119666
  var DELEGATE_PROVIDER_ID = "ametyst";
119379
119667
  var DELEGATE_SHIM_API_KEY = "ametyst-local-shim";
119380
119668
  function buildDelegateConfig(opts) {
@@ -119398,8 +119686,8 @@ function delegateModelRef(model) {
119398
119686
  }
119399
119687
  function writeTempDelegateConfig(config, deps = {}) {
119400
119688
  const fs = deps.fs ?? nodeFs3;
119401
- const dir = fs.mkdtempSync(join12(deps.tmp ?? tmpdir(), "ametyst-delegate-"));
119402
- const path2 = join12(dir, "opencode.json");
119689
+ const dir = fs.mkdtempSync(join13(deps.tmp ?? tmpdir(), "ametyst-delegate-"));
119690
+ const path2 = join13(dir, "opencode.json");
119403
119691
  fs.writeFileSync(path2, `${JSON.stringify(config, null, 2)}
119404
119692
  `, { mode: 384 });
119405
119693
  let done = false;
@@ -119468,7 +119756,7 @@ init_paths();
119468
119756
  import * as nodeFs4 from "fs";
119469
119757
  import { execFileSync as execFileSync2 } from "child_process";
119470
119758
  import { homedir as homedir8 } from "os";
119471
- import { basename as basename4, join as join13 } from "path";
119759
+ import { basename as basename4, join as join14 } from "path";
119472
119760
  var DELEGATED_CHILD_ENV_VAR = "AMETYST_DELEGATED";
119473
119761
  var SPEND_GRANT_TOKEN_ENV_VAR = "AMETYST_DELEGATE_SPEND_TOKEN";
119474
119762
  var SPEND_KILL_SWITCH_ENV_VAR = "AMETYST_DELEGATE_NO_SPEND";
@@ -119550,11 +119838,11 @@ function isDelegatedByAncestry(deps = {}) {
119550
119838
  return classifyAncestry(deps) === "delegated";
119551
119839
  }
119552
119840
  function delegateChildConfigHome(home = homedir8()) {
119553
- return join13(home, `.ametyst${ENV_SUFFIX}`, "delegate-xdg");
119841
+ return join14(home, `.ametyst${ENV_SUFFIX}`, "delegate-xdg");
119554
119842
  }
119555
119843
  function ensureChildConfigHome(fs = nodeFs4, home = homedir8()) {
119556
119844
  const root2 = delegateChildConfigHome(home);
119557
- const inner = join13(root2, "opencode");
119845
+ const inner = join14(root2, "opencode");
119558
119846
  if (!fs.existsSync(inner)) fs.mkdirSync(inner, { recursive: true, mode: 448 });
119559
119847
  return root2;
119560
119848
  }
@@ -119564,7 +119852,7 @@ function userOpencodeConfigDir(deps = {}) {
119564
119852
  const env = deps.env ?? process.env;
119565
119853
  const home = deps.home ?? homedir8();
119566
119854
  const xdg = env.XDG_CONFIG_HOME?.trim();
119567
- return join13(xdg ? xdg : join13(home, ".config"), "opencode");
119855
+ return join14(xdg ? xdg : join14(home, ".config"), "opencode");
119568
119856
  }
119569
119857
  function stripJsonComments(text) {
119570
119858
  let out = "";
@@ -119608,9 +119896,9 @@ function readUserMcpServers(deps = {}) {
119608
119896
  const warn = deps.warn ?? ((line) => console.error(line));
119609
119897
  const home = deps.home ?? homedir8();
119610
119898
  const dir = userOpencodeConfigDir(deps);
119611
- if (dir === join13(delegateChildConfigHome(home), "opencode")) return {};
119899
+ if (dir === join14(delegateChildConfigHome(home), "opencode")) return {};
119612
119900
  for (const filename of USER_OPENCODE_CONFIG_FILENAMES) {
119613
- const path2 = join13(dir, filename);
119901
+ const path2 = join14(dir, filename);
119614
119902
  let raw;
119615
119903
  try {
119616
119904
  if (!fs.existsSync(path2)) continue;
@@ -120708,7 +120996,7 @@ init_esm_shims();
120708
120996
  import * as nodeFs6 from "fs";
120709
120997
  import { createHash as createHash2, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
120710
120998
  import { homedir as homedir10 } from "os";
120711
- import { join as join14 } from "path";
120999
+ import { join as join15 } from "path";
120712
121000
  var SPEND_GRANT_RECORD_VERSION = 1;
120713
121001
  var TOKEN_BYTES = 32;
120714
121002
  var SPEND_GRANT_GRACE_MS = 5 * 6e4;
@@ -120716,7 +121004,7 @@ function spendGrantPath(jobId, home = homedir10()) {
120716
121004
  if (!/^[A-Za-z0-9_-]{1,64}$/.test(jobId)) {
120717
121005
  throw new Error(`refusing to build a grant path for job id ${JSON.stringify(jobId)}`);
120718
121006
  }
120719
- return join14(bridgeRunDir(home), `spend-grant-${jobId}.json`);
121007
+ return join15(bridgeRunDir(home), `spend-grant-${jobId}.json`);
120720
121008
  }
120721
121009
  function hashSpendGrantToken(token) {
120722
121010
  return createHash2("sha256").update(token, "utf-8").digest("hex");
@@ -120822,13 +121110,13 @@ init_esm_shims();
120822
121110
  init_paths();
120823
121111
  import * as nodeFs7 from "fs";
120824
121112
  import { homedir as homedir11 } from "os";
120825
- import { dirname as dirname6, join as join15 } from "path";
121113
+ import { dirname as dirname6, join as join16 } from "path";
120826
121114
  var DELEGATE_CREDIT_CAPABILITY = "credit";
120827
121115
  var DELEGATE_CREDIT_PRICE_USD = 1;
120828
121116
  var DELEGATE_CREDIT_REQUEST_BODY = "{}";
120829
121117
  var DELEGATE_CREDIT_SPEND_SNIPPET = `spend({ merchant_slug: "${DELEGATE_MERCHANT_SLUG}", capability: "${DELEGATE_CREDIT_CAPABILITY}", inputs: "${DELEGATE_CREDIT_REQUEST_BODY}" })`;
120830
121118
  function creditLedgerPath(home = homedir11()) {
120831
- return join15(home, `.ametyst${ENV_SUFFIX}`, "run", "delegate-credit.json");
121119
+ return join16(home, `.ametyst${ENV_SUFFIX}`, "run", "delegate-credit.json");
120832
121120
  }
120833
121121
  function readCreditLedger(fs = nodeFs7, path2 = creditLedgerPath()) {
120834
121122
  try {
@@ -121219,7 +121507,7 @@ function registerDelegateTools(deps) {
121219
121507
  const validated = validateStartInput(params, {
121220
121508
  isDirectory: (path2) => {
121221
121509
  try {
121222
- return existsSync10(path2) && statSync2(path2).isDirectory();
121510
+ return existsSync11(path2) && statSync2(path2).isDirectory();
121223
121511
  } catch {
121224
121512
  return false;
121225
121513
  }
@@ -121474,7 +121762,7 @@ init_resolve();
121474
121762
 
121475
121763
  // src/mcp-server/identity-reload.ts
121476
121764
  init_esm_shims();
121477
- import { readFileSync as readFileSync12, statSync as statSync3 } from "fs";
121765
+ import { readFileSync as readFileSync13, statSync as statSync3 } from "fs";
121478
121766
  var NO_VAULT = "absent";
121479
121767
  var IDENTITY_CHECK_THROTTLE_MS = 1e3;
121480
121768
  function fingerprintVault(stat) {
@@ -121495,7 +121783,11 @@ var IdentityWatch = class {
121495
121783
  stat;
121496
121784
  /** False for an env-supplied key: `poll` is then a guaranteed no-op. */
121497
121785
  armed;
121786
+ configPath;
121787
+ readWorkspaceKey;
121498
121788
  fingerprint;
121789
+ configStamp;
121790
+ workspaceKey;
121499
121791
  lastCheckedAt = null;
121500
121792
  constructor(options) {
121501
121793
  this.vaultPath = options.vaultPath;
@@ -121503,12 +121795,43 @@ var IdentityWatch = class {
121503
121795
  this.throttleMs = options.throttleMs ?? IDENTITY_CHECK_THROTTLE_MS;
121504
121796
  this.stat = options.stat ?? statVaultFile;
121505
121797
  this.armed = options.source !== "env";
121506
- this.fingerprint = fingerprintVault(this.stat(this.vaultPath));
121798
+ this.configPath = options.configPath ?? null;
121799
+ this.readWorkspaceKey = options.readWorkspaceKey ?? null;
121800
+ this.configStamp = this.configPath ? fingerprintVault(this.stat(this.configPath)) : NO_VAULT;
121801
+ this.workspaceKey = this.readWorkspaceKeySafely();
121802
+ this.fingerprint = this.composeFingerprint();
121803
+ }
121804
+ /** Never let a config read take the watch — or the tool call behind it — down. */
121805
+ readWorkspaceKeySafely() {
121806
+ if (!this.readWorkspaceKey) return null;
121807
+ try {
121808
+ return this.readWorkspaceKey();
121809
+ } catch {
121810
+ return null;
121811
+ }
121812
+ }
121813
+ /**
121814
+ * Vault fingerprint AND workspace key in one string, so `poll` stays a single
121815
+ * comparison and neither half can change without being noticed.
121816
+ *
121817
+ * A watch with NO workspace reader appends nothing, so its fingerprint stays
121818
+ * exactly the string it was before 0.3.6 (`"<mtime>:<size>"` / `"absent"`).
121819
+ * That is not cosmetic: the fingerprint is an observable of this class, and a
121820
+ * vault-only watch must be indistinguishable from the one that shipped.
121821
+ */
121822
+ composeFingerprint() {
121823
+ const vault = fingerprintVault(this.stat(this.vaultPath));
121824
+ if (!this.readWorkspaceKey) return vault;
121825
+ return `${vault}|ws:${this.workspaceKey ?? "unbound"}`;
121507
121826
  }
121508
121827
  /** The fingerprint this watch currently considers current. */
121509
121828
  currentFingerprint() {
121510
121829
  return this.fingerprint;
121511
121830
  }
121831
+ /** The workspace key this watch last read from the config. */
121832
+ currentWorkspaceKey() {
121833
+ return this.workspaceKey;
121834
+ }
121512
121835
  /**
121513
121836
  * One throttled `stat`. Returns the change when the vault file differs from
121514
121837
  * the fingerprint on record — and ADOPTS it in the same step, so a reload the
@@ -121521,7 +121844,14 @@ var IdentityWatch = class {
121521
121844
  const at = this.now();
121522
121845
  if (this.lastCheckedAt !== null && at - this.lastCheckedAt < this.throttleMs) return null;
121523
121846
  this.lastCheckedAt = at;
121524
- const next = fingerprintVault(this.stat(this.vaultPath));
121847
+ if (this.configPath) {
121848
+ const stamp = fingerprintVault(this.stat(this.configPath));
121849
+ if (stamp !== this.configStamp) {
121850
+ this.configStamp = stamp;
121851
+ this.workspaceKey = this.readWorkspaceKeySafely();
121852
+ }
121853
+ }
121854
+ const next = this.composeFingerprint();
121525
121855
  if (next === this.fingerprint) return null;
121526
121856
  const from14 = this.fingerprint;
121527
121857
  this.fingerprint = next;
@@ -121537,7 +121867,7 @@ var IdentityWatch = class {
121537
121867
  */
121538
121868
  readVault() {
121539
121869
  try {
121540
- return readFileSync12(this.vaultPath, "utf-8");
121870
+ return readFileSync13(this.vaultPath, "utf-8");
121541
121871
  } catch {
121542
121872
  return null;
121543
121873
  }
@@ -121545,6 +121875,7 @@ var IdentityWatch = class {
121545
121875
  };
121546
121876
 
121547
121877
  // src/mcp-server/index.ts
121878
+ init_grant_keystore();
121548
121879
  function buildDeclaredConnectorsSentence(catalog, unreadable) {
121549
121880
  const read = catalog === void 0 ? { connectors: cachedCatalogOrEmpty(), unreadable: mirrorGap() } : { connectors: catalog, unreadable: unreadable ?? [] };
121550
121881
  const gap = describeCatalogGap(read.unreadable);
@@ -121555,6 +121886,39 @@ function buildDeclaredConnectorsSentence(catalog, unreadable) {
121555
121886
  return `Currently declared: ${declared}. Those are the operations picked out as most useful, NOT the limit: each provider's own surface is larger, and any operation it advertises is callable by its exact name. If the one you want is not listed, try it anyway \u2014 an operation that really does not exist comes back as a typed \`unknown_operation\` with the alternatives.` + (gap === "" ? "" : ` ${gap}`);
121556
121887
  }
121557
121888
  var currentCredentials = {};
121889
+ function vaultIsEncrypted() {
121890
+ return !plaintextVaultMode;
121891
+ }
121892
+ function ownedSigningAddresses() {
121893
+ const addresses = [];
121894
+ if (currentCredentials.eoaAddress) addresses.push(currentCredentials.eoaAddress);
121895
+ try {
121896
+ addresses.push(...listGrantAddresses({ workspaceId: readBoundWorkspace() }));
121897
+ } catch (err) {
121898
+ console.error(
121899
+ `\u26A0\uFE0F could not read the per-grant keystore \u2014 only the install EOA is selectable: ${err instanceof Error ? err.message : String(err)}`
121900
+ );
121901
+ }
121902
+ return addresses;
121903
+ }
121904
+ function isOwnedSigningAddress(address) {
121905
+ if (typeof address !== "string" || address.trim() === "") return false;
121906
+ const needle = address.toLowerCase();
121907
+ return ownedSigningAddresses().some((a) => a.toLowerCase() === needle);
121908
+ }
121909
+ function signingKeyForAddress(address, passphrase) {
121910
+ return resolveGrantSigningKey({
121911
+ address,
121912
+ vaultJson: currentCredentials.walletKeystoreJson,
121913
+ vaultAddress: currentCredentials.eoaAddress,
121914
+ // ⛔ THE CALLER'S PASSPHRASE WINS. `start_session` resolves the key DURING
121915
+ // `runStartSession`, before `mergeStartSessionCredentials` has put the
121916
+ // passphrase into `currentCredentials` — so reading only the field would
121917
+ // make every encrypted-vault unlock fail with "a passphrase is required"
121918
+ // on the very call that was handed one.
121919
+ passphrase: passphrase ?? currentCredentials.passphrase
121920
+ });
121921
+ }
121558
121922
  var plaintextVaultMode = false;
121559
121923
  function refreshPlaintextVaultMode() {
121560
121924
  const vault = currentCredentials.walletKeystoreJson;
@@ -122576,7 +122940,16 @@ async function initializeCredentials(walletKeystoreJson, eoaAddress, config) {
122576
122940
  currentCredentials.signerAddress = eoaAddress;
122577
122941
  currentCredentials.eoaAddress = eoaAddress;
122578
122942
  currentCredentials.apiKey = config.apiKey;
122579
- identityWatch = new IdentityWatch({ vaultPath: VAULT_PATH, source: config.apiKeySource ?? null });
122943
+ identityWatch = new IdentityWatch({
122944
+ vaultPath: VAULT_PATH,
122945
+ source: config.apiKeySource ?? null,
122946
+ // The workspace half (0.3.6): a `login`/`init --claim` that re-binds this
122947
+ // install to a DIFFERENT workspace changes the recorded key, and the same
122948
+ // reload that follows a swapped `wallet.json` drops the state cached for
122949
+ // the old one. See ./identity-reload.ts.
122950
+ configPath: CONFIG_PATH,
122951
+ readWorkspaceKey: readBoundWorkspace
122952
+ });
122580
122953
  mcpEventLogger = new McpEventLogger(
122581
122954
  () => getSDK(),
122582
122955
  () => currentCredentials.apiKey,
@@ -122662,10 +123035,8 @@ async function createKernelClientFromVault() {
122662
123035
  throw new Error("No passphrase or keystore available");
122663
123036
  }
122664
123037
  if (!cliConfig) throw new Error("CLI config not initialized");
122665
- const { readVaultPrivateKey: readVaultPrivateKey2 } = await Promise.resolve().then(() => (init_core(), core_exports));
122666
- let privateKey = readVaultPrivateKey2(
122667
- currentCredentials.walletKeystoreJson,
122668
- currentCredentials.passphrase
123038
+ let privateKey = signingKeyForAddress(
123039
+ currentCredentials.signerAddress || currentCredentials.eoaAddress || ""
122669
123040
  );
122670
123041
  console.error(`${ts()} Wallet key loaded`);
122671
123042
  try {
@@ -122806,12 +123177,17 @@ async function tryResolvePendingApproval(probe) {
122806
123177
  if (probe) probe.fetched = true;
122807
123178
  const approvedWallet = findCurrentApprovedWallet(
122808
123179
  freshWallets,
122809
- currentCredentials.eoaAddress,
123180
+ // Post-rotation the row belongs to the ephemeral key this request minted,
123181
+ // not to the install EOA — match on every address we hold a key for.
123182
+ ownedSigningAddresses(),
122810
123183
  currentCredentials.pendingWalletId
122811
123184
  );
122812
123185
  if (!approvedWallet) return false;
122813
123186
  console.error("\u2705 [approval-wait] Backend says approved \u2014 syncing");
122814
123187
  currentCredentials.authorizationStatus = "approved";
123188
+ if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
123189
+ currentCredentials.signerAddress = approvedWallet.address;
123190
+ }
122815
123191
  currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
122816
123192
  currentCredentials.virtualWalletId = String(approvedWallet.id);
122817
123193
  currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
@@ -122938,6 +123314,12 @@ async function applyStartSessionUnlock(passphrase, resetRequested) {
122938
123314
  // this process loaded at boot verifies nothing: a grant approved since boot
122939
123315
  // would be invisible to exactly the check that exists to find it.
122940
123316
  fetchWallets: () => fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true),
123317
+ // ROTATION (0.3.6). Grant selection must span every address this install
123318
+ // holds a key for, and the key that signs must be the SELECTED row's —
123319
+ // not whatever `wallet.json` happens to contain. Both are injected so
123320
+ // `start-session.ts` keeps no dependency on the keystore's fs layer.
123321
+ candidateAddresses: ownedSigningAddresses(),
123322
+ resolveSigningKey: (address) => signingKeyForAddress(address, passphrase),
122941
123323
  readPolicyActiveOnchain: buildPolicyActiveOnchainReader(sdk)
122942
123324
  }
122943
123325
  );
@@ -123351,11 +123733,15 @@ server.tool(
123351
123733
  };
123352
123734
  }
123353
123735
  const { virtualWalletsManagers: virtualWalletsManagers2 } = await getSDK();
123354
- const result = await submitAccessRequest(
123736
+ const result = await submitRotatedAccessRequest(
123355
123737
  virtualWalletsManagers2,
123356
123738
  currentCredentials.apiKey,
123357
- currentCredentials.eoaAddress,
123358
- policyId
123739
+ policyId,
123740
+ createGrantKeyIssuer({
123741
+ encrypted: vaultIsEncrypted(),
123742
+ passphrase: currentCredentials.passphrase,
123743
+ workspaceId: readBoundWorkspace()
123744
+ })
123359
123745
  );
123360
123746
  currentCredentials.authorizationStatus = "pending";
123361
123747
  currentCredentials.pendingWalletId = result.id ? String(result.id) : void 0;
@@ -123413,7 +123799,7 @@ server.tool(
123413
123799
  server.tool(
123414
123800
  {
123415
123801
  name: "getWalletStatus",
123416
- description: "Get wallet status: auth, balance, policy, allowlist, WHICH WORKSPACE this server is acting in, and the last 10 transactions (amount, merchant, timestamp, status). `workspace` is `{companyName, employeeName}` \u2014 the Ametyst workspace every task, memory document and payment from this server lands in; both fields are null when the profile could not be read, which means UNKNOWN, never 'no workspace'. A transaction `amount` is a EUR display string like `\u20AC0.0126`, or the em dash `\u2014` when the row carries no usable amount (none recorded, or a value that is not a base-units integer) \u2014 `\u2014` means UNKNOWN, never zero. The unformatted base-units value is on `amountRaw`, which is null for exactly those rows.",
123802
+ description: "Get wallet status: auth, balance, policy, allowlist, WHICH GRANT AND KEY THIS SESSION SIGNS WITH, WHICH WORKSPACE this server is acting in, and the last 10 transactions (amount, merchant, timestamp, status). `signerAddress` is the address of the approved grant this session signs with \u2014 since every access request now mints its OWN ephemeral session key, it is NOT the same as `eoaAddress` (the install wallet) except for a grant approved before rotation; `activeVirtualWalletId` / `activePolicyId` name that grant's row. `workspace` is `{companyName, employeeName}` \u2014 the Ametyst workspace every task, memory document and payment from this server lands in; both fields are null when the profile could not be read, which means UNKNOWN, never 'no workspace'. A transaction `amount` is a EUR display string like `\u20AC0.0126`, or the em dash `\u2014` when the row carries no usable amount (none recorded, or a value that is not a base-units integer) \u2014 `\u2014` means UNKNOWN, never zero. The unformatted base-units value is on `amountRaw`, which is null for exactly those rows.",
123417
123803
  inputs: []
123418
123804
  },
123419
123805
  async () => {
@@ -123445,7 +123831,7 @@ server.tool(
123445
123831
  fetchVirtualWalletsFromBackend(currentCredentials.apiKey, !refreshedFromBackend),
123446
123832
  fetchPoliciesFromBackend(currentCredentials.apiKey, true)
123447
123833
  ]);
123448
- const freshWallet = selectNewestApprovedWallet(freshWallets, currentCredentials.eoaAddress);
123834
+ const freshWallet = selectNewestApprovedWalletAmong(freshWallets, ownedSigningAddresses());
123449
123835
  const freshWalletIsActiveGrant = String(freshWallet?.id) === String(currentCredentials.virtualWalletId);
123450
123836
  if (freshWallet && freshWalletIsActiveGrant) {
123451
123837
  if (freshWallet.policyAssociated != null) {
@@ -123466,6 +123852,14 @@ server.tool(
123466
123852
  authorized: currentCredentials.authorizationStatus === "approved",
123467
123853
  status: currentCredentials.authorizationStatus || "none",
123468
123854
  eoaAddress: currentCredentials.eoaAddress || null,
123855
+ // WHICH KEY IS SIGNING. Since 0.3.6 every grant gets its OWN ephemeral
123856
+ // session key, so `signerAddress` is no longer a synonym for
123857
+ // `eoaAddress`: it is the address of the approved row this session
123858
+ // adopted, and `eoaAddress` is the install identity in `wallet.json`.
123859
+ // They agree only for a pre-rotation grant. Reporting both is what makes
123860
+ // "which key signed this" readable from a payload instead of from a
123861
+ // nonce-key autopsy — the same lesson as `activeVirtualWalletId` below.
123862
+ signerAddress: currentCredentials.signerAddress || currentCredentials.eoaAddress || null,
123469
123863
  walletAddress: currentCredentials.walletAddress || null,
123470
123864
  kernelClientActive: !!currentCredentials.virtualWalletKernelAccountClient,
123471
123865
  // WHICH GRANT THIS SESSION SIGNS WITH. The 2026-09-01 incident — three
@@ -123489,9 +123883,7 @@ server.tool(
123489
123883
  }
123490
123884
  }
123491
123885
  if (walletsCache && currentCredentials.eoaAddress) {
123492
- const wallet = selectNewestApprovedWallet(walletsCache, currentCredentials.eoaAddress) ?? walletsCache.find(
123493
- (w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
123494
- );
123886
+ const wallet = selectNewestApprovedWalletAmong(walletsCache, ownedSigningAddresses()) ?? walletsCache.find((w) => isOwnedSigningAddress(w.address));
123495
123887
  if (wallet) {
123496
123888
  const now = Math.floor(Date.now() / 1e3);
123497
123889
  const validUntil = wallet.policyValidUntil;
@@ -124015,7 +124407,7 @@ async function startLiveDashboard(entity, dir) {
124015
124407
  clearInterval(watcher);
124016
124408
  return;
124017
124409
  }
124018
- if (!existsSync12(dir)) {
124410
+ if (!existsSync13(dir)) {
124019
124411
  clearInterval(watcher);
124020
124412
  liveDashboards.delete(slug);
124021
124413
  try {
@@ -124962,10 +125354,13 @@ server.tool(
124962
125354
  }
124963
125355
  const approvedWallet = findCurrentApprovedWallet(
124964
125356
  wallets,
124965
- currentCredentials.eoaAddress,
125357
+ ownedSigningAddresses(),
124966
125358
  currentCredentials.pendingWalletId
124967
125359
  );
124968
125360
  if (approvedWallet && currentCredentials.authorizationStatus !== "approved") {
125361
+ if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
125362
+ currentCredentials.signerAddress = approvedWallet.address;
125363
+ }
124969
125364
  currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
124970
125365
  currentCredentials.virtualWalletId = String(approvedWallet.id);
124971
125366
  currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
@@ -125854,7 +126249,7 @@ async function reconcileApprovalStateOnConnect() {
125854
126249
  }
125855
126250
  try {
125856
126251
  const wallets = await fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true);
125857
- const approved = selectNewestApprovedWallet(wallets, currentCredentials.eoaAddress);
126252
+ const approved = selectNewestApprovedWalletAmong(wallets, ownedSigningAddresses());
125858
126253
  if (!approved) return;
125859
126254
  if (typeof approved.id === "number" && hasProcessedApprovalEventId(approved.id)) return;
125860
126255
  console.error(
@@ -125869,9 +126264,7 @@ async function reconcileApprovalStateOnConnect() {
125869
126264
  policyOnchainPermissions: void 0
125870
126265
  });
125871
126266
  if (walletsCache) {
125872
- const idx = walletsCache.findIndex(
125873
- (w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
125874
- );
126267
+ const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
125875
126268
  if (idx >= 0) walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
125876
126269
  }
125877
126270
  if (typeof approved.id === "number") rememberProcessedApprovalEventId(approved.id);
@@ -125904,8 +126297,8 @@ async function setupWebSocketListener() {
125904
126297
  return;
125905
126298
  }
125906
126299
  const statusMatch = event.status === "approved" || event.status === "ok";
125907
- const addressMatch = event.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase();
125908
- console.error(`\u{1F50D} [WebSocket] statusMatch=${statusMatch} addressMatch=${addressMatch} eoaAddress=${currentCredentials.eoaAddress}`);
126300
+ const addressMatch = isOwnedSigningAddress(event.address);
126301
+ console.error(`\u{1F50D} [WebSocket] statusMatch=${statusMatch} addressMatch=${addressMatch} eoaAddress=${currentCredentials.eoaAddress} signerAddress=${currentCredentials.signerAddress}`);
125909
126302
  if (statusMatch && addressMatch) {
125910
126303
  try {
125911
126304
  await onAuthorizationApproved({
@@ -125917,9 +126310,7 @@ async function setupWebSocketListener() {
125917
126310
  policyOnchainPermissions: void 0
125918
126311
  });
125919
126312
  if (walletsCache) {
125920
- const idx = walletsCache.findIndex(
125921
- (w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
125922
- );
126313
+ const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
125923
126314
  if (idx >= 0) {
125924
126315
  walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
125925
126316
  }
@@ -126192,15 +126583,15 @@ function killPreviousServeInstances(deps) {
126192
126583
 
126193
126584
  // src/commands/autosync-skills.ts
126194
126585
  init_esm_shims();
126195
- import { existsSync as existsSync14 } from "fs";
126586
+ import { existsSync as existsSync15 } from "fs";
126196
126587
  import { homedir as homedir12 } from "os";
126197
- import { join as join18 } from "path";
126588
+ import { join as join19 } from "path";
126198
126589
 
126199
126590
  // src/compounds/sync-skills.ts
126200
126591
  init_esm_shims();
126201
126592
  init_paths();
126202
- import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync4, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync10 } from "fs";
126203
- import { join as join17 } from "path";
126593
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync10 } from "fs";
126594
+ import { join as join18 } from "path";
126204
126595
  var MANAGED_MARKER = "<!-- ametyst-managed: sync-skills -->";
126205
126596
  var GITIGNORE_HEADER = "# ametyst-managed: sync-skills \u2014 pointer skills generated for this account; never commit them.";
126206
126597
  var GITIGNORE_HEADER_2 = "# Maintained by `ametyst serve` on every boot. Real skills without the managed marker are not listed.";
@@ -126244,22 +126635,22 @@ ${taskRunSection(slug, kind)}`;
126244
126635
  }
126245
126636
  function isManaged(file) {
126246
126637
  try {
126247
- return readFileSync13(file, "utf8").includes(MANAGED_MARKER);
126638
+ return readFileSync14(file, "utf8").includes(MANAGED_MARKER);
126248
126639
  } catch {
126249
126640
  return false;
126250
126641
  }
126251
126642
  }
126252
126643
  function listManagedDirs(root2) {
126253
- return readdirSync4(root2, { withFileTypes: true }).filter((e) => e.isDirectory() && isManaged(join17(root2, e.name, "SKILL.md"))).map((e) => e.name).sort();
126644
+ return readdirSync5(root2, { withFileTypes: true }).filter((e) => e.isDirectory() && isManaged(join18(root2, e.name, "SKILL.md"))).map((e) => e.name).sort();
126254
126645
  }
126255
126646
  function buildGitignoreContent(managedSlugs) {
126256
126647
  return [GITIGNORE_HEADER, GITIGNORE_HEADER_2, ".gitignore", ...managedSlugs.map((s) => `/${s}/`)].join("\n") + "\n";
126257
126648
  }
126258
126649
  function maintainGitignore(root2) {
126259
- const file = join17(root2, ".gitignore");
126650
+ const file = join18(root2, ".gitignore");
126260
126651
  const managed = listManagedDirs(root2);
126261
- if (existsSync13(file)) {
126262
- const current = readFileSync13(file, "utf8");
126652
+ if (existsSync14(file)) {
126653
+ const current = readFileSync14(file, "utf8");
126263
126654
  const firstLine = current.split(/\r?\n/, 1)[0];
126264
126655
  if (firstLine !== GITIGNORE_HEADER) {
126265
126656
  console.error(
@@ -126317,26 +126708,26 @@ async function syncSkills(opts = {}) {
126317
126708
  if (!desired.has(dir)) desired.set(dir, item);
126318
126709
  }
126319
126710
  const root2 = skillsRoot(target, global2);
126320
- mkdirSync8(root2, { recursive: true });
126711
+ mkdirSync9(root2, { recursive: true });
126321
126712
  let written = 0;
126322
126713
  const skipped = [];
126323
126714
  for (const [dir, item] of desired) {
126324
- const file = join17(dir, "SKILL.md");
126325
- if (existsSync13(file) && !isManaged(file)) {
126715
+ const file = join18(dir, "SKILL.md");
126716
+ if (existsSync14(file) && !isManaged(file)) {
126326
126717
  skipped.push(item.slug);
126327
126718
  console.warn(`\u26A0\uFE0F sync-skills: skipping "${item.slug}" \u2014 an unmanaged skill already exists at ${file}`);
126328
126719
  continue;
126329
126720
  }
126330
- mkdirSync8(dir, { recursive: true });
126721
+ mkdirSync9(dir, { recursive: true });
126331
126722
  writeFileSync10(file, buildStubContent(item));
126332
126723
  written++;
126333
126724
  }
126334
126725
  let pruned = 0;
126335
- for (const entry of readdirSync4(root2, { withFileTypes: true })) {
126726
+ for (const entry of readdirSync5(root2, { withFileTypes: true })) {
126336
126727
  if (!entry.isDirectory()) continue;
126337
- const dir = join17(root2, entry.name);
126728
+ const dir = join18(root2, entry.name);
126338
126729
  if (desired.has(dir)) continue;
126339
- if (!isManaged(join17(dir, "SKILL.md"))) continue;
126730
+ if (!isManaged(join18(dir, "SKILL.md"))) continue;
126340
126731
  rmSync(dir, { recursive: true, force: true });
126341
126732
  pruned++;
126342
126733
  }
@@ -126350,12 +126741,12 @@ function isSkillsAutosyncEnabled(env = process.env) {
126350
126741
  return !(raw === "0" || raw === "false" || raw === "off" || raw === "no");
126351
126742
  }
126352
126743
  function detectPresentTargets(deps = {}) {
126353
- const exists = deps.existsSync ?? existsSync14;
126744
+ const exists = deps.existsSync ?? existsSync15;
126354
126745
  const cwd = deps.cwd ?? (() => process.cwd());
126355
126746
  const home = deps.homedir ?? homedir12;
126356
126747
  const targets = [];
126357
- if (exists(join18(cwd(), ".claude")) || exists(join18(home(), ".claude"))) targets.push("claude");
126358
- if (exists(join18(cwd(), ".codex")) || exists(join18(home(), ".codex", "config.toml"))) targets.push("codex");
126748
+ if (exists(join19(cwd(), ".claude")) || exists(join19(home(), ".claude"))) targets.push("claude");
126749
+ if (exists(join19(cwd(), ".codex")) || exists(join19(home(), ".codex", "config.toml"))) targets.push("codex");
126359
126750
  return targets;
126360
126751
  }
126361
126752
  function resolveAutosyncTargets(clientName, deps = {}) {
@@ -127045,14 +127436,14 @@ init_esm_shims();
127045
127436
  // src/loops/materialize.ts
127046
127437
  init_esm_shims();
127047
127438
  init_paths();
127048
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "fs";
127049
- import { join as join19 } from "path";
127439
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
127440
+ import { join as join20 } from "path";
127050
127441
  var MIRRORED_DEFINITION_FILES = ["SKILL.md", "VISION.md", "README.md"];
127051
127442
  function materialize(loop2, fireId, stateDocs = []) {
127052
127443
  const dir = loopFireDir(loop2.slug, fireId);
127053
- mkdirSync9(dir, { recursive: true, mode: 448 });
127444
+ mkdirSync10(dir, { recursive: true, mode: 448 });
127054
127445
  for (const doc of stateDocs) {
127055
- writeFileSync11(join19(dir, doc.filename), doc.body, { mode: 384 });
127446
+ writeFileSync11(join20(dir, doc.filename), doc.body, { mode: 384 });
127056
127447
  }
127057
127448
  const files = {
127058
127449
  "SKILL.md": loop2.markdownBody ?? "",
@@ -127067,10 +127458,10 @@ function materialize(loop2, fireId, stateDocs = []) {
127067
127458
  files["dashboard.manifest.json"] = loop2.dashboardManifest;
127068
127459
  }
127069
127460
  for (const [name, body] of Object.entries(files)) {
127070
- writeFileSync11(join19(dir, name), body, { mode: 384 });
127461
+ writeFileSync11(join20(dir, name), body, { mode: 384 });
127071
127462
  }
127072
127463
  writeFileSync11(
127073
- join19(dir, "STATUS.md"),
127464
+ join20(dir, "STATUS.md"),
127074
127465
  `# STATUS \u2014 ${loop2.slug}
127075
127466
 
127076
127467
  loop_id: ${loop2.id}
@@ -127086,11 +127477,11 @@ queue: not started
127086
127477
  function mirrorDefinitionFiles(slug, files) {
127087
127478
  try {
127088
127479
  const root2 = loopDir(slug);
127089
- mkdirSync9(root2, { recursive: true, mode: 448 });
127480
+ mkdirSync10(root2, { recursive: true, mode: 448 });
127090
127481
  for (const name of [...MIRRORED_DEFINITION_FILES, "dashboard.html", "dashboard.manifest.json"]) {
127091
127482
  const body = files[name];
127092
127483
  if (body === void 0) continue;
127093
- writeFileSync11(join19(root2, name), body, { mode: 384 });
127484
+ writeFileSync11(join20(root2, name), body, { mode: 384 });
127094
127485
  }
127095
127486
  } catch {
127096
127487
  }
@@ -127216,15 +127607,15 @@ function resolveMaxBudgetUsd(opts, env = process.env) {
127216
127607
  // src/loops/heartbeat.ts
127217
127608
  init_esm_shims();
127218
127609
  import * as realFs2 from "fs";
127219
- import { join as join20 } from "path";
127610
+ import { join as join21 } from "path";
127220
127611
  function startHeartbeat(loopDir2, info, deps = {}) {
127221
127612
  const fs = deps.fs ?? realFs2;
127222
127613
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
127223
127614
  const setI = deps.setInterval ?? globalThis.setInterval;
127224
127615
  const clearI = deps.clearInterval ?? globalThis.clearInterval;
127225
127616
  const intervalMs = deps.intervalMs ?? 6e4;
127226
- const stateDir = join20(loopDir2, ".state");
127227
- const path2 = join20(stateDir, "fire.running");
127617
+ const stateDir = join21(loopDir2, ".state");
127618
+ const path2 = join21(stateDir, "fire.running");
127228
127619
  try {
127229
127620
  fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
127230
127621
  fs.writeFileSync(
@@ -127264,10 +127655,10 @@ ${info.sessionId ? `session=${info.sessionId}
127264
127655
  init_esm_shims();
127265
127656
  import * as realFs3 from "fs";
127266
127657
  import { homedir as homedir13 } from "os";
127267
- import { join as join21 } from "path";
127658
+ import { join as join22 } from "path";
127268
127659
  function transcriptPathFor(cwd, sessionId2, home = homedir13()) {
127269
127660
  const slug = cwd.replace(/\//g, "-");
127270
- return join21(home, ".claude", "projects", slug, `${sessionId2}.jsonl`);
127661
+ return join22(home, ".claude", "projects", slug, `${sessionId2}.jsonl`);
127271
127662
  }
127272
127663
  function parseTranscriptStats(jsonl) {
127273
127664
  const tokens = {
@@ -127338,9 +127729,9 @@ function recordFireAccounting(args) {
127338
127729
  }
127339
127730
  }
127340
127731
  function appendFireLine(fs, loopDir2, rec) {
127341
- const stateDir = join21(loopDir2, ".state");
127732
+ const stateDir = join22(loopDir2, ".state");
127342
127733
  fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
127343
- fs.appendFileSync(join21(stateDir, "fires.jsonl"), JSON.stringify(rec) + "\n", { mode: 384 });
127734
+ fs.appendFileSync(join22(stateDir, "fires.jsonl"), JSON.stringify(rec) + "\n", { mode: 384 });
127344
127735
  }
127345
127736
  function recordFailedLaunch(args) {
127346
127737
  const fs = args.deps?.fs ?? realFs3;
@@ -127369,8 +127760,8 @@ function recordFailedLaunch(args) {
127369
127760
  init_esm_shims();
127370
127761
  import { spawn as spawn2 } from "child_process";
127371
127762
  import { randomUUID as randomUUID5 } from "crypto";
127372
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync14, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
127373
- import { dirname as dirname8, join as join23 } from "path";
127763
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
127764
+ import { dirname as dirname8, join as join24 } from "path";
127374
127765
 
127375
127766
  // src/loops/claude-binary.ts
127376
127767
  init_esm_shims();
@@ -127422,7 +127813,7 @@ function ensureClaudeBinary(deps = {}) {
127422
127813
  // src/loops/concurrency.ts
127423
127814
  init_esm_shims();
127424
127815
  import * as realFs4 from "fs";
127425
- import { join as join22 } from "path";
127816
+ import { join as join23 } from "path";
127426
127817
  var DEFAULT_MAX_CONCURRENT_FIRES = 6;
127427
127818
  var DEFAULT_HEARTBEAT_STALE_MS = 10 * 6e4;
127428
127819
  function pidIsAlive(pid) {
@@ -127446,14 +127837,14 @@ function liveFires(loopRoot, deps = {}) {
127446
127837
  const staleMs = deps.staleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
127447
127838
  let entries;
127448
127839
  try {
127449
- entries = fs.readdirSync(join22(loopRoot, "fires"));
127840
+ entries = fs.readdirSync(join23(loopRoot, "fires"));
127450
127841
  } catch {
127451
127842
  return [];
127452
127843
  }
127453
127844
  const out = [];
127454
127845
  for (const entry of entries) {
127455
127846
  try {
127456
- const beat = join22(loopRoot, "fires", String(entry), ".state", "fire.running");
127847
+ const beat = join23(loopRoot, "fires", String(entry), ".state", "fire.running");
127457
127848
  const st = fs.statSync(beat);
127458
127849
  const heartbeatAgeMs = now() - Number(st.mtimeMs);
127459
127850
  if (!(heartbeatAgeMs <= staleMs)) continue;
@@ -127481,7 +127872,7 @@ var SHIPBACK_SIGNAL_TIMEOUT_MS = 8e3;
127481
127872
  function preserveRefusedConstraints(slug, fireId, body) {
127482
127873
  try {
127483
127874
  const path2 = refusedConstraintsPath(slug, fireId);
127484
- mkdirSync10(dirname8(path2), { recursive: true, mode: 448 });
127875
+ mkdirSync11(dirname8(path2), { recursive: true, mode: 448 });
127485
127876
  writeFileSync12(path2, body, { mode: 384 });
127486
127877
  return { path: path2 };
127487
127878
  } catch (err) {
@@ -127560,8 +127951,8 @@ async function runLoop(loopId, opts = {}) {
127560
127951
  console.log(
127561
127952
  `Loop ${loop2.slug}: ${est.steps} steps (${est.paidSteps} paid), est \u20AC${est.estCostEur ?? "?"} \u2014 ${capLabel}`
127562
127953
  );
127563
- const statusPath = join23(dir, "STATUS.md");
127564
- const statusBefore = existsSync15(statusPath) ? readFileSync14(statusPath, "utf-8") : "";
127954
+ const statusPath = join24(dir, "STATUS.md");
127955
+ const statusBefore = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
127565
127956
  writeFileSync12(
127566
127957
  statusPath,
127567
127958
  statusBefore.replace(
@@ -127620,8 +128011,8 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
127620
128011
  }
127621
128012
  async function shipConstraints() {
127622
128013
  try {
127623
- const constraintsPath = join23(dir, "CONSTRAINTS.md");
127624
- const materializedConstraints = existsSync15(constraintsPath) ? readFileSync14(constraintsPath, "utf-8") : void 0;
128014
+ const constraintsPath = join24(dir, "CONSTRAINTS.md");
128015
+ const materializedConstraints = existsSync16(constraintsPath) ? readFileSync15(constraintsPath, "utf-8") : void 0;
127625
128016
  if (materializedConstraints === void 0) return;
127626
128017
  const boot = loop2.constraintsMd ?? "";
127627
128018
  for (let attempt = 1; attempt <= 2; attempt++) {
@@ -127696,7 +128087,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
127696
128087
  void Promise.race([shipBackConstraints().then(() => "shipped"), deadline]).then((outcome) => {
127697
128088
  if (outcome === "timeout") {
127698
128089
  console.error(
127699
- `Loop ${loop2.slug}: the ship-back did not finish within ${SHIPBACK_SIGNAL_TIMEOUT_MS}ms of the signal \u2014 exiting anyway so the shutdown is not held open. Nothing was discarded: this fire's rules remain at ${join23(dir, "CONSTRAINTS.md")}, and any state doc that did not get shipped remains beside it in ${dir}. The next fire can recover them by hand from there \u2014 nothing in the CLI reads a prior fire's folder automatically. \u26D4 Constraints are shipped FIRST, so the state docs are the likelier victim of this deadline \u2014 though a constraints ship-back that alone exceeds it truncates the rules too.`
128090
+ `Loop ${loop2.slug}: the ship-back did not finish within ${SHIPBACK_SIGNAL_TIMEOUT_MS}ms of the signal \u2014 exiting anyway so the shutdown is not held open. Nothing was discarded: this fire's rules remain at ${join24(dir, "CONSTRAINTS.md")}, and any state doc that did not get shipped remains beside it in ${dir}. The next fire can recover them by hand from there \u2014 nothing in the CLI reads a prior fire's folder automatically. \u26D4 Constraints are shipped FIRST, so the state docs are the likelier victim of this deadline \u2014 though a constraints ship-back that alone exceeds it truncates the rules too.`
127700
128091
  );
127701
128092
  }
127702
128093
  }).finally(() => process.exit(130));
@@ -127732,7 +128123,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
127732
128123
  startedAtEpochMs
127733
128124
  });
127734
128125
  }
127735
- const statusAfter = existsSync15(statusPath) ? readFileSync14(statusPath, "utf-8") : "";
128126
+ const statusAfter = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
127736
128127
  const clean2 = exitCode === 0 && /(vision\s*done|queue\s*drained|status:\s*done)/i.test(statusAfter) && !/(brake|crash|crashed|errored)/i.test(statusAfter);
127737
128128
  await shipBackConstraints();
127738
128129
  process.off("SIGINT", onSignal);
@@ -127770,9 +128161,9 @@ async function showLoop(loopId) {
127770
128161
  // src/loops/schedule.ts
127771
128162
  init_esm_shims();
127772
128163
  init_paths();
127773
- import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync11, rmSync as rmSync3, existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync15, accessSync as accessSync2, constants as constants2 } from "fs";
128164
+ import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync12, rmSync as rmSync3, existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync16, accessSync as accessSync2, constants as constants2 } from "fs";
127774
128165
  import { execFileSync as execFileSync4 } from "child_process";
127775
- import { join as join24, dirname as dirname9 } from "path";
128166
+ import { join as join25, dirname as dirname9 } from "path";
127776
128167
  import { homedir as homedir14 } from "os";
127777
128168
  var LOOP_KIND = {
127778
128169
  labelPrefix: "xyz.ametyst.loop.",
@@ -127783,7 +128174,7 @@ var LOOP_KIND = {
127783
128174
  return args;
127784
128175
  },
127785
128176
  stateDir(slug, cwd) {
127786
- return join24(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
128177
+ return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
127787
128178
  }
127788
128179
  };
127789
128180
  var TASK_KIND = {
@@ -127795,7 +128186,7 @@ var TASK_KIND = {
127795
128186
  return args;
127796
128187
  },
127797
128188
  stateDir(slug, cwd) {
127798
- return join24(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
128189
+ return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
127799
128190
  }
127800
128191
  };
127801
128192
  var COMPOUND_KIND = {
@@ -127807,7 +128198,7 @@ var COMPOUND_KIND = {
127807
128198
  return args;
127808
128199
  },
127809
128200
  stateDir(slug, cwd) {
127810
- return join24(cwd, `.ametyst${ENV_SUFFIX}`, "compounds", safeSlug2(slug, this.noun), ".state");
128201
+ return join25(cwd, `.ametyst${ENV_SUFFIX}`, "compounds", safeSlug2(slug, this.noun), ".state");
127811
128202
  }
127812
128203
  };
127813
128204
  function safeSlug2(slug, noun) {
@@ -127834,7 +128225,7 @@ function parseAt(at) {
127834
128225
  return { hour, minute };
127835
128226
  }
127836
128227
  function plistPath(kind, home, slug) {
127837
- return join24(home, "Library", "LaunchAgents", `${label(kind, slug)}.plist`);
128228
+ return join25(home, "Library", "LaunchAgents", `${label(kind, slug)}.plist`);
127838
128229
  }
127839
128230
  function launchctl(args) {
127840
128231
  try {
@@ -127850,7 +128241,7 @@ function isLoaded(lbl) {
127850
128241
  }
127851
128242
  function readInteractiveDefaultModel(home) {
127852
128243
  try {
127853
- const raw = readFileSync15(join24(home, ".claude", "settings.json"), "utf-8");
128244
+ const raw = readFileSync16(join25(home, ".claude", "settings.json"), "utf-8");
127854
128245
  const model = JSON.parse(String(raw)).model;
127855
128246
  return typeof model === "string" && model.trim() ? model.trim() : void 0;
127856
128247
  } catch {
@@ -127952,9 +128343,9 @@ ${progArgs}
127952
128343
  ${envEntries}
127953
128344
  </dict>
127954
128345
  <key>StandardOutPath</key>
127955
- <string>${escapeXml(join24(stateDir, LAUNCHD_OUT))}</string>
128346
+ <string>${escapeXml(join25(stateDir, LAUNCHD_OUT))}</string>
127956
128347
  <key>StandardErrorPath</key>
127957
- <string>${escapeXml(join24(stateDir, LAUNCHD_ERR))}</string>
128348
+ <string>${escapeXml(join25(stateDir, LAUNCHD_ERR))}</string>
127958
128349
  <key>AbandonProcessGroup</key>
127959
128350
  <true/>
127960
128351
  ${scheduleBlock}
@@ -128020,8 +128411,8 @@ function schedule(kind, slug, opts = {}) {
128020
128411
  const path2 = plistPath(kind, home, slug);
128021
128412
  const stateDir2 = kind.stateDir(slug, cwd);
128022
128413
  assertWritable(cwd, "working directory");
128023
- mkdirSync11(dirname9(path2), { recursive: true });
128024
- mkdirSync11(stateDir2, { recursive: true });
128414
+ mkdirSync12(dirname9(path2), { recursive: true });
128415
+ mkdirSync12(stateDir2, { recursive: true });
128025
128416
  assertWritable(stateDir2, "log directory");
128026
128417
  writeFileSync13(path2, plistXml(lbl, args, scheduleBlock, cwd, jobEnv, stateDir2), { mode: 384 });
128027
128418
  if (isLoaded(lbl)) {
@@ -128044,7 +128435,7 @@ launchctl said: ${loaded.output.trim()}` : "")
128044
128435
  }
128045
128436
  const stateDir = kind.stateDir(slug, cwd);
128046
128437
  assertWritable(cwd, "working directory");
128047
- mkdirSync11(stateDir, { recursive: true });
128438
+ mkdirSync12(stateDir, { recursive: true });
128048
128439
  assertWritable(stateDir, "log directory");
128049
128440
  const cronEnv = { PATH: envPath };
128050
128441
  if (model) cronEnv.ANTHROPIC_MODEL = model;
@@ -128077,9 +128468,9 @@ function list(kind, opts = {}) {
128077
128468
  const home = opts.home ?? homedir14();
128078
128469
  const prefix = kind.labelPrefix;
128079
128470
  if (platform === "darwin") {
128080
- const dir = join24(home, "Library", "LaunchAgents");
128081
- if (!existsSync16(dir)) return [];
128082
- return readdirSync5(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => f.slice(prefix.length, -".plist".length));
128471
+ const dir = join25(home, "Library", "LaunchAgents");
128472
+ if (!existsSync17(dir)) return [];
128473
+ return readdirSync6(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => f.slice(prefix.length, -".plist".length));
128083
128474
  }
128084
128475
  return readCrontab().split("\n").filter((l) => l.includes(`# ${prefix}`)).map((l) => l.trimEnd().slice(l.trimEnd().lastIndexOf(prefix) + prefix.length));
128085
128476
  }
@@ -128139,13 +128530,13 @@ function listEntries(kind, opts = {}) {
128139
128530
  const home = opts.home ?? homedir14();
128140
128531
  const prefix = kind.labelPrefix;
128141
128532
  if (platform === "darwin") {
128142
- const dir = join24(home, "Library", "LaunchAgents");
128143
- if (!existsSync16(dir)) return [];
128144
- return readdirSync5(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => {
128533
+ const dir = join25(home, "Library", "LaunchAgents");
128534
+ if (!existsSync17(dir)) return [];
128535
+ return readdirSync6(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => {
128145
128536
  const slug = f.slice(prefix.length, -".plist".length);
128146
128537
  let envKeys = [];
128147
128538
  try {
128148
- envKeys = plistEnvKeys(String(readFileSync15(join24(dir, f), "utf-8")));
128539
+ envKeys = plistEnvKeys(String(readFileSync16(join25(dir, f), "utf-8")));
128149
128540
  } catch {
128150
128541
  envKeys = [];
128151
128542
  }