@ametyst/cli 0.3.4 → 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.
- package/dist/index.js +671 -220
- 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
|
-
|
|
111877
|
-
|
|
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,
|
|
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
|
|
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 (!
|
|
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.
|
|
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
|
|
112206
|
-
import { join as
|
|
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 (!
|
|
112463
|
-
raw = JSON.parse(
|
|
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 (!
|
|
112500
|
-
const raw = JSON.parse(
|
|
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 (!
|
|
112515
|
-
const raw = JSON.parse(
|
|
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 =
|
|
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
|
|
113505
|
-
import { dirname as dirname7, join as
|
|
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
|
|
113722
|
+
return join17(AMETYST_DIR, CONNECTIONS_DB_FILENAME);
|
|
113508
113723
|
}
|
|
113509
113724
|
function ensureOwnerOnlyFile(path2) {
|
|
113510
113725
|
if (path2 === IN_MEMORY_DB) return;
|
|
113511
|
-
|
|
113512
|
-
if (!
|
|
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
|
|
114930
|
-
|
|
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
|
|
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:
|
|
115218
|
-
|
|
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
|
|
@@ -115417,6 +115690,54 @@ var McpEventLogger = class {
|
|
|
115417
115690
|
}
|
|
115418
115691
|
};
|
|
115419
115692
|
|
|
115693
|
+
// src/mcp-server/newest-grant.ts
|
|
115694
|
+
init_esm_shims();
|
|
115695
|
+
function nonEmptyLowerCase(value2) {
|
|
115696
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2.toLowerCase() : null;
|
|
115697
|
+
}
|
|
115698
|
+
function isNewerGrant(candidate, incumbent) {
|
|
115699
|
+
const candidateId = Number(candidate?.id);
|
|
115700
|
+
const incumbentId = Number(incumbent?.id);
|
|
115701
|
+
const candidateIdUsable = Number.isFinite(candidateId);
|
|
115702
|
+
const incumbentIdUsable = Number.isFinite(incumbentId);
|
|
115703
|
+
if (candidateIdUsable && incumbentIdUsable && candidateId !== incumbentId) {
|
|
115704
|
+
return candidateId > incumbentId;
|
|
115705
|
+
}
|
|
115706
|
+
const candidateAt = Date.parse(typeof candidate?.createdAt === "string" ? candidate.createdAt : "");
|
|
115707
|
+
const incumbentAt = Date.parse(typeof incumbent?.createdAt === "string" ? incumbent.createdAt : "");
|
|
115708
|
+
if (Number.isFinite(candidateAt) && Number.isFinite(incumbentAt) && candidateAt !== incumbentAt) {
|
|
115709
|
+
return candidateAt > incumbentAt;
|
|
115710
|
+
}
|
|
115711
|
+
return candidateIdUsable && !incumbentIdUsable;
|
|
115712
|
+
}
|
|
115713
|
+
function selectNewestApprovedWalletAmong(wallets, addresses) {
|
|
115714
|
+
if (!Array.isArray(wallets)) 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;
|
|
115721
|
+
let newest;
|
|
115722
|
+
for (const wallet of wallets) {
|
|
115723
|
+
if (wallet?.status !== "approved") continue;
|
|
115724
|
+
const rowAddress = nonEmptyLowerCase(wallet?.address);
|
|
115725
|
+
if (!rowAddress || !candidates.has(rowAddress)) continue;
|
|
115726
|
+
if (newest === void 0 || isNewerGrant(wallet, newest)) newest = wallet;
|
|
115727
|
+
}
|
|
115728
|
+
return newest;
|
|
115729
|
+
}
|
|
115730
|
+
function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
|
|
115731
|
+
if (!Array.isArray(wallets)) return void 0;
|
|
115732
|
+
if (pendingWalletId) {
|
|
115733
|
+
return wallets.find((w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved");
|
|
115734
|
+
}
|
|
115735
|
+
return selectNewestApprovedWalletAmong(
|
|
115736
|
+
wallets,
|
|
115737
|
+
Array.isArray(eoaAddress) ? eoaAddress : eoaAddress === void 0 ? [] : [eoaAddress]
|
|
115738
|
+
);
|
|
115739
|
+
}
|
|
115740
|
+
|
|
115420
115741
|
// src/mcp-server/start-session.ts
|
|
115421
115742
|
init_esm_shims();
|
|
115422
115743
|
init_dist();
|
|
@@ -115551,43 +115872,69 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115551
115872
|
let virtualWalletId = ctx.virtualWalletId;
|
|
115552
115873
|
let paymentManagerAddress = ctx.paymentManagerAddress;
|
|
115553
115874
|
let policyId = ctx.policyId;
|
|
115554
|
-
|
|
115555
|
-
|
|
115556
|
-
|
|
115557
|
-
|
|
115558
|
-
|
|
115559
|
-
|
|
115560
|
-
|
|
115561
|
-
|
|
115562
|
-
|
|
115563
|
-
|
|
115564
|
-
|
|
115565
|
-
|
|
115566
|
-
|
|
115567
|
-
|
|
115568
|
-
|
|
115569
|
-
|
|
115570
|
-
|
|
115571
|
-
|
|
115572
|
-
|
|
115573
|
-
|
|
115875
|
+
let activeWallet;
|
|
115876
|
+
let grantChanged = false;
|
|
115877
|
+
try {
|
|
115878
|
+
activeWallet = selectNewestApprovedWalletAmong(wallets, [
|
|
115879
|
+
ctx.eoaAddress,
|
|
115880
|
+
...ctx.candidateAddresses ?? []
|
|
115881
|
+
]);
|
|
115882
|
+
if (activeWallet) {
|
|
115883
|
+
const newestId = String(activeWallet.id);
|
|
115884
|
+
const rowWalletAddress = activeWallet.walletAddress || activeWallet.kernelAccountAddress;
|
|
115885
|
+
const rowPaymentManagerAddress = activeWallet.paymentManagerAddress;
|
|
115886
|
+
const rowPolicyId = activeWallet.policyAssociated != null ? String(activeWallet.policyAssociated) : void 0;
|
|
115887
|
+
grantChanged = !!virtualWalletId && virtualWalletId !== newestId;
|
|
115888
|
+
if (grantChanged) {
|
|
115889
|
+
console.error(
|
|
115890
|
+
`[start_session] The active grant moved on \u2014 virtual wallet ${virtualWalletId} -> ${newestId}. Adopting the newest approved grant and dropping the session state derived from the old one.`
|
|
115891
|
+
);
|
|
115892
|
+
walletAddress = rowWalletAddress;
|
|
115893
|
+
paymentManagerAddress = rowPaymentManagerAddress;
|
|
115894
|
+
policyId = rowPolicyId;
|
|
115895
|
+
} else {
|
|
115896
|
+
walletAddress = rowWalletAddress || walletAddress;
|
|
115897
|
+
paymentManagerAddress = rowPaymentManagerAddress || paymentManagerAddress;
|
|
115898
|
+
policyId = rowPolicyId ?? policyId;
|
|
115899
|
+
}
|
|
115900
|
+
virtualWalletId = newestId;
|
|
115901
|
+
if (typeof activeWallet.address === "string" && activeWallet.address.trim() !== "") {
|
|
115902
|
+
credentials.signerAddress = activeWallet.address;
|
|
115903
|
+
}
|
|
115904
|
+
credentials.walletAddress = walletAddress;
|
|
115905
|
+
credentials.virtualWalletId = virtualWalletId;
|
|
115906
|
+
credentials.paymentManagerAddress = paymentManagerAddress;
|
|
115907
|
+
credentials.policyId = policyId;
|
|
115908
|
+
credentials.authorizationStatus = "approved";
|
|
115909
|
+
if (activeWallet.policyName != null) {
|
|
115910
|
+
credentials.policyName = activeWallet.policyName;
|
|
115911
|
+
}
|
|
115912
|
+
if (activeWallet.policyValidUntil != null) {
|
|
115913
|
+
credentials.policyValidUntil = BigInt(activeWallet.policyValidUntil);
|
|
115914
|
+
}
|
|
115915
|
+
} else if (walletsFetchFailed && virtualWalletId) {
|
|
115574
115916
|
console.warn(
|
|
115575
|
-
|
|
115576
|
-
backendError instanceof Error ? backendError.message : String(backendError)
|
|
115917
|
+
`[start_session] \u26A0\uFE0F could not verify the active grant \u2014 signing with the cached one (virtual wallet ${virtualWalletId}, policy ${policyId ?? "unknown"}). If a newer grant was approved since, spends are metered against the older one and may hit its spending limit.`
|
|
115577
115918
|
);
|
|
115578
115919
|
}
|
|
115920
|
+
} catch (backendError) {
|
|
115921
|
+
console.warn(
|
|
115922
|
+
"[start_session] Could not check backend:",
|
|
115923
|
+
backendError instanceof Error ? backendError.message : String(backendError)
|
|
115924
|
+
);
|
|
115579
115925
|
}
|
|
115580
115926
|
const mergedAuth = credentials.authorizationStatus !== void 0 ? credentials.authorizationStatus : ctx.authorizationStatus;
|
|
115581
115927
|
console.error(
|
|
115582
115928
|
`[start_session] State check \u2014 walletAddress: ${walletAddress}, virtualWalletId: ${virtualWalletId}, paymentManagerAddress: ${paymentManagerAddress}, policyId: ${policyId}`
|
|
115583
115929
|
);
|
|
115584
115930
|
if (mergedAuth === "expired") {
|
|
115585
|
-
return { responseKey: "unlocked_expired", credentials, timingReport: timing.report() };
|
|
115931
|
+
return { responseKey: "unlocked_expired", credentials, timingReport: timing.report(), grantChanged };
|
|
115586
115932
|
}
|
|
115587
115933
|
if (walletAddress && virtualWalletId && paymentManagerAddress && policyId) {
|
|
115588
115934
|
let privateKey = null;
|
|
115589
115935
|
try {
|
|
115590
|
-
|
|
115936
|
+
const signerAddress = credentials.signerAddress ?? ctx.eoaAddress;
|
|
115937
|
+
privateKey = ctx.resolveSigningKey && signerAddress ? ctx.resolveSigningKey(signerAddress) : readVaultPrivateKey(ctx.walletKeystoreJson, ctx.passphrase);
|
|
115591
115938
|
const { virtualWalletsManagers: virtualWalletsManagers2, financialAccounts: financialAccounts2 } = ctx.sdk;
|
|
115592
115939
|
const walletData = await virtualWalletsManagers2.getVirtualWalletDataByApiKey(
|
|
115593
115940
|
ctx.apiKey,
|
|
@@ -115604,12 +115951,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115604
115951
|
}
|
|
115605
115952
|
credentials.walletAddress = walletAddress;
|
|
115606
115953
|
credentials.paymentManagerAddress = paymentManagerAddress;
|
|
115607
|
-
const
|
|
115608
|
-
|
|
115609
|
-
|
|
115610
|
-
const preloadedWalletData = approvedWallet != null && approvedWallet.policyValidAfter != null && approvedWallet.policyValidUntil != null ? {
|
|
115611
|
-
policyValidAfter: Number(approvedWallet.policyValidAfter),
|
|
115612
|
-
policyValidUntil: Number(approvedWallet.policyValidUntil)
|
|
115954
|
+
const preloadedWalletData = activeWallet != null && activeWallet.policyValidAfter != null && activeWallet.policyValidUntil != null ? {
|
|
115955
|
+
policyValidAfter: Number(activeWallet.policyValidAfter),
|
|
115956
|
+
policyValidUntil: Number(activeWallet.policyValidUntil)
|
|
115613
115957
|
} : void 0;
|
|
115614
115958
|
if (ctx.readPolicyActiveOnchain) {
|
|
115615
115959
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
@@ -115631,7 +115975,7 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115631
115975
|
maxAttempts
|
|
115632
115976
|
});
|
|
115633
115977
|
if (bootAction === "reauthorize") {
|
|
115634
|
-
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report() };
|
|
115978
|
+
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report(), grantChanged };
|
|
115635
115979
|
}
|
|
115636
115980
|
}
|
|
115637
115981
|
const { kernelClient: virtualWalletKernelAccountClient, permissionPlugin: virtualWalletPermissionPlugin, kernelDomain } = await virtualWalletsManagers2.configureVirtualWalletKernelAccount(
|
|
@@ -115661,13 +116005,14 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115661
116005
|
console.warn("[start_session] \u26A0\uFE0F Could not fetch credit token address (Path B routing disabled):", e);
|
|
115662
116006
|
}
|
|
115663
116007
|
}
|
|
115664
|
-
return { responseKey: "unlocked_and_authorized", credentials, timingReport: timing.report() };
|
|
116008
|
+
return { responseKey: "unlocked_and_authorized", credentials, timingReport: timing.report(), grantChanged };
|
|
115665
116009
|
} catch (error) {
|
|
115666
116010
|
console.error("\u274C [start_session] Kernel client creation failed:", error);
|
|
115667
116011
|
return {
|
|
115668
116012
|
responseKey: "unlocked_not_configured",
|
|
115669
116013
|
credentials,
|
|
115670
116014
|
timingReport: timing.report(),
|
|
116015
|
+
grantChanged,
|
|
115671
116016
|
kernelErrorMessage: error instanceof Error ? error.message : String(error)
|
|
115672
116017
|
};
|
|
115673
116018
|
} finally {
|
|
@@ -115675,9 +116020,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115675
116020
|
}
|
|
115676
116021
|
}
|
|
115677
116022
|
if (!walletsFetchFailed && wallets.length === 0) {
|
|
115678
|
-
return { responseKey: "unlocked_provisioning_pending", credentials, timingReport: timing.report() };
|
|
116023
|
+
return { responseKey: "unlocked_provisioning_pending", credentials, timingReport: timing.report(), grantChanged };
|
|
115679
116024
|
}
|
|
115680
|
-
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report() };
|
|
116025
|
+
return { responseKey: "unlocked_pending_authorization", credentials, timingReport: timing.report(), grantChanged };
|
|
115681
116026
|
}
|
|
115682
116027
|
var WALLET_SCOPED_CREDENTIAL_KEYS = [
|
|
115683
116028
|
"virtualWalletId",
|
|
@@ -115707,9 +116052,9 @@ function clearWalletScopedCredentials(target) {
|
|
|
115707
116052
|
function sameAddress(a, b) {
|
|
115708
116053
|
return String(a ?? "").toLowerCase() === String(b ?? "").toLowerCase();
|
|
115709
116054
|
}
|
|
115710
|
-
function mergeStartSessionCredentials(target, patch, responseKey) {
|
|
116055
|
+
function mergeStartSessionCredentials(target, patch, responseKey, options = {}) {
|
|
115711
116056
|
const walletChanged = patch.eoaAddress !== void 0 && !sameAddress(patch.eoaAddress, target.eoaAddress);
|
|
115712
|
-
if (walletChanged) {
|
|
116057
|
+
if (walletChanged || options.grantChanged) {
|
|
115713
116058
|
clearWalletScopedCredentials(target);
|
|
115714
116059
|
}
|
|
115715
116060
|
if (patch.walletKeystoreJson !== void 0) {
|
|
@@ -116889,13 +117234,13 @@ init_local_state();
|
|
|
116889
117234
|
// src/mcp-server/materialize-task.ts
|
|
116890
117235
|
init_esm_shims();
|
|
116891
117236
|
init_paths();
|
|
116892
|
-
import { mkdirSync as
|
|
116893
|
-
import { join as
|
|
117237
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
117238
|
+
import { join as join9 } from "path";
|
|
116894
117239
|
|
|
116895
117240
|
// src/loops/state-docs.ts
|
|
116896
117241
|
init_esm_shims();
|
|
116897
|
-
import { existsSync as
|
|
116898
|
-
import { join as
|
|
117242
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
117243
|
+
import { join as join8 } from "path";
|
|
116899
117244
|
|
|
116900
117245
|
// src/loops/shipback.ts
|
|
116901
117246
|
init_esm_shims();
|
|
@@ -117328,12 +117673,12 @@ function docMergeRefusal(boot, fresh, next, cleanAppend) {
|
|
|
117328
117673
|
async function shipBackStateDocs(sdk, apiKey, slug, dir, boot) {
|
|
117329
117674
|
const outcomes = [];
|
|
117330
117675
|
for (const doc of boot) {
|
|
117331
|
-
const path2 =
|
|
117332
|
-
if (!
|
|
117676
|
+
const path2 = join8(dir, doc.filename);
|
|
117677
|
+
if (!existsSync10(path2)) {
|
|
117333
117678
|
outcomes.push({ key: doc.key, outcome: "unchanged" });
|
|
117334
117679
|
continue;
|
|
117335
117680
|
}
|
|
117336
|
-
const materialized =
|
|
117681
|
+
const materialized = readFileSync9(path2, "utf-8");
|
|
117337
117682
|
const reread = await readScoped(sdk, apiKey, slug, doc.key, doc.scope);
|
|
117338
117683
|
const fresh = reread.status === "ok" ? reread.body : reread.status === "absent" && doc.body === "" ? "" : void 0;
|
|
117339
117684
|
if (fresh === void 0) {
|
|
@@ -117403,20 +117748,20 @@ var TASK_DEFINITION_FILES = [
|
|
|
117403
117748
|
];
|
|
117404
117749
|
function materializeTask(task, runId) {
|
|
117405
117750
|
const dir = loopFireDir(task.slug, runId);
|
|
117406
|
-
|
|
117751
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
117407
117752
|
const files = {};
|
|
117408
117753
|
const skipped = [];
|
|
117409
117754
|
for (const [label2, filename, field] of TASK_DEFINITION_FILES) {
|
|
117410
117755
|
const body = task[field];
|
|
117411
117756
|
if (typeof body === "string" && body.length > 0) {
|
|
117412
|
-
writeFileSync8(
|
|
117757
|
+
writeFileSync8(join9(dir, filename), body, { mode: 384 });
|
|
117413
117758
|
files[label2] = filename;
|
|
117414
117759
|
} else {
|
|
117415
117760
|
skipped.push(label2);
|
|
117416
117761
|
}
|
|
117417
117762
|
}
|
|
117418
117763
|
writeFileSync8(
|
|
117419
|
-
|
|
117764
|
+
join9(dir, "STATUS.md"),
|
|
117420
117765
|
`# STATUS \u2014 ${task.slug}
|
|
117421
117766
|
|
|
117422
117767
|
task_id: ${task.id}
|
|
@@ -117447,7 +117792,7 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
|
|
|
117447
117792
|
notes.push(`memory doc '${f.key}' not materialized: ${f.reason}`);
|
|
117448
117793
|
}
|
|
117449
117794
|
for (const doc of fetched) {
|
|
117450
|
-
writeFileSync8(
|
|
117795
|
+
writeFileSync8(join9(dir, doc.filename), doc.body, { mode: 384 });
|
|
117451
117796
|
files[`doc:${doc.key}`] = doc.filename;
|
|
117452
117797
|
notes.push(describeSource(doc));
|
|
117453
117798
|
}
|
|
@@ -117461,14 +117806,14 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
|
|
|
117461
117806
|
}
|
|
117462
117807
|
|
|
117463
117808
|
// src/mcp-server/index.ts
|
|
117464
|
-
import { existsSync as
|
|
117809
|
+
import { existsSync as existsSync13 } from "fs";
|
|
117465
117810
|
|
|
117466
117811
|
// src/loops/dashboard.ts
|
|
117467
117812
|
init_esm_shims();
|
|
117468
117813
|
import { createServer as createServer2 } from "http";
|
|
117469
117814
|
import * as realFs from "fs";
|
|
117470
117815
|
import { spawn as realSpawn } from "child_process";
|
|
117471
|
-
import { join as
|
|
117816
|
+
import { join as join10 } from "path";
|
|
117472
117817
|
var DEFAULT_PORT = 4477;
|
|
117473
117818
|
var DASHBOARD_PORT_ENV = "AMETYST_LOOP_DASHBOARD_PORT";
|
|
117474
117819
|
var DASHBOARD_NO_OPEN_ENV = "AMETYST_DASHBOARD_NO_OPEN";
|
|
@@ -117529,14 +117874,14 @@ function startDashboardServer(args) {
|
|
|
117529
117874
|
const data = {};
|
|
117530
117875
|
for (const name of files) {
|
|
117531
117876
|
try {
|
|
117532
|
-
const p =
|
|
117877
|
+
const p = join10(args.loopDir, name);
|
|
117533
117878
|
if (fs.existsSync(p)) data[name] = fs.readFileSync(p, "utf-8");
|
|
117534
117879
|
} catch {
|
|
117535
117880
|
}
|
|
117536
117881
|
}
|
|
117537
117882
|
const state = {};
|
|
117538
117883
|
try {
|
|
117539
|
-
const p =
|
|
117884
|
+
const p = join10(args.loopDir, ".state", "fires.jsonl");
|
|
117540
117885
|
if (fs.existsSync(p)) state["fires.jsonl"] = fs.readFileSync(p, "utf-8");
|
|
117541
117886
|
} catch {
|
|
117542
117887
|
}
|
|
@@ -117935,7 +118280,7 @@ function injectDefaultDashboard(loop2) {
|
|
|
117935
118280
|
|
|
117936
118281
|
// src/loops/memory-verbs.ts
|
|
117937
118282
|
init_esm_shims();
|
|
117938
|
-
import { readFileSync as
|
|
118283
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
117939
118284
|
|
|
117940
118285
|
// src/loops/sdk.ts
|
|
117941
118286
|
init_esm_shims();
|
|
@@ -118195,7 +118540,7 @@ async function taskMemoryAppendVerb(rawSlug, opts) {
|
|
|
118195
118540
|
const archived = opts.archived === true;
|
|
118196
118541
|
const note = opts.note?.trim() || void 0;
|
|
118197
118542
|
const scope = parseScopeFlag(opts.scope, false);
|
|
118198
|
-
const content = opts.file !== void 0 ?
|
|
118543
|
+
const content = opts.file !== void 0 ? readFileSync10(opts.file, "utf-8") : opts.content ?? "";
|
|
118199
118544
|
if (!content) {
|
|
118200
118545
|
throw new Error("nothing to store: pass --content <text> or --file <path>");
|
|
118201
118546
|
}
|
|
@@ -118332,14 +118677,14 @@ ${full.markdownBody ?? ""}`;
|
|
|
118332
118677
|
|
|
118333
118678
|
// src/mcp-server/read-file-body.ts
|
|
118334
118679
|
init_esm_shims();
|
|
118335
|
-
import { readFileSync as
|
|
118680
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
118336
118681
|
import { resolve as resolvePath } from "path";
|
|
118337
118682
|
function readMarkdownFile(filePath) {
|
|
118338
118683
|
const raw = typeof filePath === "string" ? filePath.trim() : "";
|
|
118339
118684
|
if (!raw) throw new Error("filePath is empty.");
|
|
118340
118685
|
const abs = resolvePath(raw);
|
|
118341
118686
|
try {
|
|
118342
|
-
return
|
|
118687
|
+
return readFileSync11(abs, "utf8");
|
|
118343
118688
|
} catch (err) {
|
|
118344
118689
|
const reason = err instanceof Error ? err.message : String(err);
|
|
118345
118690
|
throw new Error(`Could not read file at "${abs}": ${reason}`);
|
|
@@ -118668,7 +119013,7 @@ function enforceResponseCap(payload) {
|
|
|
118668
119013
|
|
|
118669
119014
|
// src/mcp-server/delegate-tools.ts
|
|
118670
119015
|
init_esm_shims();
|
|
118671
|
-
import { existsSync as
|
|
119016
|
+
import { existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
118672
119017
|
import { z as z3 } from "zod";
|
|
118673
119018
|
|
|
118674
119019
|
// src/delegate/jobs.ts
|
|
@@ -118770,7 +119115,7 @@ import * as nodeFs from "fs";
|
|
|
118770
119115
|
import { execFileSync } from "child_process";
|
|
118771
119116
|
import { createHash } from "crypto";
|
|
118772
119117
|
import { homedir as homedir5 } from "os";
|
|
118773
|
-
import { join as
|
|
119118
|
+
import { join as join11 } from "path";
|
|
118774
119119
|
var OPENCODE_VERSION = "1.18.13";
|
|
118775
119120
|
var OPENCODE_CHECKSUMS = {
|
|
118776
119121
|
"darwin-arm64": "6a85ae6de1aeb8e39ae4d977337b03f49168c2a827ee37b6f82c39471d711c63",
|
|
@@ -118792,10 +119137,10 @@ function resolveOpencodePlatform(platform, arch) {
|
|
|
118792
119137
|
return void 0;
|
|
118793
119138
|
}
|
|
118794
119139
|
function opencodeBinDir(home = homedir5()) {
|
|
118795
|
-
return
|
|
119140
|
+
return join11(home, `.ametyst${ENV_SUFFIX}`, "bin");
|
|
118796
119141
|
}
|
|
118797
119142
|
function opencodeBinaryPath(home = homedir5()) {
|
|
118798
|
-
return
|
|
119143
|
+
return join11(opencodeBinDir(home), `opencode-${OPENCODE_VERSION}`);
|
|
118799
119144
|
}
|
|
118800
119145
|
function opencodeSidecarPath(home = homedir5()) {
|
|
118801
119146
|
return `${opencodeBinaryPath(home)}.sha256`;
|
|
@@ -118850,7 +119195,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118850
119195
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
118851
119196
|
const expected = (deps.checksums ?? OPENCODE_CHECKSUMS)[key];
|
|
118852
119197
|
const actual = sha256Hex(bytes);
|
|
118853
|
-
const tmpArchive =
|
|
119198
|
+
const tmpArchive = join11(binDir, `.${artifact}.download-${process.pid}`);
|
|
118854
119199
|
fs.writeFileSync(tmpArchive, bytes);
|
|
118855
119200
|
if (actual !== expected) {
|
|
118856
119201
|
fs.rmSync(tmpArchive, { force: true });
|
|
@@ -118858,7 +119203,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118858
119203
|
`opencode bootstrap: checksum mismatch for ${artifact} (expected ${expected}, got ${actual}). The download was deleted; refusing to install an unverified binary.`
|
|
118859
119204
|
);
|
|
118860
119205
|
}
|
|
118861
|
-
const tmpExtractDir =
|
|
119206
|
+
const tmpExtractDir = join11(binDir, `.extract-${OPENCODE_VERSION}-${process.pid}`);
|
|
118862
119207
|
fs.rmSync(tmpExtractDir, { recursive: true, force: true });
|
|
118863
119208
|
fs.mkdirSync(tmpExtractDir, { recursive: true, mode: 448 });
|
|
118864
119209
|
try {
|
|
@@ -118867,7 +119212,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118867
119212
|
} else {
|
|
118868
119213
|
exec("tar", ["-xzf", tmpArchive, "-C", tmpExtractDir]);
|
|
118869
119214
|
}
|
|
118870
|
-
const extracted =
|
|
119215
|
+
const extracted = join11(tmpExtractDir, "opencode");
|
|
118871
119216
|
if (!fs.existsSync(extracted)) {
|
|
118872
119217
|
const entries = fs.readdirSync(tmpExtractDir).join(", ") || "<empty>";
|
|
118873
119218
|
throw new Error(
|
|
@@ -118938,15 +119283,15 @@ import { randomUUID } from "crypto";
|
|
|
118938
119283
|
init_esm_shims();
|
|
118939
119284
|
init_paths();
|
|
118940
119285
|
import { homedir as homedir6 } from "os";
|
|
118941
|
-
import { join as
|
|
119286
|
+
import { join as join12 } from "path";
|
|
118942
119287
|
function bridgeRunDir(home = homedir6()) {
|
|
118943
|
-
return
|
|
119288
|
+
return join12(home, `.ametyst${ENV_SUFFIX}`, "run");
|
|
118944
119289
|
}
|
|
118945
119290
|
function bridgeSocketPath(pid, home = homedir6()) {
|
|
118946
|
-
return
|
|
119291
|
+
return join12(bridgeRunDir(home), `delegate-${pid}.sock`);
|
|
118947
119292
|
}
|
|
118948
119293
|
function bridgeNoncePath(pid, home = homedir6()) {
|
|
118949
|
-
return
|
|
119294
|
+
return join12(bridgeRunDir(home), `delegate-${pid}.nonce`);
|
|
118950
119295
|
}
|
|
118951
119296
|
function bridgeNoncePathForSocket(socketPath) {
|
|
118952
119297
|
return socketPath.replace(/\.sock$/, ".nonce");
|
|
@@ -118968,7 +119313,7 @@ function listBridgeSockets(fs, home = homedir6()) {
|
|
|
118968
119313
|
const entries = [];
|
|
118969
119314
|
for (const name of names) {
|
|
118970
119315
|
if (pidFromSocketName(name) === void 0) continue;
|
|
118971
|
-
const path2 =
|
|
119316
|
+
const path2 = join12(dir, name);
|
|
118972
119317
|
try {
|
|
118973
119318
|
entries.push({ path: path2, mtimeMs: fs.statSync(path2).mtimeMs });
|
|
118974
119319
|
} catch {
|
|
@@ -119317,7 +119662,7 @@ async function startShim(deps) {
|
|
|
119317
119662
|
init_esm_shims();
|
|
119318
119663
|
import * as nodeFs3 from "fs";
|
|
119319
119664
|
import { tmpdir } from "os";
|
|
119320
|
-
import { join as
|
|
119665
|
+
import { join as join13 } from "path";
|
|
119321
119666
|
var DELEGATE_PROVIDER_ID = "ametyst";
|
|
119322
119667
|
var DELEGATE_SHIM_API_KEY = "ametyst-local-shim";
|
|
119323
119668
|
function buildDelegateConfig(opts) {
|
|
@@ -119341,8 +119686,8 @@ function delegateModelRef(model) {
|
|
|
119341
119686
|
}
|
|
119342
119687
|
function writeTempDelegateConfig(config, deps = {}) {
|
|
119343
119688
|
const fs = deps.fs ?? nodeFs3;
|
|
119344
|
-
const dir = fs.mkdtempSync(
|
|
119345
|
-
const path2 =
|
|
119689
|
+
const dir = fs.mkdtempSync(join13(deps.tmp ?? tmpdir(), "ametyst-delegate-"));
|
|
119690
|
+
const path2 = join13(dir, "opencode.json");
|
|
119346
119691
|
fs.writeFileSync(path2, `${JSON.stringify(config, null, 2)}
|
|
119347
119692
|
`, { mode: 384 });
|
|
119348
119693
|
let done = false;
|
|
@@ -119411,7 +119756,7 @@ init_paths();
|
|
|
119411
119756
|
import * as nodeFs4 from "fs";
|
|
119412
119757
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
119413
119758
|
import { homedir as homedir8 } from "os";
|
|
119414
|
-
import { basename as basename4, join as
|
|
119759
|
+
import { basename as basename4, join as join14 } from "path";
|
|
119415
119760
|
var DELEGATED_CHILD_ENV_VAR = "AMETYST_DELEGATED";
|
|
119416
119761
|
var SPEND_GRANT_TOKEN_ENV_VAR = "AMETYST_DELEGATE_SPEND_TOKEN";
|
|
119417
119762
|
var SPEND_KILL_SWITCH_ENV_VAR = "AMETYST_DELEGATE_NO_SPEND";
|
|
@@ -119493,11 +119838,11 @@ function isDelegatedByAncestry(deps = {}) {
|
|
|
119493
119838
|
return classifyAncestry(deps) === "delegated";
|
|
119494
119839
|
}
|
|
119495
119840
|
function delegateChildConfigHome(home = homedir8()) {
|
|
119496
|
-
return
|
|
119841
|
+
return join14(home, `.ametyst${ENV_SUFFIX}`, "delegate-xdg");
|
|
119497
119842
|
}
|
|
119498
119843
|
function ensureChildConfigHome(fs = nodeFs4, home = homedir8()) {
|
|
119499
119844
|
const root2 = delegateChildConfigHome(home);
|
|
119500
|
-
const inner =
|
|
119845
|
+
const inner = join14(root2, "opencode");
|
|
119501
119846
|
if (!fs.existsSync(inner)) fs.mkdirSync(inner, { recursive: true, mode: 448 });
|
|
119502
119847
|
return root2;
|
|
119503
119848
|
}
|
|
@@ -119507,7 +119852,7 @@ function userOpencodeConfigDir(deps = {}) {
|
|
|
119507
119852
|
const env = deps.env ?? process.env;
|
|
119508
119853
|
const home = deps.home ?? homedir8();
|
|
119509
119854
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
119510
|
-
return
|
|
119855
|
+
return join14(xdg ? xdg : join14(home, ".config"), "opencode");
|
|
119511
119856
|
}
|
|
119512
119857
|
function stripJsonComments(text) {
|
|
119513
119858
|
let out = "";
|
|
@@ -119551,9 +119896,9 @@ function readUserMcpServers(deps = {}) {
|
|
|
119551
119896
|
const warn = deps.warn ?? ((line) => console.error(line));
|
|
119552
119897
|
const home = deps.home ?? homedir8();
|
|
119553
119898
|
const dir = userOpencodeConfigDir(deps);
|
|
119554
|
-
if (dir ===
|
|
119899
|
+
if (dir === join14(delegateChildConfigHome(home), "opencode")) return {};
|
|
119555
119900
|
for (const filename of USER_OPENCODE_CONFIG_FILENAMES) {
|
|
119556
|
-
const path2 =
|
|
119901
|
+
const path2 = join14(dir, filename);
|
|
119557
119902
|
let raw;
|
|
119558
119903
|
try {
|
|
119559
119904
|
if (!fs.existsSync(path2)) continue;
|
|
@@ -120651,7 +120996,7 @@ init_esm_shims();
|
|
|
120651
120996
|
import * as nodeFs6 from "fs";
|
|
120652
120997
|
import { createHash as createHash2, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
120653
120998
|
import { homedir as homedir10 } from "os";
|
|
120654
|
-
import { join as
|
|
120999
|
+
import { join as join15 } from "path";
|
|
120655
121000
|
var SPEND_GRANT_RECORD_VERSION = 1;
|
|
120656
121001
|
var TOKEN_BYTES = 32;
|
|
120657
121002
|
var SPEND_GRANT_GRACE_MS = 5 * 6e4;
|
|
@@ -120659,7 +121004,7 @@ function spendGrantPath(jobId, home = homedir10()) {
|
|
|
120659
121004
|
if (!/^[A-Za-z0-9_-]{1,64}$/.test(jobId)) {
|
|
120660
121005
|
throw new Error(`refusing to build a grant path for job id ${JSON.stringify(jobId)}`);
|
|
120661
121006
|
}
|
|
120662
|
-
return
|
|
121007
|
+
return join15(bridgeRunDir(home), `spend-grant-${jobId}.json`);
|
|
120663
121008
|
}
|
|
120664
121009
|
function hashSpendGrantToken(token) {
|
|
120665
121010
|
return createHash2("sha256").update(token, "utf-8").digest("hex");
|
|
@@ -120765,13 +121110,13 @@ init_esm_shims();
|
|
|
120765
121110
|
init_paths();
|
|
120766
121111
|
import * as nodeFs7 from "fs";
|
|
120767
121112
|
import { homedir as homedir11 } from "os";
|
|
120768
|
-
import { dirname as dirname6, join as
|
|
121113
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
120769
121114
|
var DELEGATE_CREDIT_CAPABILITY = "credit";
|
|
120770
121115
|
var DELEGATE_CREDIT_PRICE_USD = 1;
|
|
120771
121116
|
var DELEGATE_CREDIT_REQUEST_BODY = "{}";
|
|
120772
121117
|
var DELEGATE_CREDIT_SPEND_SNIPPET = `spend({ merchant_slug: "${DELEGATE_MERCHANT_SLUG}", capability: "${DELEGATE_CREDIT_CAPABILITY}", inputs: "${DELEGATE_CREDIT_REQUEST_BODY}" })`;
|
|
120773
121118
|
function creditLedgerPath(home = homedir11()) {
|
|
120774
|
-
return
|
|
121119
|
+
return join16(home, `.ametyst${ENV_SUFFIX}`, "run", "delegate-credit.json");
|
|
120775
121120
|
}
|
|
120776
121121
|
function readCreditLedger(fs = nodeFs7, path2 = creditLedgerPath()) {
|
|
120777
121122
|
try {
|
|
@@ -121162,7 +121507,7 @@ function registerDelegateTools(deps) {
|
|
|
121162
121507
|
const validated = validateStartInput(params, {
|
|
121163
121508
|
isDirectory: (path2) => {
|
|
121164
121509
|
try {
|
|
121165
|
-
return
|
|
121510
|
+
return existsSync11(path2) && statSync2(path2).isDirectory();
|
|
121166
121511
|
} catch {
|
|
121167
121512
|
return false;
|
|
121168
121513
|
}
|
|
@@ -121417,7 +121762,7 @@ init_resolve();
|
|
|
121417
121762
|
|
|
121418
121763
|
// src/mcp-server/identity-reload.ts
|
|
121419
121764
|
init_esm_shims();
|
|
121420
|
-
import { readFileSync as
|
|
121765
|
+
import { readFileSync as readFileSync13, statSync as statSync3 } from "fs";
|
|
121421
121766
|
var NO_VAULT = "absent";
|
|
121422
121767
|
var IDENTITY_CHECK_THROTTLE_MS = 1e3;
|
|
121423
121768
|
function fingerprintVault(stat) {
|
|
@@ -121438,7 +121783,11 @@ var IdentityWatch = class {
|
|
|
121438
121783
|
stat;
|
|
121439
121784
|
/** False for an env-supplied key: `poll` is then a guaranteed no-op. */
|
|
121440
121785
|
armed;
|
|
121786
|
+
configPath;
|
|
121787
|
+
readWorkspaceKey;
|
|
121441
121788
|
fingerprint;
|
|
121789
|
+
configStamp;
|
|
121790
|
+
workspaceKey;
|
|
121442
121791
|
lastCheckedAt = null;
|
|
121443
121792
|
constructor(options) {
|
|
121444
121793
|
this.vaultPath = options.vaultPath;
|
|
@@ -121446,12 +121795,43 @@ var IdentityWatch = class {
|
|
|
121446
121795
|
this.throttleMs = options.throttleMs ?? IDENTITY_CHECK_THROTTLE_MS;
|
|
121447
121796
|
this.stat = options.stat ?? statVaultFile;
|
|
121448
121797
|
this.armed = options.source !== "env";
|
|
121449
|
-
this.
|
|
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"}`;
|
|
121450
121826
|
}
|
|
121451
121827
|
/** The fingerprint this watch currently considers current. */
|
|
121452
121828
|
currentFingerprint() {
|
|
121453
121829
|
return this.fingerprint;
|
|
121454
121830
|
}
|
|
121831
|
+
/** The workspace key this watch last read from the config. */
|
|
121832
|
+
currentWorkspaceKey() {
|
|
121833
|
+
return this.workspaceKey;
|
|
121834
|
+
}
|
|
121455
121835
|
/**
|
|
121456
121836
|
* One throttled `stat`. Returns the change when the vault file differs from
|
|
121457
121837
|
* the fingerprint on record — and ADOPTS it in the same step, so a reload the
|
|
@@ -121464,7 +121844,14 @@ var IdentityWatch = class {
|
|
|
121464
121844
|
const at = this.now();
|
|
121465
121845
|
if (this.lastCheckedAt !== null && at - this.lastCheckedAt < this.throttleMs) return null;
|
|
121466
121846
|
this.lastCheckedAt = at;
|
|
121467
|
-
|
|
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();
|
|
121468
121855
|
if (next === this.fingerprint) return null;
|
|
121469
121856
|
const from14 = this.fingerprint;
|
|
121470
121857
|
this.fingerprint = next;
|
|
@@ -121480,7 +121867,7 @@ var IdentityWatch = class {
|
|
|
121480
121867
|
*/
|
|
121481
121868
|
readVault() {
|
|
121482
121869
|
try {
|
|
121483
|
-
return
|
|
121870
|
+
return readFileSync13(this.vaultPath, "utf-8");
|
|
121484
121871
|
} catch {
|
|
121485
121872
|
return null;
|
|
121486
121873
|
}
|
|
@@ -121488,6 +121875,7 @@ var IdentityWatch = class {
|
|
|
121488
121875
|
};
|
|
121489
121876
|
|
|
121490
121877
|
// src/mcp-server/index.ts
|
|
121878
|
+
init_grant_keystore();
|
|
121491
121879
|
function buildDeclaredConnectorsSentence(catalog, unreadable) {
|
|
121492
121880
|
const read = catalog === void 0 ? { connectors: cachedCatalogOrEmpty(), unreadable: mirrorGap() } : { connectors: catalog, unreadable: unreadable ?? [] };
|
|
121493
121881
|
const gap = describeCatalogGap(read.unreadable);
|
|
@@ -121498,6 +121886,39 @@ function buildDeclaredConnectorsSentence(catalog, unreadable) {
|
|
|
121498
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}`);
|
|
121499
121887
|
}
|
|
121500
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
|
+
}
|
|
121501
121922
|
var plaintextVaultMode = false;
|
|
121502
121923
|
function refreshPlaintextVaultMode() {
|
|
121503
121924
|
const vault = currentCredentials.walletKeystoreJson;
|
|
@@ -122519,7 +122940,16 @@ async function initializeCredentials(walletKeystoreJson, eoaAddress, config) {
|
|
|
122519
122940
|
currentCredentials.signerAddress = eoaAddress;
|
|
122520
122941
|
currentCredentials.eoaAddress = eoaAddress;
|
|
122521
122942
|
currentCredentials.apiKey = config.apiKey;
|
|
122522
|
-
identityWatch = new IdentityWatch({
|
|
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
|
+
});
|
|
122523
122953
|
mcpEventLogger = new McpEventLogger(
|
|
122524
122954
|
() => getSDK(),
|
|
122525
122955
|
() => currentCredentials.apiKey,
|
|
@@ -122605,10 +123035,8 @@ async function createKernelClientFromVault() {
|
|
|
122605
123035
|
throw new Error("No passphrase or keystore available");
|
|
122606
123036
|
}
|
|
122607
123037
|
if (!cliConfig) throw new Error("CLI config not initialized");
|
|
122608
|
-
|
|
122609
|
-
|
|
122610
|
-
currentCredentials.walletKeystoreJson,
|
|
122611
|
-
currentCredentials.passphrase
|
|
123038
|
+
let privateKey = signingKeyForAddress(
|
|
123039
|
+
currentCredentials.signerAddress || currentCredentials.eoaAddress || ""
|
|
122612
123040
|
);
|
|
122613
123041
|
console.error(`${ts()} Wallet key loaded`);
|
|
122614
123042
|
try {
|
|
@@ -122739,17 +123167,6 @@ var approvalWaitConfig = (() => {
|
|
|
122739
123167
|
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
122740
123168
|
};
|
|
122741
123169
|
})();
|
|
122742
|
-
function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
|
|
122743
|
-
if (!Array.isArray(wallets)) return void 0;
|
|
122744
|
-
if (pendingWalletId) {
|
|
122745
|
-
return wallets.find(
|
|
122746
|
-
(w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved"
|
|
122747
|
-
);
|
|
122748
|
-
}
|
|
122749
|
-
return wallets.find(
|
|
122750
|
-
(w) => w?.address?.toLowerCase() === eoaAddress?.toLowerCase() && w?.status === "approved"
|
|
122751
|
-
);
|
|
122752
|
-
}
|
|
122753
123170
|
async function tryResolvePendingApproval(probe) {
|
|
122754
123171
|
if (currentCredentials.authorizationStatus === "approved") return true;
|
|
122755
123172
|
if (currentCredentials.authorizationStatus !== "pending" || !currentCredentials.apiKey || !currentCredentials.eoaAddress) {
|
|
@@ -122760,12 +123177,17 @@ async function tryResolvePendingApproval(probe) {
|
|
|
122760
123177
|
if (probe) probe.fetched = true;
|
|
122761
123178
|
const approvedWallet = findCurrentApprovedWallet(
|
|
122762
123179
|
freshWallets,
|
|
122763
|
-
|
|
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(),
|
|
122764
123183
|
currentCredentials.pendingWalletId
|
|
122765
123184
|
);
|
|
122766
123185
|
if (!approvedWallet) return false;
|
|
122767
123186
|
console.error("\u2705 [approval-wait] Backend says approved \u2014 syncing");
|
|
122768
123187
|
currentCredentials.authorizationStatus = "approved";
|
|
123188
|
+
if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
|
|
123189
|
+
currentCredentials.signerAddress = approvedWallet.address;
|
|
123190
|
+
}
|
|
122769
123191
|
currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
|
|
122770
123192
|
currentCredentials.virtualWalletId = String(approvedWallet.id);
|
|
122771
123193
|
currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
|
|
@@ -122887,11 +123309,26 @@ async function applyStartSessionUnlock(passphrase, resetRequested) {
|
|
|
122887
123309
|
persistVault: persistVault2,
|
|
122888
123310
|
wipeVault: wipeVault2,
|
|
122889
123311
|
sdk,
|
|
122890
|
-
|
|
123312
|
+
// FORCED REFRESH. `start_session` verifies the session's grant against the
|
|
123313
|
+
// backend (see `runStartSession`), and a verification run against the cache
|
|
123314
|
+
// this process loaded at boot verifies nothing: a grant approved since boot
|
|
123315
|
+
// would be invisible to exactly the check that exists to find it.
|
|
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),
|
|
122891
123323
|
readPolicyActiveOnchain: buildPolicyActiveOnchainReader(sdk)
|
|
122892
123324
|
}
|
|
122893
123325
|
);
|
|
122894
|
-
const { walletChanged } = mergeStartSessionCredentials(
|
|
123326
|
+
const { walletChanged } = mergeStartSessionCredentials(
|
|
123327
|
+
currentCredentials,
|
|
123328
|
+
result.credentials,
|
|
123329
|
+
result.responseKey,
|
|
123330
|
+
{ grantChanged: result.grantChanged }
|
|
123331
|
+
);
|
|
122895
123332
|
refreshPlaintextVaultMode();
|
|
122896
123333
|
if (walletChanged) {
|
|
122897
123334
|
walletsCache = null;
|
|
@@ -123296,11 +123733,15 @@ server.tool(
|
|
|
123296
123733
|
};
|
|
123297
123734
|
}
|
|
123298
123735
|
const { virtualWalletsManagers: virtualWalletsManagers2 } = await getSDK();
|
|
123299
|
-
const result = await
|
|
123736
|
+
const result = await submitRotatedAccessRequest(
|
|
123300
123737
|
virtualWalletsManagers2,
|
|
123301
123738
|
currentCredentials.apiKey,
|
|
123302
|
-
|
|
123303
|
-
|
|
123739
|
+
policyId,
|
|
123740
|
+
createGrantKeyIssuer({
|
|
123741
|
+
encrypted: vaultIsEncrypted(),
|
|
123742
|
+
passphrase: currentCredentials.passphrase,
|
|
123743
|
+
workspaceId: readBoundWorkspace()
|
|
123744
|
+
})
|
|
123304
123745
|
);
|
|
123305
123746
|
currentCredentials.authorizationStatus = "pending";
|
|
123306
123747
|
currentCredentials.pendingWalletId = result.id ? String(result.id) : void 0;
|
|
@@ -123358,7 +123799,7 @@ server.tool(
|
|
|
123358
123799
|
server.tool(
|
|
123359
123800
|
{
|
|
123360
123801
|
name: "getWalletStatus",
|
|
123361
|
-
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.",
|
|
123362
123803
|
inputs: []
|
|
123363
123804
|
},
|
|
123364
123805
|
async () => {
|
|
@@ -123390,10 +123831,9 @@ server.tool(
|
|
|
123390
123831
|
fetchVirtualWalletsFromBackend(currentCredentials.apiKey, !refreshedFromBackend),
|
|
123391
123832
|
fetchPoliciesFromBackend(currentCredentials.apiKey, true)
|
|
123392
123833
|
]);
|
|
123393
|
-
const freshWallet = freshWallets
|
|
123394
|
-
|
|
123395
|
-
)
|
|
123396
|
-
if (freshWallet && freshWallet.status === "approved") {
|
|
123834
|
+
const freshWallet = selectNewestApprovedWalletAmong(freshWallets, ownedSigningAddresses());
|
|
123835
|
+
const freshWalletIsActiveGrant = String(freshWallet?.id) === String(currentCredentials.virtualWalletId);
|
|
123836
|
+
if (freshWallet && freshWalletIsActiveGrant) {
|
|
123397
123837
|
if (freshWallet.policyAssociated != null) {
|
|
123398
123838
|
currentCredentials.policyId = String(freshWallet.policyAssociated);
|
|
123399
123839
|
}
|
|
@@ -123412,8 +123852,24 @@ server.tool(
|
|
|
123412
123852
|
authorized: currentCredentials.authorizationStatus === "approved",
|
|
123413
123853
|
status: currentCredentials.authorizationStatus || "none",
|
|
123414
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,
|
|
123415
123863
|
walletAddress: currentCredentials.walletAddress || null,
|
|
123416
123864
|
kernelClientActive: !!currentCredentials.virtualWalletKernelAccountClient,
|
|
123865
|
+
// WHICH GRANT THIS SESSION SIGNS WITH. The 2026-09-01 incident — three
|
|
123866
|
+
// approved rows for one EOA, every session silently signing with the oldest
|
|
123867
|
+
// — was invisible in every payload: diagnosing it took a nonce-key autopsy.
|
|
123868
|
+
// These two ids are the row `start_session` actually selected, so the next
|
|
123869
|
+
// stale grant is a field to read rather than a forensic exercise. Null until
|
|
123870
|
+
// a session has adopted one.
|
|
123871
|
+
activeVirtualWalletId: currentCredentials.virtualWalletId != null ? String(currentCredentials.virtualWalletId) : null,
|
|
123872
|
+
activePolicyId: currentCredentials.policyId != null ? String(currentCredentials.policyId) : null,
|
|
123417
123873
|
// WHICH WORKSPACE THIS IS. The 2026-08-31 incident produced writes into
|
|
123418
123874
|
// the wrong workspace with nothing in any payload naming one — an agent
|
|
123419
123875
|
// reading wallet status could not have told, and neither could a human
|
|
@@ -123427,9 +123883,7 @@ server.tool(
|
|
|
123427
123883
|
}
|
|
123428
123884
|
}
|
|
123429
123885
|
if (walletsCache && currentCredentials.eoaAddress) {
|
|
123430
|
-
const wallet = walletsCache.find(
|
|
123431
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
123432
|
-
);
|
|
123886
|
+
const wallet = selectNewestApprovedWalletAmong(walletsCache, ownedSigningAddresses()) ?? walletsCache.find((w) => isOwnedSigningAddress(w.address));
|
|
123433
123887
|
if (wallet) {
|
|
123434
123888
|
const now = Math.floor(Date.now() / 1e3);
|
|
123435
123889
|
const validUntil = wallet.policyValidUntil;
|
|
@@ -123953,7 +124407,7 @@ async function startLiveDashboard(entity, dir) {
|
|
|
123953
124407
|
clearInterval(watcher);
|
|
123954
124408
|
return;
|
|
123955
124409
|
}
|
|
123956
|
-
if (!
|
|
124410
|
+
if (!existsSync13(dir)) {
|
|
123957
124411
|
clearInterval(watcher);
|
|
123958
124412
|
liveDashboards.delete(slug);
|
|
123959
124413
|
try {
|
|
@@ -124900,10 +125354,13 @@ server.tool(
|
|
|
124900
125354
|
}
|
|
124901
125355
|
const approvedWallet = findCurrentApprovedWallet(
|
|
124902
125356
|
wallets,
|
|
124903
|
-
|
|
125357
|
+
ownedSigningAddresses(),
|
|
124904
125358
|
currentCredentials.pendingWalletId
|
|
124905
125359
|
);
|
|
124906
125360
|
if (approvedWallet && currentCredentials.authorizationStatus !== "approved") {
|
|
125361
|
+
if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
|
|
125362
|
+
currentCredentials.signerAddress = approvedWallet.address;
|
|
125363
|
+
}
|
|
124907
125364
|
currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
|
|
124908
125365
|
currentCredentials.virtualWalletId = String(approvedWallet.id);
|
|
124909
125366
|
currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
|
|
@@ -125792,9 +126249,7 @@ async function reconcileApprovalStateOnConnect() {
|
|
|
125792
126249
|
}
|
|
125793
126250
|
try {
|
|
125794
126251
|
const wallets = await fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true);
|
|
125795
|
-
const approved = wallets
|
|
125796
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase() && w.status === "approved"
|
|
125797
|
-
);
|
|
126252
|
+
const approved = selectNewestApprovedWalletAmong(wallets, ownedSigningAddresses());
|
|
125798
126253
|
if (!approved) return;
|
|
125799
126254
|
if (typeof approved.id === "number" && hasProcessedApprovalEventId(approved.id)) return;
|
|
125800
126255
|
console.error(
|
|
@@ -125809,9 +126264,7 @@ async function reconcileApprovalStateOnConnect() {
|
|
|
125809
126264
|
policyOnchainPermissions: void 0
|
|
125810
126265
|
});
|
|
125811
126266
|
if (walletsCache) {
|
|
125812
|
-
const idx = walletsCache.findIndex(
|
|
125813
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
125814
|
-
);
|
|
126267
|
+
const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
|
|
125815
126268
|
if (idx >= 0) walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
|
|
125816
126269
|
}
|
|
125817
126270
|
if (typeof approved.id === "number") rememberProcessedApprovalEventId(approved.id);
|
|
@@ -125844,8 +126297,8 @@ async function setupWebSocketListener() {
|
|
|
125844
126297
|
return;
|
|
125845
126298
|
}
|
|
125846
126299
|
const statusMatch = event.status === "approved" || event.status === "ok";
|
|
125847
|
-
const addressMatch = event.address
|
|
125848
|
-
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}`);
|
|
125849
126302
|
if (statusMatch && addressMatch) {
|
|
125850
126303
|
try {
|
|
125851
126304
|
await onAuthorizationApproved({
|
|
@@ -125857,9 +126310,7 @@ async function setupWebSocketListener() {
|
|
|
125857
126310
|
policyOnchainPermissions: void 0
|
|
125858
126311
|
});
|
|
125859
126312
|
if (walletsCache) {
|
|
125860
|
-
const idx = walletsCache.findIndex(
|
|
125861
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
125862
|
-
);
|
|
126313
|
+
const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
|
|
125863
126314
|
if (idx >= 0) {
|
|
125864
126315
|
walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
|
|
125865
126316
|
}
|
|
@@ -126132,15 +126583,15 @@ function killPreviousServeInstances(deps) {
|
|
|
126132
126583
|
|
|
126133
126584
|
// src/commands/autosync-skills.ts
|
|
126134
126585
|
init_esm_shims();
|
|
126135
|
-
import { existsSync as
|
|
126586
|
+
import { existsSync as existsSync15 } from "fs";
|
|
126136
126587
|
import { homedir as homedir12 } from "os";
|
|
126137
|
-
import { join as
|
|
126588
|
+
import { join as join19 } from "path";
|
|
126138
126589
|
|
|
126139
126590
|
// src/compounds/sync-skills.ts
|
|
126140
126591
|
init_esm_shims();
|
|
126141
126592
|
init_paths();
|
|
126142
|
-
import { existsSync as
|
|
126143
|
-
import { join as
|
|
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";
|
|
126144
126595
|
var MANAGED_MARKER = "<!-- ametyst-managed: sync-skills -->";
|
|
126145
126596
|
var GITIGNORE_HEADER = "# ametyst-managed: sync-skills \u2014 pointer skills generated for this account; never commit them.";
|
|
126146
126597
|
var GITIGNORE_HEADER_2 = "# Maintained by `ametyst serve` on every boot. Real skills without the managed marker are not listed.";
|
|
@@ -126184,22 +126635,22 @@ ${taskRunSection(slug, kind)}`;
|
|
|
126184
126635
|
}
|
|
126185
126636
|
function isManaged(file) {
|
|
126186
126637
|
try {
|
|
126187
|
-
return
|
|
126638
|
+
return readFileSync14(file, "utf8").includes(MANAGED_MARKER);
|
|
126188
126639
|
} catch {
|
|
126189
126640
|
return false;
|
|
126190
126641
|
}
|
|
126191
126642
|
}
|
|
126192
126643
|
function listManagedDirs(root2) {
|
|
126193
|
-
return
|
|
126644
|
+
return readdirSync5(root2, { withFileTypes: true }).filter((e) => e.isDirectory() && isManaged(join18(root2, e.name, "SKILL.md"))).map((e) => e.name).sort();
|
|
126194
126645
|
}
|
|
126195
126646
|
function buildGitignoreContent(managedSlugs) {
|
|
126196
126647
|
return [GITIGNORE_HEADER, GITIGNORE_HEADER_2, ".gitignore", ...managedSlugs.map((s) => `/${s}/`)].join("\n") + "\n";
|
|
126197
126648
|
}
|
|
126198
126649
|
function maintainGitignore(root2) {
|
|
126199
|
-
const file =
|
|
126650
|
+
const file = join18(root2, ".gitignore");
|
|
126200
126651
|
const managed = listManagedDirs(root2);
|
|
126201
|
-
if (
|
|
126202
|
-
const current =
|
|
126652
|
+
if (existsSync14(file)) {
|
|
126653
|
+
const current = readFileSync14(file, "utf8");
|
|
126203
126654
|
const firstLine = current.split(/\r?\n/, 1)[0];
|
|
126204
126655
|
if (firstLine !== GITIGNORE_HEADER) {
|
|
126205
126656
|
console.error(
|
|
@@ -126257,26 +126708,26 @@ async function syncSkills(opts = {}) {
|
|
|
126257
126708
|
if (!desired.has(dir)) desired.set(dir, item);
|
|
126258
126709
|
}
|
|
126259
126710
|
const root2 = skillsRoot(target, global2);
|
|
126260
|
-
|
|
126711
|
+
mkdirSync9(root2, { recursive: true });
|
|
126261
126712
|
let written = 0;
|
|
126262
126713
|
const skipped = [];
|
|
126263
126714
|
for (const [dir, item] of desired) {
|
|
126264
|
-
const file =
|
|
126265
|
-
if (
|
|
126715
|
+
const file = join18(dir, "SKILL.md");
|
|
126716
|
+
if (existsSync14(file) && !isManaged(file)) {
|
|
126266
126717
|
skipped.push(item.slug);
|
|
126267
126718
|
console.warn(`\u26A0\uFE0F sync-skills: skipping "${item.slug}" \u2014 an unmanaged skill already exists at ${file}`);
|
|
126268
126719
|
continue;
|
|
126269
126720
|
}
|
|
126270
|
-
|
|
126721
|
+
mkdirSync9(dir, { recursive: true });
|
|
126271
126722
|
writeFileSync10(file, buildStubContent(item));
|
|
126272
126723
|
written++;
|
|
126273
126724
|
}
|
|
126274
126725
|
let pruned = 0;
|
|
126275
|
-
for (const entry of
|
|
126726
|
+
for (const entry of readdirSync5(root2, { withFileTypes: true })) {
|
|
126276
126727
|
if (!entry.isDirectory()) continue;
|
|
126277
|
-
const dir =
|
|
126728
|
+
const dir = join18(root2, entry.name);
|
|
126278
126729
|
if (desired.has(dir)) continue;
|
|
126279
|
-
if (!isManaged(
|
|
126730
|
+
if (!isManaged(join18(dir, "SKILL.md"))) continue;
|
|
126280
126731
|
rmSync(dir, { recursive: true, force: true });
|
|
126281
126732
|
pruned++;
|
|
126282
126733
|
}
|
|
@@ -126290,12 +126741,12 @@ function isSkillsAutosyncEnabled(env = process.env) {
|
|
|
126290
126741
|
return !(raw === "0" || raw === "false" || raw === "off" || raw === "no");
|
|
126291
126742
|
}
|
|
126292
126743
|
function detectPresentTargets(deps = {}) {
|
|
126293
|
-
const exists = deps.existsSync ??
|
|
126744
|
+
const exists = deps.existsSync ?? existsSync15;
|
|
126294
126745
|
const cwd = deps.cwd ?? (() => process.cwd());
|
|
126295
126746
|
const home = deps.homedir ?? homedir12;
|
|
126296
126747
|
const targets = [];
|
|
126297
|
-
if (exists(
|
|
126298
|
-
if (exists(
|
|
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");
|
|
126299
126750
|
return targets;
|
|
126300
126751
|
}
|
|
126301
126752
|
function resolveAutosyncTargets(clientName, deps = {}) {
|
|
@@ -126985,14 +127436,14 @@ init_esm_shims();
|
|
|
126985
127436
|
// src/loops/materialize.ts
|
|
126986
127437
|
init_esm_shims();
|
|
126987
127438
|
init_paths();
|
|
126988
|
-
import { mkdirSync as
|
|
126989
|
-
import { join as
|
|
127439
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
127440
|
+
import { join as join20 } from "path";
|
|
126990
127441
|
var MIRRORED_DEFINITION_FILES = ["SKILL.md", "VISION.md", "README.md"];
|
|
126991
127442
|
function materialize(loop2, fireId, stateDocs = []) {
|
|
126992
127443
|
const dir = loopFireDir(loop2.slug, fireId);
|
|
126993
|
-
|
|
127444
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
126994
127445
|
for (const doc of stateDocs) {
|
|
126995
|
-
writeFileSync11(
|
|
127446
|
+
writeFileSync11(join20(dir, doc.filename), doc.body, { mode: 384 });
|
|
126996
127447
|
}
|
|
126997
127448
|
const files = {
|
|
126998
127449
|
"SKILL.md": loop2.markdownBody ?? "",
|
|
@@ -127007,10 +127458,10 @@ function materialize(loop2, fireId, stateDocs = []) {
|
|
|
127007
127458
|
files["dashboard.manifest.json"] = loop2.dashboardManifest;
|
|
127008
127459
|
}
|
|
127009
127460
|
for (const [name, body] of Object.entries(files)) {
|
|
127010
|
-
writeFileSync11(
|
|
127461
|
+
writeFileSync11(join20(dir, name), body, { mode: 384 });
|
|
127011
127462
|
}
|
|
127012
127463
|
writeFileSync11(
|
|
127013
|
-
|
|
127464
|
+
join20(dir, "STATUS.md"),
|
|
127014
127465
|
`# STATUS \u2014 ${loop2.slug}
|
|
127015
127466
|
|
|
127016
127467
|
loop_id: ${loop2.id}
|
|
@@ -127026,11 +127477,11 @@ queue: not started
|
|
|
127026
127477
|
function mirrorDefinitionFiles(slug, files) {
|
|
127027
127478
|
try {
|
|
127028
127479
|
const root2 = loopDir(slug);
|
|
127029
|
-
|
|
127480
|
+
mkdirSync10(root2, { recursive: true, mode: 448 });
|
|
127030
127481
|
for (const name of [...MIRRORED_DEFINITION_FILES, "dashboard.html", "dashboard.manifest.json"]) {
|
|
127031
127482
|
const body = files[name];
|
|
127032
127483
|
if (body === void 0) continue;
|
|
127033
|
-
writeFileSync11(
|
|
127484
|
+
writeFileSync11(join20(root2, name), body, { mode: 384 });
|
|
127034
127485
|
}
|
|
127035
127486
|
} catch {
|
|
127036
127487
|
}
|
|
@@ -127156,15 +127607,15 @@ function resolveMaxBudgetUsd(opts, env = process.env) {
|
|
|
127156
127607
|
// src/loops/heartbeat.ts
|
|
127157
127608
|
init_esm_shims();
|
|
127158
127609
|
import * as realFs2 from "fs";
|
|
127159
|
-
import { join as
|
|
127610
|
+
import { join as join21 } from "path";
|
|
127160
127611
|
function startHeartbeat(loopDir2, info, deps = {}) {
|
|
127161
127612
|
const fs = deps.fs ?? realFs2;
|
|
127162
127613
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
127163
127614
|
const setI = deps.setInterval ?? globalThis.setInterval;
|
|
127164
127615
|
const clearI = deps.clearInterval ?? globalThis.clearInterval;
|
|
127165
127616
|
const intervalMs = deps.intervalMs ?? 6e4;
|
|
127166
|
-
const stateDir =
|
|
127167
|
-
const path2 =
|
|
127617
|
+
const stateDir = join21(loopDir2, ".state");
|
|
127618
|
+
const path2 = join21(stateDir, "fire.running");
|
|
127168
127619
|
try {
|
|
127169
127620
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
127170
127621
|
fs.writeFileSync(
|
|
@@ -127204,10 +127655,10 @@ ${info.sessionId ? `session=${info.sessionId}
|
|
|
127204
127655
|
init_esm_shims();
|
|
127205
127656
|
import * as realFs3 from "fs";
|
|
127206
127657
|
import { homedir as homedir13 } from "os";
|
|
127207
|
-
import { join as
|
|
127658
|
+
import { join as join22 } from "path";
|
|
127208
127659
|
function transcriptPathFor(cwd, sessionId2, home = homedir13()) {
|
|
127209
127660
|
const slug = cwd.replace(/\//g, "-");
|
|
127210
|
-
return
|
|
127661
|
+
return join22(home, ".claude", "projects", slug, `${sessionId2}.jsonl`);
|
|
127211
127662
|
}
|
|
127212
127663
|
function parseTranscriptStats(jsonl) {
|
|
127213
127664
|
const tokens = {
|
|
@@ -127278,9 +127729,9 @@ function recordFireAccounting(args) {
|
|
|
127278
127729
|
}
|
|
127279
127730
|
}
|
|
127280
127731
|
function appendFireLine(fs, loopDir2, rec) {
|
|
127281
|
-
const stateDir =
|
|
127732
|
+
const stateDir = join22(loopDir2, ".state");
|
|
127282
127733
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
127283
|
-
fs.appendFileSync(
|
|
127734
|
+
fs.appendFileSync(join22(stateDir, "fires.jsonl"), JSON.stringify(rec) + "\n", { mode: 384 });
|
|
127284
127735
|
}
|
|
127285
127736
|
function recordFailedLaunch(args) {
|
|
127286
127737
|
const fs = args.deps?.fs ?? realFs3;
|
|
@@ -127309,8 +127760,8 @@ function recordFailedLaunch(args) {
|
|
|
127309
127760
|
init_esm_shims();
|
|
127310
127761
|
import { spawn as spawn2 } from "child_process";
|
|
127311
127762
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
127312
|
-
import { existsSync as
|
|
127313
|
-
import { dirname as dirname8, join as
|
|
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";
|
|
127314
127765
|
|
|
127315
127766
|
// src/loops/claude-binary.ts
|
|
127316
127767
|
init_esm_shims();
|
|
@@ -127362,7 +127813,7 @@ function ensureClaudeBinary(deps = {}) {
|
|
|
127362
127813
|
// src/loops/concurrency.ts
|
|
127363
127814
|
init_esm_shims();
|
|
127364
127815
|
import * as realFs4 from "fs";
|
|
127365
|
-
import { join as
|
|
127816
|
+
import { join as join23 } from "path";
|
|
127366
127817
|
var DEFAULT_MAX_CONCURRENT_FIRES = 6;
|
|
127367
127818
|
var DEFAULT_HEARTBEAT_STALE_MS = 10 * 6e4;
|
|
127368
127819
|
function pidIsAlive(pid) {
|
|
@@ -127386,14 +127837,14 @@ function liveFires(loopRoot, deps = {}) {
|
|
|
127386
127837
|
const staleMs = deps.staleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
|
|
127387
127838
|
let entries;
|
|
127388
127839
|
try {
|
|
127389
|
-
entries = fs.readdirSync(
|
|
127840
|
+
entries = fs.readdirSync(join23(loopRoot, "fires"));
|
|
127390
127841
|
} catch {
|
|
127391
127842
|
return [];
|
|
127392
127843
|
}
|
|
127393
127844
|
const out = [];
|
|
127394
127845
|
for (const entry of entries) {
|
|
127395
127846
|
try {
|
|
127396
|
-
const beat =
|
|
127847
|
+
const beat = join23(loopRoot, "fires", String(entry), ".state", "fire.running");
|
|
127397
127848
|
const st = fs.statSync(beat);
|
|
127398
127849
|
const heartbeatAgeMs = now() - Number(st.mtimeMs);
|
|
127399
127850
|
if (!(heartbeatAgeMs <= staleMs)) continue;
|
|
@@ -127421,7 +127872,7 @@ var SHIPBACK_SIGNAL_TIMEOUT_MS = 8e3;
|
|
|
127421
127872
|
function preserveRefusedConstraints(slug, fireId, body) {
|
|
127422
127873
|
try {
|
|
127423
127874
|
const path2 = refusedConstraintsPath(slug, fireId);
|
|
127424
|
-
|
|
127875
|
+
mkdirSync11(dirname8(path2), { recursive: true, mode: 448 });
|
|
127425
127876
|
writeFileSync12(path2, body, { mode: 384 });
|
|
127426
127877
|
return { path: path2 };
|
|
127427
127878
|
} catch (err) {
|
|
@@ -127500,8 +127951,8 @@ async function runLoop(loopId, opts = {}) {
|
|
|
127500
127951
|
console.log(
|
|
127501
127952
|
`Loop ${loop2.slug}: ${est.steps} steps (${est.paidSteps} paid), est \u20AC${est.estCostEur ?? "?"} \u2014 ${capLabel}`
|
|
127502
127953
|
);
|
|
127503
|
-
const statusPath =
|
|
127504
|
-
const statusBefore =
|
|
127954
|
+
const statusPath = join24(dir, "STATUS.md");
|
|
127955
|
+
const statusBefore = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
|
|
127505
127956
|
writeFileSync12(
|
|
127506
127957
|
statusPath,
|
|
127507
127958
|
statusBefore.replace(
|
|
@@ -127560,8 +128011,8 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127560
128011
|
}
|
|
127561
128012
|
async function shipConstraints() {
|
|
127562
128013
|
try {
|
|
127563
|
-
const constraintsPath =
|
|
127564
|
-
const materializedConstraints =
|
|
128014
|
+
const constraintsPath = join24(dir, "CONSTRAINTS.md");
|
|
128015
|
+
const materializedConstraints = existsSync16(constraintsPath) ? readFileSync15(constraintsPath, "utf-8") : void 0;
|
|
127565
128016
|
if (materializedConstraints === void 0) return;
|
|
127566
128017
|
const boot = loop2.constraintsMd ?? "";
|
|
127567
128018
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
@@ -127636,7 +128087,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127636
128087
|
void Promise.race([shipBackConstraints().then(() => "shipped"), deadline]).then((outcome) => {
|
|
127637
128088
|
if (outcome === "timeout") {
|
|
127638
128089
|
console.error(
|
|
127639
|
-
`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 ${
|
|
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.`
|
|
127640
128091
|
);
|
|
127641
128092
|
}
|
|
127642
128093
|
}).finally(() => process.exit(130));
|
|
@@ -127672,7 +128123,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127672
128123
|
startedAtEpochMs
|
|
127673
128124
|
});
|
|
127674
128125
|
}
|
|
127675
|
-
const statusAfter =
|
|
128126
|
+
const statusAfter = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
|
|
127676
128127
|
const clean2 = exitCode === 0 && /(vision\s*done|queue\s*drained|status:\s*done)/i.test(statusAfter) && !/(brake|crash|crashed|errored)/i.test(statusAfter);
|
|
127677
128128
|
await shipBackConstraints();
|
|
127678
128129
|
process.off("SIGINT", onSignal);
|
|
@@ -127710,9 +128161,9 @@ async function showLoop(loopId) {
|
|
|
127710
128161
|
// src/loops/schedule.ts
|
|
127711
128162
|
init_esm_shims();
|
|
127712
128163
|
init_paths();
|
|
127713
|
-
import { writeFileSync as writeFileSync13, mkdirSync as
|
|
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";
|
|
127714
128165
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
127715
|
-
import { join as
|
|
128166
|
+
import { join as join25, dirname as dirname9 } from "path";
|
|
127716
128167
|
import { homedir as homedir14 } from "os";
|
|
127717
128168
|
var LOOP_KIND = {
|
|
127718
128169
|
labelPrefix: "xyz.ametyst.loop.",
|
|
@@ -127723,7 +128174,7 @@ var LOOP_KIND = {
|
|
|
127723
128174
|
return args;
|
|
127724
128175
|
},
|
|
127725
128176
|
stateDir(slug, cwd) {
|
|
127726
|
-
return
|
|
128177
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
|
|
127727
128178
|
}
|
|
127728
128179
|
};
|
|
127729
128180
|
var TASK_KIND = {
|
|
@@ -127735,7 +128186,7 @@ var TASK_KIND = {
|
|
|
127735
128186
|
return args;
|
|
127736
128187
|
},
|
|
127737
128188
|
stateDir(slug, cwd) {
|
|
127738
|
-
return
|
|
128189
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
|
|
127739
128190
|
}
|
|
127740
128191
|
};
|
|
127741
128192
|
var COMPOUND_KIND = {
|
|
@@ -127747,7 +128198,7 @@ var COMPOUND_KIND = {
|
|
|
127747
128198
|
return args;
|
|
127748
128199
|
},
|
|
127749
128200
|
stateDir(slug, cwd) {
|
|
127750
|
-
return
|
|
128201
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "compounds", safeSlug2(slug, this.noun), ".state");
|
|
127751
128202
|
}
|
|
127752
128203
|
};
|
|
127753
128204
|
function safeSlug2(slug, noun) {
|
|
@@ -127774,7 +128225,7 @@ function parseAt(at) {
|
|
|
127774
128225
|
return { hour, minute };
|
|
127775
128226
|
}
|
|
127776
128227
|
function plistPath(kind, home, slug) {
|
|
127777
|
-
return
|
|
128228
|
+
return join25(home, "Library", "LaunchAgents", `${label(kind, slug)}.plist`);
|
|
127778
128229
|
}
|
|
127779
128230
|
function launchctl(args) {
|
|
127780
128231
|
try {
|
|
@@ -127790,7 +128241,7 @@ function isLoaded(lbl) {
|
|
|
127790
128241
|
}
|
|
127791
128242
|
function readInteractiveDefaultModel(home) {
|
|
127792
128243
|
try {
|
|
127793
|
-
const raw =
|
|
128244
|
+
const raw = readFileSync16(join25(home, ".claude", "settings.json"), "utf-8");
|
|
127794
128245
|
const model = JSON.parse(String(raw)).model;
|
|
127795
128246
|
return typeof model === "string" && model.trim() ? model.trim() : void 0;
|
|
127796
128247
|
} catch {
|
|
@@ -127892,9 +128343,9 @@ ${progArgs}
|
|
|
127892
128343
|
${envEntries}
|
|
127893
128344
|
</dict>
|
|
127894
128345
|
<key>StandardOutPath</key>
|
|
127895
|
-
<string>${escapeXml(
|
|
128346
|
+
<string>${escapeXml(join25(stateDir, LAUNCHD_OUT))}</string>
|
|
127896
128347
|
<key>StandardErrorPath</key>
|
|
127897
|
-
<string>${escapeXml(
|
|
128348
|
+
<string>${escapeXml(join25(stateDir, LAUNCHD_ERR))}</string>
|
|
127898
128349
|
<key>AbandonProcessGroup</key>
|
|
127899
128350
|
<true/>
|
|
127900
128351
|
${scheduleBlock}
|
|
@@ -127960,8 +128411,8 @@ function schedule(kind, slug, opts = {}) {
|
|
|
127960
128411
|
const path2 = plistPath(kind, home, slug);
|
|
127961
128412
|
const stateDir2 = kind.stateDir(slug, cwd);
|
|
127962
128413
|
assertWritable(cwd, "working directory");
|
|
127963
|
-
|
|
127964
|
-
|
|
128414
|
+
mkdirSync12(dirname9(path2), { recursive: true });
|
|
128415
|
+
mkdirSync12(stateDir2, { recursive: true });
|
|
127965
128416
|
assertWritable(stateDir2, "log directory");
|
|
127966
128417
|
writeFileSync13(path2, plistXml(lbl, args, scheduleBlock, cwd, jobEnv, stateDir2), { mode: 384 });
|
|
127967
128418
|
if (isLoaded(lbl)) {
|
|
@@ -127984,7 +128435,7 @@ launchctl said: ${loaded.output.trim()}` : "")
|
|
|
127984
128435
|
}
|
|
127985
128436
|
const stateDir = kind.stateDir(slug, cwd);
|
|
127986
128437
|
assertWritable(cwd, "working directory");
|
|
127987
|
-
|
|
128438
|
+
mkdirSync12(stateDir, { recursive: true });
|
|
127988
128439
|
assertWritable(stateDir, "log directory");
|
|
127989
128440
|
const cronEnv = { PATH: envPath };
|
|
127990
128441
|
if (model) cronEnv.ANTHROPIC_MODEL = model;
|
|
@@ -128017,9 +128468,9 @@ function list(kind, opts = {}) {
|
|
|
128017
128468
|
const home = opts.home ?? homedir14();
|
|
128018
128469
|
const prefix = kind.labelPrefix;
|
|
128019
128470
|
if (platform === "darwin") {
|
|
128020
|
-
const dir =
|
|
128021
|
-
if (!
|
|
128022
|
-
return
|
|
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));
|
|
128023
128474
|
}
|
|
128024
128475
|
return readCrontab().split("\n").filter((l) => l.includes(`# ${prefix}`)).map((l) => l.trimEnd().slice(l.trimEnd().lastIndexOf(prefix) + prefix.length));
|
|
128025
128476
|
}
|
|
@@ -128079,13 +128530,13 @@ function listEntries(kind, opts = {}) {
|
|
|
128079
128530
|
const home = opts.home ?? homedir14();
|
|
128080
128531
|
const prefix = kind.labelPrefix;
|
|
128081
128532
|
if (platform === "darwin") {
|
|
128082
|
-
const dir =
|
|
128083
|
-
if (!
|
|
128084
|
-
return
|
|
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) => {
|
|
128085
128536
|
const slug = f.slice(prefix.length, -".plist".length);
|
|
128086
128537
|
let envKeys = [];
|
|
128087
128538
|
try {
|
|
128088
|
-
envKeys = plistEnvKeys(String(
|
|
128539
|
+
envKeys = plistEnvKeys(String(readFileSync16(join25(dir, f), "utf-8")));
|
|
128089
128540
|
} catch {
|
|
128090
128541
|
envKeys = [];
|
|
128091
128542
|
}
|