@ametyst/cli 0.3.5 → 0.3.7
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 +725 -178
- 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,236 @@ 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
|
+
MissingGrantKeyError: () => MissingGrantKeyError,
|
|
112030
|
+
buildGrantVault: () => buildGrantVault,
|
|
112031
|
+
createGrantKeyIssuer: () => createGrantKeyIssuer,
|
|
112032
|
+
deleteGrantEntry: () => deleteGrantEntry,
|
|
112033
|
+
ensureGrantsDir: () => ensureGrantsDir,
|
|
112034
|
+
generateGrantKey: () => generateGrantKey,
|
|
112035
|
+
grantEntryPath: () => grantEntryPath,
|
|
112036
|
+
grantsDir: () => grantsDir,
|
|
112037
|
+
hasGrantSigningKey: () => hasGrantSigningKey,
|
|
112038
|
+
isMissingGrantKeyError: () => isMissingGrantKeyError,
|
|
112039
|
+
listGrantAddresses: () => listGrantAddresses,
|
|
112040
|
+
listGrantEntries: () => listGrantEntries,
|
|
112041
|
+
missingGrantKeyMessage: () => missingGrantKeyMessage,
|
|
112042
|
+
normalizeGrantAddress: () => normalizeGrantAddress,
|
|
112043
|
+
parseGrantEntry: () => parseGrantEntry,
|
|
112044
|
+
persistGrantKey: () => persistGrantKey,
|
|
112045
|
+
readGrantEntry: () => readGrantEntry,
|
|
112046
|
+
recordGrantRowId: () => recordGrantRowId,
|
|
112047
|
+
resolveGrantSigningKey: () => resolveGrantSigningKey
|
|
112048
|
+
});
|
|
112049
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
112050
|
+
import { join as join6 } from "path";
|
|
112051
|
+
function grantsDir() {
|
|
112052
|
+
return join6(WALLETS_DIR, "grants");
|
|
112053
|
+
}
|
|
112054
|
+
function resolveDir(dir) {
|
|
112055
|
+
return dir ?? grantsDir();
|
|
112056
|
+
}
|
|
112057
|
+
function ensureGrantsDir(dir) {
|
|
112058
|
+
const target = resolveDir(dir);
|
|
112059
|
+
if (target === grantsDir()) ensureDirectories();
|
|
112060
|
+
if (!existsSync7(target)) mkdirSync6(target, { mode: 448, recursive: true });
|
|
112061
|
+
return target;
|
|
112062
|
+
}
|
|
112063
|
+
function normalizeGrantAddress(address) {
|
|
112064
|
+
if (typeof address !== "string" || !ADDRESS_RE2.test(address)) {
|
|
112065
|
+
throw new Error(`Not a 0x-hex EOA address: ${JSON.stringify(address)}`);
|
|
112066
|
+
}
|
|
112067
|
+
return address.toLowerCase();
|
|
112068
|
+
}
|
|
112069
|
+
function grantEntryPath(address, dir) {
|
|
112070
|
+
return join6(resolveDir(dir), `${normalizeGrantAddress(address)}.json`);
|
|
112071
|
+
}
|
|
112072
|
+
function generateGrantKey() {
|
|
112073
|
+
const derived = derivePlaintextWallet(generatePrivateKey());
|
|
112074
|
+
return { address: derived.address.toLowerCase(), privateKey: derived.privateKey };
|
|
112075
|
+
}
|
|
112076
|
+
function buildGrantVault(address, privateKey, options) {
|
|
112077
|
+
if (options.encrypted) {
|
|
112078
|
+
if (!options.passphrase) {
|
|
112079
|
+
throw new Error(
|
|
112080
|
+
"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."
|
|
112081
|
+
);
|
|
112082
|
+
}
|
|
112083
|
+
return JSON.parse(importWallet(privateKey, options.passphrase));
|
|
112084
|
+
}
|
|
112085
|
+
return JSON.parse(serializePlaintextVault(address, privateKey));
|
|
112086
|
+
}
|
|
112087
|
+
function persistGrantKey(params, dir) {
|
|
112088
|
+
const address = normalizeGrantAddress(params.address);
|
|
112089
|
+
const target = ensureGrantsDir(dir);
|
|
112090
|
+
const entry = {
|
|
112091
|
+
format: GRANT_ENTRY_FORMAT,
|
|
112092
|
+
address,
|
|
112093
|
+
createdAt: params.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
112094
|
+
workspaceId: params.workspaceId ?? null,
|
|
112095
|
+
policyId: params.policyId ?? null,
|
|
112096
|
+
backendWalletRowId: params.backendWalletRowId ?? null,
|
|
112097
|
+
vault: buildGrantVault(address, params.privateKey, {
|
|
112098
|
+
encrypted: params.encrypted,
|
|
112099
|
+
passphrase: params.passphrase
|
|
112100
|
+
})
|
|
112101
|
+
};
|
|
112102
|
+
writeSecretFileAtomic(join6(target, `${address}.json`), JSON.stringify(entry, null, 2));
|
|
112103
|
+
return entry;
|
|
112104
|
+
}
|
|
112105
|
+
function parseGrantEntry(json) {
|
|
112106
|
+
let parsed;
|
|
112107
|
+
try {
|
|
112108
|
+
parsed = JSON.parse(json);
|
|
112109
|
+
} catch {
|
|
112110
|
+
return null;
|
|
112111
|
+
}
|
|
112112
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
112113
|
+
const p = parsed;
|
|
112114
|
+
if (p.format !== GRANT_ENTRY_FORMAT) return null;
|
|
112115
|
+
if (typeof p.address !== "string" || !ADDRESS_RE2.test(p.address)) return null;
|
|
112116
|
+
if (p.vault === null || typeof p.vault !== "object" || Array.isArray(p.vault)) return null;
|
|
112117
|
+
return {
|
|
112118
|
+
format: GRANT_ENTRY_FORMAT,
|
|
112119
|
+
address: p.address.toLowerCase(),
|
|
112120
|
+
createdAt: typeof p.createdAt === "string" ? p.createdAt : "",
|
|
112121
|
+
workspaceId: typeof p.workspaceId === "string" ? p.workspaceId : null,
|
|
112122
|
+
policyId: typeof p.policyId === "number" ? p.policyId : null,
|
|
112123
|
+
backendWalletRowId: typeof p.backendWalletRowId === "string" ? p.backendWalletRowId : null,
|
|
112124
|
+
vault: p.vault
|
|
112125
|
+
};
|
|
112126
|
+
}
|
|
112127
|
+
function readGrantEntry(address, dir) {
|
|
112128
|
+
let path2;
|
|
112129
|
+
try {
|
|
112130
|
+
path2 = grantEntryPath(address, dir);
|
|
112131
|
+
} catch {
|
|
112132
|
+
return null;
|
|
112133
|
+
}
|
|
112134
|
+
try {
|
|
112135
|
+
return parseGrantEntry(readFileSync7(path2, "utf-8"));
|
|
112136
|
+
} catch {
|
|
112137
|
+
return null;
|
|
112138
|
+
}
|
|
112139
|
+
}
|
|
112140
|
+
function listGrantEntries(dir) {
|
|
112141
|
+
const target = resolveDir(dir);
|
|
112142
|
+
let names;
|
|
112143
|
+
try {
|
|
112144
|
+
names = readdirSync4(target);
|
|
112145
|
+
} catch {
|
|
112146
|
+
return [];
|
|
112147
|
+
}
|
|
112148
|
+
const entries = [];
|
|
112149
|
+
for (const name of names) {
|
|
112150
|
+
if (!name.endsWith(".json")) continue;
|
|
112151
|
+
let raw;
|
|
112152
|
+
try {
|
|
112153
|
+
raw = readFileSync7(join6(target, name), "utf-8");
|
|
112154
|
+
} catch {
|
|
112155
|
+
continue;
|
|
112156
|
+
}
|
|
112157
|
+
const entry = parseGrantEntry(raw);
|
|
112158
|
+
if (entry) entries.push(entry);
|
|
112159
|
+
}
|
|
112160
|
+
return entries;
|
|
112161
|
+
}
|
|
112162
|
+
function listGrantAddresses(options = {}, dir) {
|
|
112163
|
+
const active = options.workspaceId ?? null;
|
|
112164
|
+
return listGrantEntries(dir).filter((e) => active === null || e.workspaceId === null || e.workspaceId === active).map((e) => e.address);
|
|
112165
|
+
}
|
|
112166
|
+
function recordGrantRowId(address, rowId, dir) {
|
|
112167
|
+
const entry = readGrantEntry(address, dir);
|
|
112168
|
+
if (!entry) return false;
|
|
112169
|
+
try {
|
|
112170
|
+
const next = { ...entry, backendWalletRowId: rowId ?? null };
|
|
112171
|
+
writeSecretFileAtomic(grantEntryPath(address, dir), JSON.stringify(next, null, 2));
|
|
112172
|
+
return true;
|
|
112173
|
+
} catch {
|
|
112174
|
+
return false;
|
|
112175
|
+
}
|
|
112176
|
+
}
|
|
112177
|
+
function deleteGrantEntry(address, dir) {
|
|
112178
|
+
try {
|
|
112179
|
+
unlinkSync5(grantEntryPath(address, dir));
|
|
112180
|
+
} catch {
|
|
112181
|
+
}
|
|
112182
|
+
}
|
|
112183
|
+
function missingGrantKeyMessage(address) {
|
|
112184
|
+
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.`;
|
|
112185
|
+
}
|
|
112186
|
+
function isMissingGrantKeyError(err) {
|
|
112187
|
+
return err instanceof MissingGrantKeyError;
|
|
112188
|
+
}
|
|
112189
|
+
function hasGrantSigningKey(params) {
|
|
112190
|
+
let address;
|
|
112191
|
+
try {
|
|
112192
|
+
address = normalizeGrantAddress(params.address);
|
|
112193
|
+
} catch {
|
|
112194
|
+
return false;
|
|
112195
|
+
}
|
|
112196
|
+
if (readGrantEntry(address, params.dir)) return true;
|
|
112197
|
+
return typeof params.vaultAddress === "string" && params.vaultAddress.toLowerCase() === address;
|
|
112198
|
+
}
|
|
112199
|
+
function resolveGrantSigningKey(params) {
|
|
112200
|
+
const address = normalizeGrantAddress(params.address);
|
|
112201
|
+
const entry = readGrantEntry(address, params.dir);
|
|
112202
|
+
if (entry) {
|
|
112203
|
+
return readVaultPrivateKey(JSON.stringify(entry.vault), params.passphrase);
|
|
112204
|
+
}
|
|
112205
|
+
const vaultAddress = params.vaultAddress;
|
|
112206
|
+
if (params.vaultJson && typeof vaultAddress === "string" && vaultAddress.toLowerCase() === address) {
|
|
112207
|
+
return readVaultPrivateKey(params.vaultJson, params.passphrase);
|
|
112208
|
+
}
|
|
112209
|
+
throw new MissingGrantKeyError(address);
|
|
112210
|
+
}
|
|
112211
|
+
function createGrantKeyIssuer(options) {
|
|
112212
|
+
return {
|
|
112213
|
+
issue({ policyId }) {
|
|
112214
|
+
const { address, privateKey } = generateGrantKey();
|
|
112215
|
+
persistGrantKey(
|
|
112216
|
+
{
|
|
112217
|
+
address,
|
|
112218
|
+
privateKey,
|
|
112219
|
+
encrypted: options.encrypted,
|
|
112220
|
+
passphrase: options.passphrase,
|
|
112221
|
+
workspaceId: options.workspaceId ?? null,
|
|
112222
|
+
policyId
|
|
112223
|
+
},
|
|
112224
|
+
options.dir
|
|
112225
|
+
);
|
|
112226
|
+
return address;
|
|
112227
|
+
},
|
|
112228
|
+
recordRow(address, rowId) {
|
|
112229
|
+
recordGrantRowId(address, rowId, options.dir);
|
|
112230
|
+
}
|
|
112231
|
+
};
|
|
112232
|
+
}
|
|
112233
|
+
var GRANT_ENTRY_FORMAT, ADDRESS_RE2, MissingGrantKeyError;
|
|
112234
|
+
var init_grant_keystore = __esm({
|
|
112235
|
+
"src/wallet/grant-keystore.ts"() {
|
|
112236
|
+
"use strict";
|
|
112237
|
+
init_esm_shims();
|
|
112238
|
+
init_accounts();
|
|
112239
|
+
init_paths();
|
|
112240
|
+
init_core();
|
|
112241
|
+
init_plaintext_vault();
|
|
112242
|
+
GRANT_ENTRY_FORMAT = "grant-key-v1";
|
|
112243
|
+
ADDRESS_RE2 = /^0x[0-9a-fA-F]{40}$/;
|
|
112244
|
+
MissingGrantKeyError = class extends Error {
|
|
112245
|
+
grantAddress;
|
|
112246
|
+
constructor(address) {
|
|
112247
|
+
super(missingGrantKeyMessage(address));
|
|
112248
|
+
this.name = "MissingGrantKeyError";
|
|
112249
|
+
this.grantAddress = address;
|
|
112250
|
+
}
|
|
112251
|
+
};
|
|
112252
|
+
}
|
|
112253
|
+
});
|
|
112254
|
+
|
|
112016
112255
|
// src/commands/init-claim.ts
|
|
112017
112256
|
var init_claim_exports = {};
|
|
112018
112257
|
__export(init_claim_exports, {
|
|
@@ -112026,7 +112265,7 @@ __export(init_claim_exports, {
|
|
|
112026
112265
|
vaultBackupPath: () => vaultBackupPath,
|
|
112027
112266
|
vaultBackupSuffix: () => vaultBackupSuffix
|
|
112028
112267
|
});
|
|
112029
|
-
import { chmodSync as chmodSync3, constants as fsConstants, copyFileSync as copyFileSync3, existsSync as
|
|
112268
|
+
import { chmodSync as chmodSync3, constants as fsConstants, copyFileSync as copyFileSync3, existsSync as existsSync8 } from "fs";
|
|
112030
112269
|
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
112031
112270
|
function claimErrorMessage(failure) {
|
|
112032
112271
|
switch (failure.kind) {
|
|
@@ -112169,7 +112408,7 @@ function vaultBackupPath(vaultPath, at) {
|
|
|
112169
112408
|
return `${vaultPath}.bak-${vaultBackupSuffix(at)}`;
|
|
112170
112409
|
}
|
|
112171
112410
|
function backupVaultFile(vaultPath, at = /* @__PURE__ */ new Date()) {
|
|
112172
|
-
if (!
|
|
112411
|
+
if (!existsSync8(vaultPath)) return null;
|
|
112173
112412
|
const target = vaultBackupPath(vaultPath, at);
|
|
112174
112413
|
try {
|
|
112175
112414
|
copyFileSync3(vaultPath, target, fsConstants.COPYFILE_EXCL);
|
|
@@ -112197,13 +112436,13 @@ var init_version5 = __esm({
|
|
|
112197
112436
|
"src/version.ts"() {
|
|
112198
112437
|
"use strict";
|
|
112199
112438
|
init_esm_shims();
|
|
112200
|
-
CLI_VERSION = true ? "0.3.
|
|
112439
|
+
CLI_VERSION = true ? "0.3.7" : "0.0.0-dev";
|
|
112201
112440
|
}
|
|
112202
112441
|
});
|
|
112203
112442
|
|
|
112204
112443
|
// src/connections/catalog.ts
|
|
112205
|
-
import { existsSync as
|
|
112206
|
-
import { join as
|
|
112444
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
112445
|
+
import { join as join7 } from "path";
|
|
112207
112446
|
function declaredVerbsHeader() {
|
|
112208
112447
|
return SUPPORTED_HTTP_METHODS.join(",");
|
|
112209
112448
|
}
|
|
@@ -112459,8 +112698,8 @@ function parseConnectorsResponseDetailed(body, onWarn = warnToStderr) {
|
|
|
112459
112698
|
function readCachedCatalogStatus() {
|
|
112460
112699
|
let raw;
|
|
112461
112700
|
try {
|
|
112462
|
-
if (!
|
|
112463
|
-
raw = JSON.parse(
|
|
112701
|
+
if (!existsSync9(CONNECTOR_CACHE_PATH)) return { state: "absent" };
|
|
112702
|
+
raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
|
|
112464
112703
|
} catch (e) {
|
|
112465
112704
|
return { state: "unreadable", reason: e instanceof Error ? e.message : String(e) };
|
|
112466
112705
|
}
|
|
@@ -112496,8 +112735,8 @@ function readMirrorRowsFromDisk() {
|
|
|
112496
112735
|
}
|
|
112497
112736
|
function readMirrorContainer() {
|
|
112498
112737
|
try {
|
|
112499
|
-
if (!
|
|
112500
|
-
const raw = JSON.parse(
|
|
112738
|
+
if (!existsSync9(CONNECTOR_CACHE_PATH)) return { state: "absent" };
|
|
112739
|
+
const raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
|
|
112501
112740
|
if (!isRecord(raw)) {
|
|
112502
112741
|
return { state: "unreadable", reason: "the mirror's top level is not an object" };
|
|
112503
112742
|
}
|
|
@@ -112511,8 +112750,8 @@ function readMirrorContainer() {
|
|
|
112511
112750
|
}
|
|
112512
112751
|
function readMirrorFetchedAt() {
|
|
112513
112752
|
try {
|
|
112514
|
-
if (!
|
|
112515
|
-
const raw = JSON.parse(
|
|
112753
|
+
if (!existsSync9(CONNECTOR_CACHE_PATH)) return "";
|
|
112754
|
+
const raw = JSON.parse(readFileSync8(CONNECTOR_CACHE_PATH, "utf-8"));
|
|
112516
112755
|
if (!isRecord(raw)) return "";
|
|
112517
112756
|
return typeof raw.fetchedAt === "string" ? raw.fetchedAt : "";
|
|
112518
112757
|
} catch {
|
|
@@ -112763,7 +113002,7 @@ var init_catalog = __esm({
|
|
|
112763
113002
|
SOURCE_OPERATORS = ["provider", "third_party", "ametyst"];
|
|
112764
113003
|
SOURCE_PREFERENCE_RULES = ["official-mcp"];
|
|
112765
113004
|
CONNECTOR_FEED_PATH = "/api/v1/virtual-wallets/capabilities/connectors";
|
|
112766
|
-
CONNECTOR_CACHE_PATH =
|
|
113005
|
+
CONNECTOR_CACHE_PATH = join7(AMETYST_DIR, "connectors.json");
|
|
112767
113006
|
CatalogUnavailableError = class extends Error {
|
|
112768
113007
|
constructor(reason, message) {
|
|
112769
113008
|
super(message);
|
|
@@ -113501,15 +113740,15 @@ var init_call2 = __esm({
|
|
|
113501
113740
|
});
|
|
113502
113741
|
|
|
113503
113742
|
// src/connections/engine-store.ts
|
|
113504
|
-
import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as
|
|
113505
|
-
import { dirname as dirname7, join as
|
|
113743
|
+
import { chmodSync as chmodSync4, closeSync as closeSync2, existsSync as existsSync12, mkdirSync as mkdirSync8, openSync as openSync2 } from "fs";
|
|
113744
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
113506
113745
|
function connectionsDbPath() {
|
|
113507
|
-
return
|
|
113746
|
+
return join17(AMETYST_DIR, CONNECTIONS_DB_FILENAME);
|
|
113508
113747
|
}
|
|
113509
113748
|
function ensureOwnerOnlyFile(path2) {
|
|
113510
113749
|
if (path2 === IN_MEMORY_DB) return;
|
|
113511
|
-
|
|
113512
|
-
if (!
|
|
113750
|
+
mkdirSync8(dirname7(path2), { recursive: true, mode: DIR_MODE });
|
|
113751
|
+
if (!existsSync12(path2)) closeSync2(openSync2(path2, "a", CONNECTIONS_DB_FILE_MODE));
|
|
113513
113752
|
chmodSync4(path2, CONNECTIONS_DB_FILE_MODE);
|
|
113514
113753
|
}
|
|
113515
113754
|
function sqliteWasmDriver(db, raw) {
|
|
@@ -114718,6 +114957,44 @@ function hostNudges(rows) {
|
|
|
114718
114957
|
|
|
114719
114958
|
// src/commands/login.ts
|
|
114720
114959
|
init_resolve();
|
|
114960
|
+
|
|
114961
|
+
// src/config/workspace-binding.ts
|
|
114962
|
+
init_esm_shims();
|
|
114963
|
+
init_config();
|
|
114964
|
+
function workspaceKeyFrom(profile) {
|
|
114965
|
+
const name = profile?.companyName;
|
|
114966
|
+
if (typeof name !== "string") return null;
|
|
114967
|
+
const trimmed = name.trim().toLowerCase();
|
|
114968
|
+
return trimmed === "" ? null : trimmed;
|
|
114969
|
+
}
|
|
114970
|
+
function isWorkspaceSwitch(previous, next) {
|
|
114971
|
+
if (previous === null || next === null) return false;
|
|
114972
|
+
return previous !== next;
|
|
114973
|
+
}
|
|
114974
|
+
function readBoundWorkspace() {
|
|
114975
|
+
const config = loadConfig();
|
|
114976
|
+
const key = config?.workspaceId;
|
|
114977
|
+
return typeof key === "string" && key.trim() !== "" ? key : null;
|
|
114978
|
+
}
|
|
114979
|
+
function recordBoundWorkspace(key, observedAt) {
|
|
114980
|
+
const config = loadConfig();
|
|
114981
|
+
if (!config) return false;
|
|
114982
|
+
const { apiKey: _injected, apiKeySource: _src, ...persisted } = config;
|
|
114983
|
+
saveConfig({ ...persisted, workspaceId: key, workspaceIdObservedAt: observedAt });
|
|
114984
|
+
return true;
|
|
114985
|
+
}
|
|
114986
|
+
function workspaceSwitchNotice(previous, next) {
|
|
114987
|
+
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.`;
|
|
114988
|
+
}
|
|
114989
|
+
function applyObservedWorkspace(profile, observedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
114990
|
+
const key = workspaceKeyFrom(profile);
|
|
114991
|
+
const previous = readBoundWorkspace();
|
|
114992
|
+
const switched = isWorkspaceSwitch(previous, key);
|
|
114993
|
+
if (key !== null && key !== previous) recordBoundWorkspace(key, observedAt);
|
|
114994
|
+
return { key, previous, switched };
|
|
114995
|
+
}
|
|
114996
|
+
|
|
114997
|
+
// src/commands/login.ts
|
|
114721
114998
|
function lastObservedStanceLine(config) {
|
|
114722
114999
|
const observed = config?.requireWalletPassphrase;
|
|
114723
115000
|
if (typeof observed !== "boolean") {
|
|
@@ -114779,6 +115056,10 @@ async function loginCommand(options) {
|
|
|
114779
115056
|
if (observedStance !== void 0) {
|
|
114780
115057
|
setRequireWalletPassphrase(observedStance, (/* @__PURE__ */ new Date()).toISOString());
|
|
114781
115058
|
}
|
|
115059
|
+
const workspace = applyObservedWorkspace(profile);
|
|
115060
|
+
if (workspace.switched && workspace.previous && workspace.key) {
|
|
115061
|
+
console.log(workspaceSwitchNotice(workspace.previous, workspace.key));
|
|
115062
|
+
}
|
|
114782
115063
|
const stance = resolveWalletPassphraseStance(observedStance, config.requireWalletPassphrase);
|
|
114783
115064
|
const who = profile ? `Logged in as ${profile.employeeName} @ ${profile.companyName ?? "(no company)"}.` : "Logged in.";
|
|
114784
115065
|
console.log(who);
|
|
@@ -114926,8 +115207,9 @@ async function fetchAvailablePolicies(client, apiKey) {
|
|
|
114926
115207
|
const response = await client.getPoliciesFromVirtualWalletsManager(apiKey);
|
|
114927
115208
|
return normalizePoliciesResponse(response);
|
|
114928
115209
|
}
|
|
114929
|
-
async function
|
|
114930
|
-
|
|
115210
|
+
async function submitRotatedAccessRequest(client, apiKey, policyId, issuer) {
|
|
115211
|
+
const eoaAddress = await issuer.issue({ policyId });
|
|
115212
|
+
const result = await client.createVirtualWallet(
|
|
114931
115213
|
apiKey,
|
|
114932
115214
|
CLI_WALLET_NAME,
|
|
114933
115215
|
CLI_WALLET_URL,
|
|
@@ -114935,6 +115217,11 @@ async function submitAccessRequest(client, apiKey, eoaAddress, policyId) {
|
|
|
114935
115217
|
policyId,
|
|
114936
115218
|
eoaAddress
|
|
114937
115219
|
);
|
|
115220
|
+
try {
|
|
115221
|
+
issuer.recordRow?.(eoaAddress, result?.id ? String(result.id) : void 0);
|
|
115222
|
+
} catch {
|
|
115223
|
+
}
|
|
115224
|
+
return { ...result, eoaAddress };
|
|
114938
115225
|
}
|
|
114939
115226
|
function pickDefaultPolicyIndex(policies) {
|
|
114940
115227
|
const i = policies.findIndex(
|
|
@@ -115133,6 +115420,7 @@ async function freshWalletRecovery(apiKey, options) {
|
|
|
115133
115420
|
}
|
|
115134
115421
|
}
|
|
115135
115422
|
let address;
|
|
115423
|
+
let vaultPassphrase;
|
|
115136
115424
|
if (options.noPassphrase) {
|
|
115137
115425
|
const wallet = createPlaintextWallet();
|
|
115138
115426
|
persistPlaintextVault(wallet.address, wallet.privateKey);
|
|
@@ -115153,6 +115441,7 @@ New wallet created. Address: ${address}`);
|
|
|
115153
115441
|
process.exit(1);
|
|
115154
115442
|
return false;
|
|
115155
115443
|
}
|
|
115444
|
+
vaultPassphrase = passphrase;
|
|
115156
115445
|
const keystoreJson = createWallet(passphrase);
|
|
115157
115446
|
persistVault(keystoreJson);
|
|
115158
115447
|
address = getWalletAddress(keystoreJson);
|
|
@@ -115201,8 +115490,15 @@ Create one in the Ametyst web app (admin panel \u2192 policies), then re-run 'am
|
|
|
115201
115490
|
process.exit(1);
|
|
115202
115491
|
return false;
|
|
115203
115492
|
}
|
|
115493
|
+
let grantAddress;
|
|
115204
115494
|
try {
|
|
115205
|
-
await
|
|
115495
|
+
const { createGrantKeyIssuer: createGrantKeyIssuer2 } = await Promise.resolve().then(() => (init_grant_keystore(), grant_keystore_exports));
|
|
115496
|
+
const submitted = await submitRotatedAccessRequest(client, apiKey, policyId, createGrantKeyIssuer2({
|
|
115497
|
+
encrypted: !options.noPassphrase,
|
|
115498
|
+
passphrase: vaultPassphrase,
|
|
115499
|
+
workspaceId: readBoundWorkspace()
|
|
115500
|
+
}));
|
|
115501
|
+
grantAddress = submitted.eoaAddress;
|
|
115206
115502
|
} catch (e) {
|
|
115207
115503
|
const msg = e instanceof Error ? e.message : String(e);
|
|
115208
115504
|
console.error(`
|
|
@@ -115214,8 +115510,9 @@ Could not submit the authorization request (${msg}).`);
|
|
|
115214
115510
|
const policyName = typeof chosen.name === "string" && chosen.name ? chosen.name : `policy ${policyId}`;
|
|
115215
115511
|
console.log(`
|
|
115216
115512
|
Authorization request sent \u2014 PENDING approval.
|
|
115217
|
-
Wallet:
|
|
115218
|
-
|
|
115513
|
+
Wallet: ${address}
|
|
115514
|
+
Session key: ${grantAddress}
|
|
115515
|
+
Policy: ${policyName} (id ${policyId})
|
|
115219
115516
|
|
|
115220
115517
|
Next step: approve the request in the Ametyst web app admin panel (check your
|
|
115221
115518
|
notifications there). Once it's approved, you're done \u2014 run 'ametyst status' to
|
|
@@ -115437,14 +115734,19 @@ function isNewerGrant(candidate, incumbent) {
|
|
|
115437
115734
|
}
|
|
115438
115735
|
return candidateIdUsable && !incumbentIdUsable;
|
|
115439
115736
|
}
|
|
115440
|
-
function
|
|
115737
|
+
function selectNewestApprovedWalletAmong(wallets, addresses) {
|
|
115441
115738
|
if (!Array.isArray(wallets)) return void 0;
|
|
115442
|
-
const
|
|
115443
|
-
|
|
115739
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
115740
|
+
for (const address of addresses ?? []) {
|
|
115741
|
+
const normalized = nonEmptyLowerCase(address);
|
|
115742
|
+
if (normalized) candidates.add(normalized);
|
|
115743
|
+
}
|
|
115744
|
+
if (candidates.size === 0) return void 0;
|
|
115444
115745
|
let newest;
|
|
115445
115746
|
for (const wallet of wallets) {
|
|
115446
115747
|
if (wallet?.status !== "approved") continue;
|
|
115447
|
-
|
|
115748
|
+
const rowAddress = nonEmptyLowerCase(wallet?.address);
|
|
115749
|
+
if (!rowAddress || !candidates.has(rowAddress)) continue;
|
|
115448
115750
|
if (newest === void 0 || isNewerGrant(wallet, newest)) newest = wallet;
|
|
115449
115751
|
}
|
|
115450
115752
|
return newest;
|
|
@@ -115454,7 +115756,60 @@ function findCurrentApprovedWallet(wallets, eoaAddress, pendingWalletId) {
|
|
|
115454
115756
|
if (pendingWalletId) {
|
|
115455
115757
|
return wallets.find((w) => String(w?.id) === String(pendingWalletId) && w?.status === "approved");
|
|
115456
115758
|
}
|
|
115457
|
-
return
|
|
115759
|
+
return selectNewestApprovedWalletAmong(
|
|
115760
|
+
wallets,
|
|
115761
|
+
Array.isArray(eoaAddress) ? eoaAddress : eoaAddress === void 0 ? [] : [eoaAddress]
|
|
115762
|
+
);
|
|
115763
|
+
}
|
|
115764
|
+
|
|
115765
|
+
// src/mcp-server/signer-binding.ts
|
|
115766
|
+
init_esm_shims();
|
|
115767
|
+
var BINDING_DERIVED_KEYS = [
|
|
115768
|
+
"virtualWalletKernelAccountClient",
|
|
115769
|
+
"permissionPlugin",
|
|
115770
|
+
"kernelDomain",
|
|
115771
|
+
"policyPrecheckPassed"
|
|
115772
|
+
];
|
|
115773
|
+
function nonEmpty(value2) {
|
|
115774
|
+
return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
|
|
115775
|
+
}
|
|
115776
|
+
function sameAddress(a, b) {
|
|
115777
|
+
return String(a ?? "").toLowerCase() === String(b ?? "").toLowerCase();
|
|
115778
|
+
}
|
|
115779
|
+
function signerBindingMoved(state, row) {
|
|
115780
|
+
const rowId = row?.id != null ? String(row.id) : void 0;
|
|
115781
|
+
const rowAddress = nonEmpty(row?.address);
|
|
115782
|
+
if (state.virtualWalletId && rowId && state.virtualWalletId !== rowId) return true;
|
|
115783
|
+
if (state.signerAddress && rowAddress && !sameAddress(state.signerAddress, rowAddress)) return true;
|
|
115784
|
+
return false;
|
|
115785
|
+
}
|
|
115786
|
+
function bindSignerToGrantRow(state, row) {
|
|
115787
|
+
const changed = signerBindingMoved(state, row);
|
|
115788
|
+
if (changed) {
|
|
115789
|
+
for (const key of BINDING_DERIVED_KEYS) delete state[key];
|
|
115790
|
+
}
|
|
115791
|
+
const rowAddress = nonEmpty(row?.address);
|
|
115792
|
+
if (rowAddress) state.signerAddress = rowAddress;
|
|
115793
|
+
const rowWalletAddress = nonEmpty(row?.walletAddress) ?? nonEmpty(row?.kernelAccountAddress);
|
|
115794
|
+
const rowPaymentManagerAddress = nonEmpty(row?.paymentManagerAddress);
|
|
115795
|
+
const rowPolicyId = row?.policyAssociated != null ? String(row.policyAssociated) : void 0;
|
|
115796
|
+
const rowId = row?.id != null ? String(row.id) : void 0;
|
|
115797
|
+
if (changed) {
|
|
115798
|
+
state.walletAddress = rowWalletAddress;
|
|
115799
|
+
state.paymentManagerAddress = rowPaymentManagerAddress;
|
|
115800
|
+
state.policyId = rowPolicyId;
|
|
115801
|
+
state.policyOnchainPermissions = nonEmpty(row?.policyOnchainPermissions);
|
|
115802
|
+
} else {
|
|
115803
|
+
state.walletAddress = rowWalletAddress ?? state.walletAddress;
|
|
115804
|
+
state.paymentManagerAddress = rowPaymentManagerAddress ?? state.paymentManagerAddress;
|
|
115805
|
+
state.policyId = rowPolicyId ?? state.policyId;
|
|
115806
|
+
state.policyOnchainPermissions = nonEmpty(row?.policyOnchainPermissions) ?? state.policyOnchainPermissions;
|
|
115807
|
+
}
|
|
115808
|
+
if (rowId) state.virtualWalletId = rowId;
|
|
115809
|
+
if (row?.policyName != null) state.policyName = String(row.policyName);
|
|
115810
|
+
if (row?.policyValidUntil != null) state.policyValidUntil = BigInt(row.policyValidUntil);
|
|
115811
|
+
state.authorizationStatus = "approved";
|
|
115812
|
+
return { changed, signerAddress: state.signerAddress };
|
|
115458
115813
|
}
|
|
115459
115814
|
|
|
115460
115815
|
// src/mcp-server/start-session.ts
|
|
@@ -115594,7 +115949,10 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115594
115949
|
let activeWallet;
|
|
115595
115950
|
let grantChanged = false;
|
|
115596
115951
|
try {
|
|
115597
|
-
activeWallet =
|
|
115952
|
+
activeWallet = selectNewestApprovedWalletAmong(wallets, [
|
|
115953
|
+
ctx.eoaAddress,
|
|
115954
|
+
...ctx.candidateAddresses ?? []
|
|
115955
|
+
]);
|
|
115598
115956
|
if (activeWallet) {
|
|
115599
115957
|
const newestId = String(activeWallet.id);
|
|
115600
115958
|
const rowWalletAddress = activeWallet.walletAddress || activeWallet.kernelAccountAddress;
|
|
@@ -115614,6 +115972,9 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115614
115972
|
policyId = rowPolicyId ?? policyId;
|
|
115615
115973
|
}
|
|
115616
115974
|
virtualWalletId = newestId;
|
|
115975
|
+
if (typeof activeWallet.address === "string" && activeWallet.address.trim() !== "") {
|
|
115976
|
+
credentials.signerAddress = activeWallet.address;
|
|
115977
|
+
}
|
|
115617
115978
|
credentials.walletAddress = walletAddress;
|
|
115618
115979
|
credentials.virtualWalletId = virtualWalletId;
|
|
115619
115980
|
credentials.paymentManagerAddress = paymentManagerAddress;
|
|
@@ -115646,7 +116007,8 @@ async function runStartSession(ctx, timing = new TimingCollector("start_session"
|
|
|
115646
116007
|
if (walletAddress && virtualWalletId && paymentManagerAddress && policyId) {
|
|
115647
116008
|
let privateKey = null;
|
|
115648
116009
|
try {
|
|
115649
|
-
|
|
116010
|
+
const signerAddress = credentials.signerAddress ?? ctx.eoaAddress;
|
|
116011
|
+
privateKey = ctx.resolveSigningKey && signerAddress ? ctx.resolveSigningKey(signerAddress) : readVaultPrivateKey(ctx.walletKeystoreJson, ctx.passphrase);
|
|
115650
116012
|
const { virtualWalletsManagers: virtualWalletsManagers2, financialAccounts: financialAccounts2 } = ctx.sdk;
|
|
115651
116013
|
const walletData = await virtualWalletsManagers2.getVirtualWalletDataByApiKey(
|
|
115652
116014
|
ctx.apiKey,
|
|
@@ -115761,11 +116123,11 @@ function clearWalletScopedCredentials(target) {
|
|
|
115761
116123
|
delete target[key];
|
|
115762
116124
|
}
|
|
115763
116125
|
}
|
|
115764
|
-
function
|
|
116126
|
+
function sameAddress2(a, b) {
|
|
115765
116127
|
return String(a ?? "").toLowerCase() === String(b ?? "").toLowerCase();
|
|
115766
116128
|
}
|
|
115767
116129
|
function mergeStartSessionCredentials(target, patch, responseKey, options = {}) {
|
|
115768
|
-
const walletChanged = patch.eoaAddress !== void 0 && !
|
|
116130
|
+
const walletChanged = patch.eoaAddress !== void 0 && !sameAddress2(patch.eoaAddress, target.eoaAddress);
|
|
115769
116131
|
if (walletChanged || options.grantChanged) {
|
|
115770
116132
|
clearWalletScopedCredentials(target);
|
|
115771
116133
|
}
|
|
@@ -116946,13 +117308,13 @@ init_local_state();
|
|
|
116946
117308
|
// src/mcp-server/materialize-task.ts
|
|
116947
117309
|
init_esm_shims();
|
|
116948
117310
|
init_paths();
|
|
116949
|
-
import { mkdirSync as
|
|
116950
|
-
import { join as
|
|
117311
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
117312
|
+
import { join as join9 } from "path";
|
|
116951
117313
|
|
|
116952
117314
|
// src/loops/state-docs.ts
|
|
116953
117315
|
init_esm_shims();
|
|
116954
|
-
import { existsSync as
|
|
116955
|
-
import { join as
|
|
117316
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
117317
|
+
import { join as join8 } from "path";
|
|
116956
117318
|
|
|
116957
117319
|
// src/loops/shipback.ts
|
|
116958
117320
|
init_esm_shims();
|
|
@@ -117385,12 +117747,12 @@ function docMergeRefusal(boot, fresh, next, cleanAppend) {
|
|
|
117385
117747
|
async function shipBackStateDocs(sdk, apiKey, slug, dir, boot) {
|
|
117386
117748
|
const outcomes = [];
|
|
117387
117749
|
for (const doc of boot) {
|
|
117388
|
-
const path2 =
|
|
117389
|
-
if (!
|
|
117750
|
+
const path2 = join8(dir, doc.filename);
|
|
117751
|
+
if (!existsSync10(path2)) {
|
|
117390
117752
|
outcomes.push({ key: doc.key, outcome: "unchanged" });
|
|
117391
117753
|
continue;
|
|
117392
117754
|
}
|
|
117393
|
-
const materialized =
|
|
117755
|
+
const materialized = readFileSync9(path2, "utf-8");
|
|
117394
117756
|
const reread = await readScoped(sdk, apiKey, slug, doc.key, doc.scope);
|
|
117395
117757
|
const fresh = reread.status === "ok" ? reread.body : reread.status === "absent" && doc.body === "" ? "" : void 0;
|
|
117396
117758
|
if (fresh === void 0) {
|
|
@@ -117460,20 +117822,20 @@ var TASK_DEFINITION_FILES = [
|
|
|
117460
117822
|
];
|
|
117461
117823
|
function materializeTask(task, runId) {
|
|
117462
117824
|
const dir = loopFireDir(task.slug, runId);
|
|
117463
|
-
|
|
117825
|
+
mkdirSync7(dir, { recursive: true, mode: 448 });
|
|
117464
117826
|
const files = {};
|
|
117465
117827
|
const skipped = [];
|
|
117466
117828
|
for (const [label2, filename, field] of TASK_DEFINITION_FILES) {
|
|
117467
117829
|
const body = task[field];
|
|
117468
117830
|
if (typeof body === "string" && body.length > 0) {
|
|
117469
|
-
writeFileSync8(
|
|
117831
|
+
writeFileSync8(join9(dir, filename), body, { mode: 384 });
|
|
117470
117832
|
files[label2] = filename;
|
|
117471
117833
|
} else {
|
|
117472
117834
|
skipped.push(label2);
|
|
117473
117835
|
}
|
|
117474
117836
|
}
|
|
117475
117837
|
writeFileSync8(
|
|
117476
|
-
|
|
117838
|
+
join9(dir, "STATUS.md"),
|
|
117477
117839
|
`# STATUS \u2014 ${task.slug}
|
|
117478
117840
|
|
|
117479
117841
|
task_id: ${task.id}
|
|
@@ -117504,7 +117866,7 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
|
|
|
117504
117866
|
notes.push(`memory doc '${f.key}' not materialized: ${f.reason}`);
|
|
117505
117867
|
}
|
|
117506
117868
|
for (const doc of fetched) {
|
|
117507
|
-
writeFileSync8(
|
|
117869
|
+
writeFileSync8(join9(dir, doc.filename), doc.body, { mode: 384 });
|
|
117508
117870
|
files[`doc:${doc.key}`] = doc.filename;
|
|
117509
117871
|
notes.push(describeSource(doc));
|
|
117510
117872
|
}
|
|
@@ -117518,14 +117880,14 @@ async function materializeMemoryDocs(sdk, apiKey, task, dir) {
|
|
|
117518
117880
|
}
|
|
117519
117881
|
|
|
117520
117882
|
// src/mcp-server/index.ts
|
|
117521
|
-
import { existsSync as
|
|
117883
|
+
import { existsSync as existsSync13 } from "fs";
|
|
117522
117884
|
|
|
117523
117885
|
// src/loops/dashboard.ts
|
|
117524
117886
|
init_esm_shims();
|
|
117525
117887
|
import { createServer as createServer2 } from "http";
|
|
117526
117888
|
import * as realFs from "fs";
|
|
117527
117889
|
import { spawn as realSpawn } from "child_process";
|
|
117528
|
-
import { join as
|
|
117890
|
+
import { join as join10 } from "path";
|
|
117529
117891
|
var DEFAULT_PORT = 4477;
|
|
117530
117892
|
var DASHBOARD_PORT_ENV = "AMETYST_LOOP_DASHBOARD_PORT";
|
|
117531
117893
|
var DASHBOARD_NO_OPEN_ENV = "AMETYST_DASHBOARD_NO_OPEN";
|
|
@@ -117586,14 +117948,14 @@ function startDashboardServer(args) {
|
|
|
117586
117948
|
const data = {};
|
|
117587
117949
|
for (const name of files) {
|
|
117588
117950
|
try {
|
|
117589
|
-
const p =
|
|
117951
|
+
const p = join10(args.loopDir, name);
|
|
117590
117952
|
if (fs.existsSync(p)) data[name] = fs.readFileSync(p, "utf-8");
|
|
117591
117953
|
} catch {
|
|
117592
117954
|
}
|
|
117593
117955
|
}
|
|
117594
117956
|
const state = {};
|
|
117595
117957
|
try {
|
|
117596
|
-
const p =
|
|
117958
|
+
const p = join10(args.loopDir, ".state", "fires.jsonl");
|
|
117597
117959
|
if (fs.existsSync(p)) state["fires.jsonl"] = fs.readFileSync(p, "utf-8");
|
|
117598
117960
|
} catch {
|
|
117599
117961
|
}
|
|
@@ -117992,7 +118354,7 @@ function injectDefaultDashboard(loop2) {
|
|
|
117992
118354
|
|
|
117993
118355
|
// src/loops/memory-verbs.ts
|
|
117994
118356
|
init_esm_shims();
|
|
117995
|
-
import { readFileSync as
|
|
118357
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
117996
118358
|
|
|
117997
118359
|
// src/loops/sdk.ts
|
|
117998
118360
|
init_esm_shims();
|
|
@@ -118252,7 +118614,7 @@ async function taskMemoryAppendVerb(rawSlug, opts) {
|
|
|
118252
118614
|
const archived = opts.archived === true;
|
|
118253
118615
|
const note = opts.note?.trim() || void 0;
|
|
118254
118616
|
const scope = parseScopeFlag(opts.scope, false);
|
|
118255
|
-
const content = opts.file !== void 0 ?
|
|
118617
|
+
const content = opts.file !== void 0 ? readFileSync10(opts.file, "utf-8") : opts.content ?? "";
|
|
118256
118618
|
if (!content) {
|
|
118257
118619
|
throw new Error("nothing to store: pass --content <text> or --file <path>");
|
|
118258
118620
|
}
|
|
@@ -118389,14 +118751,14 @@ ${full.markdownBody ?? ""}`;
|
|
|
118389
118751
|
|
|
118390
118752
|
// src/mcp-server/read-file-body.ts
|
|
118391
118753
|
init_esm_shims();
|
|
118392
|
-
import { readFileSync as
|
|
118754
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
118393
118755
|
import { resolve as resolvePath } from "path";
|
|
118394
118756
|
function readMarkdownFile(filePath) {
|
|
118395
118757
|
const raw = typeof filePath === "string" ? filePath.trim() : "";
|
|
118396
118758
|
if (!raw) throw new Error("filePath is empty.");
|
|
118397
118759
|
const abs = resolvePath(raw);
|
|
118398
118760
|
try {
|
|
118399
|
-
return
|
|
118761
|
+
return readFileSync11(abs, "utf8");
|
|
118400
118762
|
} catch (err) {
|
|
118401
118763
|
const reason = err instanceof Error ? err.message : String(err);
|
|
118402
118764
|
throw new Error(`Could not read file at "${abs}": ${reason}`);
|
|
@@ -118725,7 +119087,7 @@ function enforceResponseCap(payload) {
|
|
|
118725
119087
|
|
|
118726
119088
|
// src/mcp-server/delegate-tools.ts
|
|
118727
119089
|
init_esm_shims();
|
|
118728
|
-
import { existsSync as
|
|
119090
|
+
import { existsSync as existsSync11, statSync as statSync2 } from "fs";
|
|
118729
119091
|
import { z as z3 } from "zod";
|
|
118730
119092
|
|
|
118731
119093
|
// src/delegate/jobs.ts
|
|
@@ -118827,7 +119189,7 @@ import * as nodeFs from "fs";
|
|
|
118827
119189
|
import { execFileSync } from "child_process";
|
|
118828
119190
|
import { createHash } from "crypto";
|
|
118829
119191
|
import { homedir as homedir5 } from "os";
|
|
118830
|
-
import { join as
|
|
119192
|
+
import { join as join11 } from "path";
|
|
118831
119193
|
var OPENCODE_VERSION = "1.18.13";
|
|
118832
119194
|
var OPENCODE_CHECKSUMS = {
|
|
118833
119195
|
"darwin-arm64": "6a85ae6de1aeb8e39ae4d977337b03f49168c2a827ee37b6f82c39471d711c63",
|
|
@@ -118849,10 +119211,10 @@ function resolveOpencodePlatform(platform, arch) {
|
|
|
118849
119211
|
return void 0;
|
|
118850
119212
|
}
|
|
118851
119213
|
function opencodeBinDir(home = homedir5()) {
|
|
118852
|
-
return
|
|
119214
|
+
return join11(home, `.ametyst${ENV_SUFFIX}`, "bin");
|
|
118853
119215
|
}
|
|
118854
119216
|
function opencodeBinaryPath(home = homedir5()) {
|
|
118855
|
-
return
|
|
119217
|
+
return join11(opencodeBinDir(home), `opencode-${OPENCODE_VERSION}`);
|
|
118856
119218
|
}
|
|
118857
119219
|
function opencodeSidecarPath(home = homedir5()) {
|
|
118858
119220
|
return `${opencodeBinaryPath(home)}.sha256`;
|
|
@@ -118907,7 +119269,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118907
119269
|
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
118908
119270
|
const expected = (deps.checksums ?? OPENCODE_CHECKSUMS)[key];
|
|
118909
119271
|
const actual = sha256Hex(bytes);
|
|
118910
|
-
const tmpArchive =
|
|
119272
|
+
const tmpArchive = join11(binDir, `.${artifact}.download-${process.pid}`);
|
|
118911
119273
|
fs.writeFileSync(tmpArchive, bytes);
|
|
118912
119274
|
if (actual !== expected) {
|
|
118913
119275
|
fs.rmSync(tmpArchive, { force: true });
|
|
@@ -118915,7 +119277,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118915
119277
|
`opencode bootstrap: checksum mismatch for ${artifact} (expected ${expected}, got ${actual}). The download was deleted; refusing to install an unverified binary.`
|
|
118916
119278
|
);
|
|
118917
119279
|
}
|
|
118918
|
-
const tmpExtractDir =
|
|
119280
|
+
const tmpExtractDir = join11(binDir, `.extract-${OPENCODE_VERSION}-${process.pid}`);
|
|
118919
119281
|
fs.rmSync(tmpExtractDir, { recursive: true, force: true });
|
|
118920
119282
|
fs.mkdirSync(tmpExtractDir, { recursive: true, mode: 448 });
|
|
118921
119283
|
try {
|
|
@@ -118924,7 +119286,7 @@ async function ensureOpencodeBinary(deps = {}) {
|
|
|
118924
119286
|
} else {
|
|
118925
119287
|
exec("tar", ["-xzf", tmpArchive, "-C", tmpExtractDir]);
|
|
118926
119288
|
}
|
|
118927
|
-
const extracted =
|
|
119289
|
+
const extracted = join11(tmpExtractDir, "opencode");
|
|
118928
119290
|
if (!fs.existsSync(extracted)) {
|
|
118929
119291
|
const entries = fs.readdirSync(tmpExtractDir).join(", ") || "<empty>";
|
|
118930
119292
|
throw new Error(
|
|
@@ -118995,15 +119357,15 @@ import { randomUUID } from "crypto";
|
|
|
118995
119357
|
init_esm_shims();
|
|
118996
119358
|
init_paths();
|
|
118997
119359
|
import { homedir as homedir6 } from "os";
|
|
118998
|
-
import { join as
|
|
119360
|
+
import { join as join12 } from "path";
|
|
118999
119361
|
function bridgeRunDir(home = homedir6()) {
|
|
119000
|
-
return
|
|
119362
|
+
return join12(home, `.ametyst${ENV_SUFFIX}`, "run");
|
|
119001
119363
|
}
|
|
119002
119364
|
function bridgeSocketPath(pid, home = homedir6()) {
|
|
119003
|
-
return
|
|
119365
|
+
return join12(bridgeRunDir(home), `delegate-${pid}.sock`);
|
|
119004
119366
|
}
|
|
119005
119367
|
function bridgeNoncePath(pid, home = homedir6()) {
|
|
119006
|
-
return
|
|
119368
|
+
return join12(bridgeRunDir(home), `delegate-${pid}.nonce`);
|
|
119007
119369
|
}
|
|
119008
119370
|
function bridgeNoncePathForSocket(socketPath) {
|
|
119009
119371
|
return socketPath.replace(/\.sock$/, ".nonce");
|
|
@@ -119025,7 +119387,7 @@ function listBridgeSockets(fs, home = homedir6()) {
|
|
|
119025
119387
|
const entries = [];
|
|
119026
119388
|
for (const name of names) {
|
|
119027
119389
|
if (pidFromSocketName(name) === void 0) continue;
|
|
119028
|
-
const path2 =
|
|
119390
|
+
const path2 = join12(dir, name);
|
|
119029
119391
|
try {
|
|
119030
119392
|
entries.push({ path: path2, mtimeMs: fs.statSync(path2).mtimeMs });
|
|
119031
119393
|
} catch {
|
|
@@ -119374,7 +119736,7 @@ async function startShim(deps) {
|
|
|
119374
119736
|
init_esm_shims();
|
|
119375
119737
|
import * as nodeFs3 from "fs";
|
|
119376
119738
|
import { tmpdir } from "os";
|
|
119377
|
-
import { join as
|
|
119739
|
+
import { join as join13 } from "path";
|
|
119378
119740
|
var DELEGATE_PROVIDER_ID = "ametyst";
|
|
119379
119741
|
var DELEGATE_SHIM_API_KEY = "ametyst-local-shim";
|
|
119380
119742
|
function buildDelegateConfig(opts) {
|
|
@@ -119398,8 +119760,8 @@ function delegateModelRef(model) {
|
|
|
119398
119760
|
}
|
|
119399
119761
|
function writeTempDelegateConfig(config, deps = {}) {
|
|
119400
119762
|
const fs = deps.fs ?? nodeFs3;
|
|
119401
|
-
const dir = fs.mkdtempSync(
|
|
119402
|
-
const path2 =
|
|
119763
|
+
const dir = fs.mkdtempSync(join13(deps.tmp ?? tmpdir(), "ametyst-delegate-"));
|
|
119764
|
+
const path2 = join13(dir, "opencode.json");
|
|
119403
119765
|
fs.writeFileSync(path2, `${JSON.stringify(config, null, 2)}
|
|
119404
119766
|
`, { mode: 384 });
|
|
119405
119767
|
let done = false;
|
|
@@ -119468,7 +119830,7 @@ init_paths();
|
|
|
119468
119830
|
import * as nodeFs4 from "fs";
|
|
119469
119831
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
119470
119832
|
import { homedir as homedir8 } from "os";
|
|
119471
|
-
import { basename as basename4, join as
|
|
119833
|
+
import { basename as basename4, join as join14 } from "path";
|
|
119472
119834
|
var DELEGATED_CHILD_ENV_VAR = "AMETYST_DELEGATED";
|
|
119473
119835
|
var SPEND_GRANT_TOKEN_ENV_VAR = "AMETYST_DELEGATE_SPEND_TOKEN";
|
|
119474
119836
|
var SPEND_KILL_SWITCH_ENV_VAR = "AMETYST_DELEGATE_NO_SPEND";
|
|
@@ -119550,11 +119912,11 @@ function isDelegatedByAncestry(deps = {}) {
|
|
|
119550
119912
|
return classifyAncestry(deps) === "delegated";
|
|
119551
119913
|
}
|
|
119552
119914
|
function delegateChildConfigHome(home = homedir8()) {
|
|
119553
|
-
return
|
|
119915
|
+
return join14(home, `.ametyst${ENV_SUFFIX}`, "delegate-xdg");
|
|
119554
119916
|
}
|
|
119555
119917
|
function ensureChildConfigHome(fs = nodeFs4, home = homedir8()) {
|
|
119556
119918
|
const root2 = delegateChildConfigHome(home);
|
|
119557
|
-
const inner =
|
|
119919
|
+
const inner = join14(root2, "opencode");
|
|
119558
119920
|
if (!fs.existsSync(inner)) fs.mkdirSync(inner, { recursive: true, mode: 448 });
|
|
119559
119921
|
return root2;
|
|
119560
119922
|
}
|
|
@@ -119564,7 +119926,7 @@ function userOpencodeConfigDir(deps = {}) {
|
|
|
119564
119926
|
const env = deps.env ?? process.env;
|
|
119565
119927
|
const home = deps.home ?? homedir8();
|
|
119566
119928
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
119567
|
-
return
|
|
119929
|
+
return join14(xdg ? xdg : join14(home, ".config"), "opencode");
|
|
119568
119930
|
}
|
|
119569
119931
|
function stripJsonComments(text) {
|
|
119570
119932
|
let out = "";
|
|
@@ -119608,9 +119970,9 @@ function readUserMcpServers(deps = {}) {
|
|
|
119608
119970
|
const warn = deps.warn ?? ((line) => console.error(line));
|
|
119609
119971
|
const home = deps.home ?? homedir8();
|
|
119610
119972
|
const dir = userOpencodeConfigDir(deps);
|
|
119611
|
-
if (dir ===
|
|
119973
|
+
if (dir === join14(delegateChildConfigHome(home), "opencode")) return {};
|
|
119612
119974
|
for (const filename of USER_OPENCODE_CONFIG_FILENAMES) {
|
|
119613
|
-
const path2 =
|
|
119975
|
+
const path2 = join14(dir, filename);
|
|
119614
119976
|
let raw;
|
|
119615
119977
|
try {
|
|
119616
119978
|
if (!fs.existsSync(path2)) continue;
|
|
@@ -120708,7 +121070,7 @@ init_esm_shims();
|
|
|
120708
121070
|
import * as nodeFs6 from "fs";
|
|
120709
121071
|
import { createHash as createHash2, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
120710
121072
|
import { homedir as homedir10 } from "os";
|
|
120711
|
-
import { join as
|
|
121073
|
+
import { join as join15 } from "path";
|
|
120712
121074
|
var SPEND_GRANT_RECORD_VERSION = 1;
|
|
120713
121075
|
var TOKEN_BYTES = 32;
|
|
120714
121076
|
var SPEND_GRANT_GRACE_MS = 5 * 6e4;
|
|
@@ -120716,7 +121078,7 @@ function spendGrantPath(jobId, home = homedir10()) {
|
|
|
120716
121078
|
if (!/^[A-Za-z0-9_-]{1,64}$/.test(jobId)) {
|
|
120717
121079
|
throw new Error(`refusing to build a grant path for job id ${JSON.stringify(jobId)}`);
|
|
120718
121080
|
}
|
|
120719
|
-
return
|
|
121081
|
+
return join15(bridgeRunDir(home), `spend-grant-${jobId}.json`);
|
|
120720
121082
|
}
|
|
120721
121083
|
function hashSpendGrantToken(token) {
|
|
120722
121084
|
return createHash2("sha256").update(token, "utf-8").digest("hex");
|
|
@@ -120822,13 +121184,13 @@ init_esm_shims();
|
|
|
120822
121184
|
init_paths();
|
|
120823
121185
|
import * as nodeFs7 from "fs";
|
|
120824
121186
|
import { homedir as homedir11 } from "os";
|
|
120825
|
-
import { dirname as dirname6, join as
|
|
121187
|
+
import { dirname as dirname6, join as join16 } from "path";
|
|
120826
121188
|
var DELEGATE_CREDIT_CAPABILITY = "credit";
|
|
120827
121189
|
var DELEGATE_CREDIT_PRICE_USD = 1;
|
|
120828
121190
|
var DELEGATE_CREDIT_REQUEST_BODY = "{}";
|
|
120829
121191
|
var DELEGATE_CREDIT_SPEND_SNIPPET = `spend({ merchant_slug: "${DELEGATE_MERCHANT_SLUG}", capability: "${DELEGATE_CREDIT_CAPABILITY}", inputs: "${DELEGATE_CREDIT_REQUEST_BODY}" })`;
|
|
120830
121192
|
function creditLedgerPath(home = homedir11()) {
|
|
120831
|
-
return
|
|
121193
|
+
return join16(home, `.ametyst${ENV_SUFFIX}`, "run", "delegate-credit.json");
|
|
120832
121194
|
}
|
|
120833
121195
|
function readCreditLedger(fs = nodeFs7, path2 = creditLedgerPath()) {
|
|
120834
121196
|
try {
|
|
@@ -121219,7 +121581,7 @@ function registerDelegateTools(deps) {
|
|
|
121219
121581
|
const validated = validateStartInput(params, {
|
|
121220
121582
|
isDirectory: (path2) => {
|
|
121221
121583
|
try {
|
|
121222
|
-
return
|
|
121584
|
+
return existsSync11(path2) && statSync2(path2).isDirectory();
|
|
121223
121585
|
} catch {
|
|
121224
121586
|
return false;
|
|
121225
121587
|
}
|
|
@@ -121474,7 +121836,7 @@ init_resolve();
|
|
|
121474
121836
|
|
|
121475
121837
|
// src/mcp-server/identity-reload.ts
|
|
121476
121838
|
init_esm_shims();
|
|
121477
|
-
import { readFileSync as
|
|
121839
|
+
import { readFileSync as readFileSync13, statSync as statSync3 } from "fs";
|
|
121478
121840
|
var NO_VAULT = "absent";
|
|
121479
121841
|
var IDENTITY_CHECK_THROTTLE_MS = 1e3;
|
|
121480
121842
|
function fingerprintVault(stat) {
|
|
@@ -121495,7 +121857,11 @@ var IdentityWatch = class {
|
|
|
121495
121857
|
stat;
|
|
121496
121858
|
/** False for an env-supplied key: `poll` is then a guaranteed no-op. */
|
|
121497
121859
|
armed;
|
|
121860
|
+
configPath;
|
|
121861
|
+
readWorkspaceKey;
|
|
121498
121862
|
fingerprint;
|
|
121863
|
+
configStamp;
|
|
121864
|
+
workspaceKey;
|
|
121499
121865
|
lastCheckedAt = null;
|
|
121500
121866
|
constructor(options) {
|
|
121501
121867
|
this.vaultPath = options.vaultPath;
|
|
@@ -121503,12 +121869,43 @@ var IdentityWatch = class {
|
|
|
121503
121869
|
this.throttleMs = options.throttleMs ?? IDENTITY_CHECK_THROTTLE_MS;
|
|
121504
121870
|
this.stat = options.stat ?? statVaultFile;
|
|
121505
121871
|
this.armed = options.source !== "env";
|
|
121506
|
-
this.
|
|
121872
|
+
this.configPath = options.configPath ?? null;
|
|
121873
|
+
this.readWorkspaceKey = options.readWorkspaceKey ?? null;
|
|
121874
|
+
this.configStamp = this.configPath ? fingerprintVault(this.stat(this.configPath)) : NO_VAULT;
|
|
121875
|
+
this.workspaceKey = this.readWorkspaceKeySafely();
|
|
121876
|
+
this.fingerprint = this.composeFingerprint();
|
|
121877
|
+
}
|
|
121878
|
+
/** Never let a config read take the watch — or the tool call behind it — down. */
|
|
121879
|
+
readWorkspaceKeySafely() {
|
|
121880
|
+
if (!this.readWorkspaceKey) return null;
|
|
121881
|
+
try {
|
|
121882
|
+
return this.readWorkspaceKey();
|
|
121883
|
+
} catch {
|
|
121884
|
+
return null;
|
|
121885
|
+
}
|
|
121886
|
+
}
|
|
121887
|
+
/**
|
|
121888
|
+
* Vault fingerprint AND workspace key in one string, so `poll` stays a single
|
|
121889
|
+
* comparison and neither half can change without being noticed.
|
|
121890
|
+
*
|
|
121891
|
+
* A watch with NO workspace reader appends nothing, so its fingerprint stays
|
|
121892
|
+
* exactly the string it was before 0.3.6 (`"<mtime>:<size>"` / `"absent"`).
|
|
121893
|
+
* That is not cosmetic: the fingerprint is an observable of this class, and a
|
|
121894
|
+
* vault-only watch must be indistinguishable from the one that shipped.
|
|
121895
|
+
*/
|
|
121896
|
+
composeFingerprint() {
|
|
121897
|
+
const vault = fingerprintVault(this.stat(this.vaultPath));
|
|
121898
|
+
if (!this.readWorkspaceKey) return vault;
|
|
121899
|
+
return `${vault}|ws:${this.workspaceKey ?? "unbound"}`;
|
|
121507
121900
|
}
|
|
121508
121901
|
/** The fingerprint this watch currently considers current. */
|
|
121509
121902
|
currentFingerprint() {
|
|
121510
121903
|
return this.fingerprint;
|
|
121511
121904
|
}
|
|
121905
|
+
/** The workspace key this watch last read from the config. */
|
|
121906
|
+
currentWorkspaceKey() {
|
|
121907
|
+
return this.workspaceKey;
|
|
121908
|
+
}
|
|
121512
121909
|
/**
|
|
121513
121910
|
* One throttled `stat`. Returns the change when the vault file differs from
|
|
121514
121911
|
* the fingerprint on record — and ADOPTS it in the same step, so a reload the
|
|
@@ -121521,7 +121918,14 @@ var IdentityWatch = class {
|
|
|
121521
121918
|
const at = this.now();
|
|
121522
121919
|
if (this.lastCheckedAt !== null && at - this.lastCheckedAt < this.throttleMs) return null;
|
|
121523
121920
|
this.lastCheckedAt = at;
|
|
121524
|
-
|
|
121921
|
+
if (this.configPath) {
|
|
121922
|
+
const stamp = fingerprintVault(this.stat(this.configPath));
|
|
121923
|
+
if (stamp !== this.configStamp) {
|
|
121924
|
+
this.configStamp = stamp;
|
|
121925
|
+
this.workspaceKey = this.readWorkspaceKeySafely();
|
|
121926
|
+
}
|
|
121927
|
+
}
|
|
121928
|
+
const next = this.composeFingerprint();
|
|
121525
121929
|
if (next === this.fingerprint) return null;
|
|
121526
121930
|
const from14 = this.fingerprint;
|
|
121527
121931
|
this.fingerprint = next;
|
|
@@ -121537,7 +121941,7 @@ var IdentityWatch = class {
|
|
|
121537
121941
|
*/
|
|
121538
121942
|
readVault() {
|
|
121539
121943
|
try {
|
|
121540
|
-
return
|
|
121944
|
+
return readFileSync13(this.vaultPath, "utf-8");
|
|
121541
121945
|
} catch {
|
|
121542
121946
|
return null;
|
|
121543
121947
|
}
|
|
@@ -121545,6 +121949,7 @@ var IdentityWatch = class {
|
|
|
121545
121949
|
};
|
|
121546
121950
|
|
|
121547
121951
|
// src/mcp-server/index.ts
|
|
121952
|
+
init_grant_keystore();
|
|
121548
121953
|
function buildDeclaredConnectorsSentence(catalog, unreadable) {
|
|
121549
121954
|
const read = catalog === void 0 ? { connectors: cachedCatalogOrEmpty(), unreadable: mirrorGap() } : { connectors: catalog, unreadable: unreadable ?? [] };
|
|
121550
121955
|
const gap = describeCatalogGap(read.unreadable);
|
|
@@ -121555,6 +121960,82 @@ function buildDeclaredConnectorsSentence(catalog, unreadable) {
|
|
|
121555
121960
|
return `Currently declared: ${declared}. Those are the operations picked out as most useful, NOT the limit: each provider's own surface is larger, and any operation it advertises is callable by its exact name. If the one you want is not listed, try it anyway \u2014 an operation that really does not exist comes back as a typed \`unknown_operation\` with the alternatives.` + (gap === "" ? "" : ` ${gap}`);
|
|
121556
121961
|
}
|
|
121557
121962
|
var currentCredentials = {};
|
|
121963
|
+
function vaultIsEncrypted() {
|
|
121964
|
+
return !plaintextVaultMode;
|
|
121965
|
+
}
|
|
121966
|
+
function ownedSigningAddresses() {
|
|
121967
|
+
const addresses = [];
|
|
121968
|
+
if (currentCredentials.eoaAddress) addresses.push(currentCredentials.eoaAddress);
|
|
121969
|
+
try {
|
|
121970
|
+
addresses.push(...listGrantAddresses({ workspaceId: readBoundWorkspace() }));
|
|
121971
|
+
} catch (err) {
|
|
121972
|
+
console.error(
|
|
121973
|
+
`\u26A0\uFE0F could not read the per-grant keystore \u2014 only the install EOA is selectable: ${err instanceof Error ? err.message : String(err)}`
|
|
121974
|
+
);
|
|
121975
|
+
}
|
|
121976
|
+
return addresses;
|
|
121977
|
+
}
|
|
121978
|
+
function isOwnedSigningAddress(address) {
|
|
121979
|
+
if (typeof address !== "string" || address.trim() === "") return false;
|
|
121980
|
+
const needle = address.toLowerCase();
|
|
121981
|
+
return ownedSigningAddresses().some((a) => a.toLowerCase() === needle);
|
|
121982
|
+
}
|
|
121983
|
+
function signingKeyForAddress(address, passphrase) {
|
|
121984
|
+
return resolveGrantSigningKey({
|
|
121985
|
+
address,
|
|
121986
|
+
vaultJson: currentCredentials.walletKeystoreJson,
|
|
121987
|
+
vaultAddress: currentCredentials.eoaAddress,
|
|
121988
|
+
// ⛔ THE CALLER'S PASSPHRASE WINS. `start_session` resolves the key DURING
|
|
121989
|
+
// `runStartSession`, before `mergeStartSessionCredentials` has put the
|
|
121990
|
+
// passphrase into `currentCredentials` — so reading only the field would
|
|
121991
|
+
// make every encrypted-vault unlock fail with "a passphrase is required"
|
|
121992
|
+
// on the very call that was handed one.
|
|
121993
|
+
passphrase: passphrase ?? currentCredentials.passphrase
|
|
121994
|
+
});
|
|
121995
|
+
}
|
|
121996
|
+
async function refreshSignerBinding() {
|
|
121997
|
+
if (!currentCredentials.apiKey) return { changed: false, address: currentCredentials.signerAddress };
|
|
121998
|
+
let wallets;
|
|
121999
|
+
try {
|
|
122000
|
+
wallets = await fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true);
|
|
122001
|
+
} catch (err) {
|
|
122002
|
+
console.error(
|
|
122003
|
+
`\u26A0\uFE0F [signer-binding] could not re-read the grants \u2014 signing with the cached binding (${currentCredentials.signerAddress ?? "none"}): ${err instanceof Error ? err.message : String(err)}`
|
|
122004
|
+
);
|
|
122005
|
+
wallets = walletsCache || [];
|
|
122006
|
+
}
|
|
122007
|
+
const row = currentCredentials.pendingWalletId ? findCurrentApprovedWallet(wallets, ownedSigningAddresses(), currentCredentials.pendingWalletId) : selectNewestApprovedWalletAmong(
|
|
122008
|
+
wallets,
|
|
122009
|
+
(Array.isArray(wallets) ? wallets : []).map((w) => w?.address)
|
|
122010
|
+
);
|
|
122011
|
+
if (!row) return { changed: false, address: currentCredentials.signerAddress };
|
|
122012
|
+
const previousSigner = currentCredentials.signerAddress;
|
|
122013
|
+
const { changed, signerAddress } = bindSignerToGrantRow(currentCredentials, row);
|
|
122014
|
+
if (changed) {
|
|
122015
|
+
console.error(
|
|
122016
|
+
`\u{1F501} [signer-binding] the active grant moved \u2014 signer ${previousSigner ?? "none"} -> ${signerAddress ?? "none"} (virtual wallet ${currentCredentials.virtualWalletId}, policy ${currentCredentials.policyId ?? "unknown"}). Dropping the signer state derived from the old grant.`
|
|
122017
|
+
);
|
|
122018
|
+
}
|
|
122019
|
+
return { changed, address: signerAddress };
|
|
122020
|
+
}
|
|
122021
|
+
async function resolveSignerForMaterialization(passphrase) {
|
|
122022
|
+
await refreshSignerBinding();
|
|
122023
|
+
const address = currentCredentials.signerAddress || currentCredentials.eoaAddress || "";
|
|
122024
|
+
if (!address) {
|
|
122025
|
+
throw new Error(
|
|
122026
|
+
"no signer address for this session \u2014 no approved grant and no install wallet. Run requestAccess first."
|
|
122027
|
+
);
|
|
122028
|
+
}
|
|
122029
|
+
return { address, privateKey: signingKeyForAddress(address, passphrase) };
|
|
122030
|
+
}
|
|
122031
|
+
function hasSigningKeyForAddress(address) {
|
|
122032
|
+
if (!address) return false;
|
|
122033
|
+
try {
|
|
122034
|
+
return hasGrantSigningKey({ address, vaultAddress: currentCredentials.eoaAddress });
|
|
122035
|
+
} catch {
|
|
122036
|
+
return false;
|
|
122037
|
+
}
|
|
122038
|
+
}
|
|
121558
122039
|
var plaintextVaultMode = false;
|
|
121559
122040
|
function refreshPlaintextVaultMode() {
|
|
121560
122041
|
const vault = currentCredentials.walletKeystoreJson;
|
|
@@ -122576,7 +123057,16 @@ async function initializeCredentials(walletKeystoreJson, eoaAddress, config) {
|
|
|
122576
123057
|
currentCredentials.signerAddress = eoaAddress;
|
|
122577
123058
|
currentCredentials.eoaAddress = eoaAddress;
|
|
122578
123059
|
currentCredentials.apiKey = config.apiKey;
|
|
122579
|
-
identityWatch = new IdentityWatch({
|
|
123060
|
+
identityWatch = new IdentityWatch({
|
|
123061
|
+
vaultPath: VAULT_PATH,
|
|
123062
|
+
source: config.apiKeySource ?? null,
|
|
123063
|
+
// The workspace half (0.3.6): a `login`/`init --claim` that re-binds this
|
|
123064
|
+
// install to a DIFFERENT workspace changes the recorded key, and the same
|
|
123065
|
+
// reload that follows a swapped `wallet.json` drops the state cached for
|
|
123066
|
+
// the old one. See ./identity-reload.ts.
|
|
123067
|
+
configPath: CONFIG_PATH,
|
|
123068
|
+
readWorkspaceKey: readBoundWorkspace
|
|
123069
|
+
});
|
|
122580
123070
|
mcpEventLogger = new McpEventLogger(
|
|
122581
123071
|
() => getSDK(),
|
|
122582
123072
|
() => currentCredentials.apiKey,
|
|
@@ -122610,12 +123100,15 @@ async function initializeCredentials(walletKeystoreJson, eoaAddress, config) {
|
|
|
122610
123100
|
const wallets = walletsCache || [];
|
|
122611
123101
|
const approvedWallet = findCurrentApprovedWallet(
|
|
122612
123102
|
wallets,
|
|
122613
|
-
|
|
123103
|
+
ownedSigningAddresses(),
|
|
122614
123104
|
currentCredentials.pendingWalletId
|
|
122615
123105
|
);
|
|
122616
123106
|
if (approvedWallet) {
|
|
122617
123107
|
console.error("\u2705 Found approved wallet from backend \u2014 authorization data stored");
|
|
122618
123108
|
currentCredentials.authorizationStatus = "approved";
|
|
123109
|
+
if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
|
|
123110
|
+
currentCredentials.signerAddress = approvedWallet.address;
|
|
123111
|
+
}
|
|
122619
123112
|
currentCredentials.virtualWalletStatus = approvedWallet.status;
|
|
122620
123113
|
currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
|
|
122621
123114
|
currentCredentials.virtualWalletId = String(approvedWallet.id);
|
|
@@ -122662,12 +123155,9 @@ async function createKernelClientFromVault() {
|
|
|
122662
123155
|
throw new Error("No passphrase or keystore available");
|
|
122663
123156
|
}
|
|
122664
123157
|
if (!cliConfig) throw new Error("CLI config not initialized");
|
|
122665
|
-
const
|
|
122666
|
-
let privateKey =
|
|
122667
|
-
|
|
122668
|
-
currentCredentials.passphrase
|
|
122669
|
-
);
|
|
122670
|
-
console.error(`${ts()} Wallet key loaded`);
|
|
123158
|
+
const signer = await resolveSignerForMaterialization();
|
|
123159
|
+
let privateKey = signer.privateKey;
|
|
123160
|
+
console.error(`${ts()} Wallet key loaded \u2014 signing as ${signer.address}`);
|
|
122671
123161
|
try {
|
|
122672
123162
|
const { virtualWalletsManagers: virtualWalletsManagers2, financialAccounts: financialAccounts2 } = await getSDK();
|
|
122673
123163
|
console.error(`${ts()} Fetching wallet data by API key...`);
|
|
@@ -122806,12 +123296,17 @@ async function tryResolvePendingApproval(probe) {
|
|
|
122806
123296
|
if (probe) probe.fetched = true;
|
|
122807
123297
|
const approvedWallet = findCurrentApprovedWallet(
|
|
122808
123298
|
freshWallets,
|
|
122809
|
-
|
|
123299
|
+
// Post-rotation the row belongs to the ephemeral key this request minted,
|
|
123300
|
+
// not to the install EOA — match on every address we hold a key for.
|
|
123301
|
+
ownedSigningAddresses(),
|
|
122810
123302
|
currentCredentials.pendingWalletId
|
|
122811
123303
|
);
|
|
122812
123304
|
if (!approvedWallet) return false;
|
|
122813
123305
|
console.error("\u2705 [approval-wait] Backend says approved \u2014 syncing");
|
|
122814
123306
|
currentCredentials.authorizationStatus = "approved";
|
|
123307
|
+
if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
|
|
123308
|
+
currentCredentials.signerAddress = approvedWallet.address;
|
|
123309
|
+
}
|
|
122815
123310
|
currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
|
|
122816
123311
|
currentCredentials.virtualWalletId = String(approvedWallet.id);
|
|
122817
123312
|
currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
|
|
@@ -122938,6 +123433,12 @@ async function applyStartSessionUnlock(passphrase, resetRequested) {
|
|
|
122938
123433
|
// this process loaded at boot verifies nothing: a grant approved since boot
|
|
122939
123434
|
// would be invisible to exactly the check that exists to find it.
|
|
122940
123435
|
fetchWallets: () => fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true),
|
|
123436
|
+
// ROTATION (0.3.6). Grant selection must span every address this install
|
|
123437
|
+
// holds a key for, and the key that signs must be the SELECTED row's —
|
|
123438
|
+
// not whatever `wallet.json` happens to contain. Both are injected so
|
|
123439
|
+
// `start-session.ts` keeps no dependency on the keystore's fs layer.
|
|
123440
|
+
candidateAddresses: ownedSigningAddresses(),
|
|
123441
|
+
resolveSigningKey: (address) => signingKeyForAddress(address, passphrase),
|
|
122941
123442
|
readPolicyActiveOnchain: buildPolicyActiveOnchainReader(sdk)
|
|
122942
123443
|
}
|
|
122943
123444
|
);
|
|
@@ -123351,11 +123852,15 @@ server.tool(
|
|
|
123351
123852
|
};
|
|
123352
123853
|
}
|
|
123353
123854
|
const { virtualWalletsManagers: virtualWalletsManagers2 } = await getSDK();
|
|
123354
|
-
const result = await
|
|
123855
|
+
const result = await submitRotatedAccessRequest(
|
|
123355
123856
|
virtualWalletsManagers2,
|
|
123356
123857
|
currentCredentials.apiKey,
|
|
123357
|
-
|
|
123358
|
-
|
|
123858
|
+
policyId,
|
|
123859
|
+
createGrantKeyIssuer({
|
|
123860
|
+
encrypted: vaultIsEncrypted(),
|
|
123861
|
+
passphrase: currentCredentials.passphrase,
|
|
123862
|
+
workspaceId: readBoundWorkspace()
|
|
123863
|
+
})
|
|
123359
123864
|
);
|
|
123360
123865
|
currentCredentials.authorizationStatus = "pending";
|
|
123361
123866
|
currentCredentials.pendingWalletId = result.id ? String(result.id) : void 0;
|
|
@@ -123413,7 +123918,7 @@ server.tool(
|
|
|
123413
123918
|
server.tool(
|
|
123414
123919
|
{
|
|
123415
123920
|
name: "getWalletStatus",
|
|
123416
|
-
description: "Get wallet status: auth, balance, policy, allowlist, WHICH WORKSPACE this server is acting in, and the last 10 transactions (amount, merchant, timestamp, status). `workspace` is `{companyName, employeeName}` \u2014 the Ametyst workspace every task, memory document and payment from this server lands in; both fields are null when the profile could not be read, which means UNKNOWN, never 'no workspace'. A transaction `amount` is a EUR display string like `\u20AC0.0126`, or the em dash `\u2014` when the row carries no usable amount (none recorded, or a value that is not a base-units integer) \u2014 `\u2014` means UNKNOWN, never zero. The unformatted base-units value is on `amountRaw`, which is null for exactly those rows.",
|
|
123921
|
+
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, and `signerKeyPresent` says whether this machine still holds the private key for `signerAddress` \u2014 `false` means no spend can be signed until `requestAccess` mints a fresh session key and an admin approves it. `workspace` is `{companyName, employeeName}` \u2014 the Ametyst workspace every task, memory document and payment from this server lands in; both fields are null when the profile could not be read, which means UNKNOWN, never 'no workspace'. A transaction `amount` is a EUR display string like `\u20AC0.0126`, or the em dash `\u2014` when the row carries no usable amount (none recorded, or a value that is not a base-units integer) \u2014 `\u2014` means UNKNOWN, never zero. The unformatted base-units value is on `amountRaw`, which is null for exactly those rows.",
|
|
123417
123922
|
inputs: []
|
|
123418
123923
|
},
|
|
123419
123924
|
async () => {
|
|
@@ -123445,9 +123950,12 @@ server.tool(
|
|
|
123445
123950
|
fetchVirtualWalletsFromBackend(currentCredentials.apiKey, !refreshedFromBackend),
|
|
123446
123951
|
fetchPoliciesFromBackend(currentCredentials.apiKey, true)
|
|
123447
123952
|
]);
|
|
123448
|
-
const freshWallet =
|
|
123953
|
+
const freshWallet = selectNewestApprovedWalletAmong(freshWallets, ownedSigningAddresses());
|
|
123449
123954
|
const freshWalletIsActiveGrant = String(freshWallet?.id) === String(currentCredentials.virtualWalletId);
|
|
123450
123955
|
if (freshWallet && freshWalletIsActiveGrant) {
|
|
123956
|
+
if (typeof freshWallet.address === "string" && freshWallet.address.trim() !== "") {
|
|
123957
|
+
currentCredentials.signerAddress = freshWallet.address;
|
|
123958
|
+
}
|
|
123451
123959
|
if (freshWallet.policyAssociated != null) {
|
|
123452
123960
|
currentCredentials.policyId = String(freshWallet.policyAssociated);
|
|
123453
123961
|
}
|
|
@@ -123466,6 +123974,24 @@ server.tool(
|
|
|
123466
123974
|
authorized: currentCredentials.authorizationStatus === "approved",
|
|
123467
123975
|
status: currentCredentials.authorizationStatus || "none",
|
|
123468
123976
|
eoaAddress: currentCredentials.eoaAddress || null,
|
|
123977
|
+
// WHICH KEY IS SIGNING. Since 0.3.6 every grant gets its OWN ephemeral
|
|
123978
|
+
// session key, so `signerAddress` is no longer a synonym for
|
|
123979
|
+
// `eoaAddress`: it is the address of the approved row this session
|
|
123980
|
+
// adopted, and `eoaAddress` is the install identity in `wallet.json`.
|
|
123981
|
+
// They agree only for a pre-rotation grant. Reporting both is what makes
|
|
123982
|
+
// "which key signed this" readable from a payload instead of from a
|
|
123983
|
+
// nonce-key autopsy — the same lesson as `activeVirtualWalletId` below.
|
|
123984
|
+
signerAddress: currentCredentials.signerAddress || currentCredentials.eoaAddress || null,
|
|
123985
|
+
// CAN THIS MACHINE ACTUALLY SIGN FOR THAT GRANT? A rotated grant is
|
|
123986
|
+
// signable only from the per-grant keystore entry minted with it, and a
|
|
123987
|
+
// machine that lost it (a re-install, a restored home, a grant approved
|
|
123988
|
+
// on another laptop) is bound to a signer it cannot produce. Presence
|
|
123989
|
+
// only — no decryption, so an encrypted vault with no cached passphrase
|
|
123990
|
+
// still reports `true` here and bounces at spend time on the passphrase,
|
|
123991
|
+
// which is a different and recoverable problem.
|
|
123992
|
+
signerKeyPresent: hasSigningKeyForAddress(
|
|
123993
|
+
currentCredentials.signerAddress || currentCredentials.eoaAddress
|
|
123994
|
+
),
|
|
123469
123995
|
walletAddress: currentCredentials.walletAddress || null,
|
|
123470
123996
|
kernelClientActive: !!currentCredentials.virtualWalletKernelAccountClient,
|
|
123471
123997
|
// WHICH GRANT THIS SESSION SIGNS WITH. The 2026-09-01 incident — three
|
|
@@ -123489,9 +124015,7 @@ server.tool(
|
|
|
123489
124015
|
}
|
|
123490
124016
|
}
|
|
123491
124017
|
if (walletsCache && currentCredentials.eoaAddress) {
|
|
123492
|
-
const wallet =
|
|
123493
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
123494
|
-
);
|
|
124018
|
+
const wallet = selectNewestApprovedWalletAmong(walletsCache, ownedSigningAddresses()) ?? walletsCache.find((w) => isOwnedSigningAddress(w.address));
|
|
123495
124019
|
if (wallet) {
|
|
123496
124020
|
const now = Math.floor(Date.now() / 1e3);
|
|
123497
124021
|
const validUntil = wallet.policyValidUntil;
|
|
@@ -124015,7 +124539,7 @@ async function startLiveDashboard(entity, dir) {
|
|
|
124015
124539
|
clearInterval(watcher);
|
|
124016
124540
|
return;
|
|
124017
124541
|
}
|
|
124018
|
-
if (!
|
|
124542
|
+
if (!existsSync13(dir)) {
|
|
124019
124543
|
clearInterval(watcher);
|
|
124020
124544
|
liveDashboards.delete(slug);
|
|
124021
124545
|
try {
|
|
@@ -124944,11 +125468,13 @@ server.tool(
|
|
|
124944
125468
|
};
|
|
124945
125469
|
}
|
|
124946
125470
|
touchWalletActivity();
|
|
125471
|
+
let missingSignerKey;
|
|
124947
125472
|
if (!currentCredentials.virtualWalletKernelAccountClient && currentCredentials.authorizationStatus === "approved" && isWalletUnlocked()) {
|
|
124948
125473
|
try {
|
|
124949
125474
|
console.error("\u{1F504} [spend] Auto-creating kernel client...");
|
|
124950
125475
|
await createKernelClientFromVault();
|
|
124951
125476
|
} catch (kcErr) {
|
|
125477
|
+
if (isMissingGrantKeyError(kcErr)) missingSignerKey = kcErr;
|
|
124952
125478
|
console.error("\u274C [spend] Auto kernel client creation failed:", kcErr instanceof Error ? kcErr.message : String(kcErr));
|
|
124953
125479
|
}
|
|
124954
125480
|
}
|
|
@@ -124962,10 +125488,13 @@ server.tool(
|
|
|
124962
125488
|
}
|
|
124963
125489
|
const approvedWallet = findCurrentApprovedWallet(
|
|
124964
125490
|
wallets,
|
|
124965
|
-
|
|
125491
|
+
ownedSigningAddresses(),
|
|
124966
125492
|
currentCredentials.pendingWalletId
|
|
124967
125493
|
);
|
|
124968
125494
|
if (approvedWallet && currentCredentials.authorizationStatus !== "approved") {
|
|
125495
|
+
if (typeof approvedWallet.address === "string" && approvedWallet.address.trim() !== "") {
|
|
125496
|
+
currentCredentials.signerAddress = approvedWallet.address;
|
|
125497
|
+
}
|
|
124969
125498
|
currentCredentials.walletAddress = approvedWallet.walletAddress || approvedWallet.kernelAccountAddress;
|
|
124970
125499
|
currentCredentials.virtualWalletId = String(approvedWallet.id);
|
|
124971
125500
|
currentCredentials.paymentManagerAddress = approvedWallet.paymentManagerAddress;
|
|
@@ -125001,6 +125530,28 @@ server.tool(
|
|
|
125001
125530
|
const apiKey = currentCredentials.apiKey;
|
|
125002
125531
|
if (!kernelClient || !apiKey) {
|
|
125003
125532
|
console.error("\u26A0\uFE0F [spend] Missing kernel client or API key in state");
|
|
125533
|
+
if (missingSignerKey) {
|
|
125534
|
+
logSpendTelemetryEarlyErr();
|
|
125535
|
+
return {
|
|
125536
|
+
content: [
|
|
125537
|
+
{
|
|
125538
|
+
type: "text",
|
|
125539
|
+
text: JSON.stringify({
|
|
125540
|
+
success: false,
|
|
125541
|
+
...disclosePayment(),
|
|
125542
|
+
// PRE-PAYMENT
|
|
125543
|
+
error: "signer_key_missing",
|
|
125544
|
+
detail: missingSignerKey.message,
|
|
125545
|
+
guidance: {
|
|
125546
|
+
say_to_user: "This machine can't sign for your approved access any more \u2014 the session key it was issued to isn't here. I need to request access again so a fresh key can be approved.",
|
|
125547
|
+
next_action: "Call getAvailablePolicies(), let the user pick a policy, then call requestAccess with it. Once the admin approves the new request, retry this spend.",
|
|
125548
|
+
stop: true
|
|
125549
|
+
}
|
|
125550
|
+
})
|
|
125551
|
+
}
|
|
125552
|
+
]
|
|
125553
|
+
};
|
|
125554
|
+
}
|
|
125004
125555
|
if (currentCredentials.authorizationStatus === "pending") {
|
|
125005
125556
|
logSpendTelemetryEarlyErr();
|
|
125006
125557
|
return {
|
|
@@ -125854,7 +126405,7 @@ async function reconcileApprovalStateOnConnect() {
|
|
|
125854
126405
|
}
|
|
125855
126406
|
try {
|
|
125856
126407
|
const wallets = await fetchVirtualWalletsFromBackend(currentCredentials.apiKey, true);
|
|
125857
|
-
const approved =
|
|
126408
|
+
const approved = selectNewestApprovedWalletAmong(wallets, ownedSigningAddresses());
|
|
125858
126409
|
if (!approved) return;
|
|
125859
126410
|
if (typeof approved.id === "number" && hasProcessedApprovalEventId(approved.id)) return;
|
|
125860
126411
|
console.error(
|
|
@@ -125869,9 +126420,7 @@ async function reconcileApprovalStateOnConnect() {
|
|
|
125869
126420
|
policyOnchainPermissions: void 0
|
|
125870
126421
|
});
|
|
125871
126422
|
if (walletsCache) {
|
|
125872
|
-
const idx = walletsCache.findIndex(
|
|
125873
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
125874
|
-
);
|
|
126423
|
+
const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
|
|
125875
126424
|
if (idx >= 0) walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
|
|
125876
126425
|
}
|
|
125877
126426
|
if (typeof approved.id === "number") rememberProcessedApprovalEventId(approved.id);
|
|
@@ -125904,8 +126453,8 @@ async function setupWebSocketListener() {
|
|
|
125904
126453
|
return;
|
|
125905
126454
|
}
|
|
125906
126455
|
const statusMatch = event.status === "approved" || event.status === "ok";
|
|
125907
|
-
const addressMatch = event.address
|
|
125908
|
-
console.error(`\u{1F50D} [WebSocket] statusMatch=${statusMatch} addressMatch=${addressMatch} eoaAddress=${currentCredentials.eoaAddress}`);
|
|
126456
|
+
const addressMatch = isOwnedSigningAddress(event.address);
|
|
126457
|
+
console.error(`\u{1F50D} [WebSocket] statusMatch=${statusMatch} addressMatch=${addressMatch} eoaAddress=${currentCredentials.eoaAddress} signerAddress=${currentCredentials.signerAddress}`);
|
|
125909
126458
|
if (statusMatch && addressMatch) {
|
|
125910
126459
|
try {
|
|
125911
126460
|
await onAuthorizationApproved({
|
|
@@ -125917,9 +126466,7 @@ async function setupWebSocketListener() {
|
|
|
125917
126466
|
policyOnchainPermissions: void 0
|
|
125918
126467
|
});
|
|
125919
126468
|
if (walletsCache) {
|
|
125920
|
-
const idx = walletsCache.findIndex(
|
|
125921
|
-
(w) => w.address?.toLowerCase() === currentCredentials.eoaAddress?.toLowerCase()
|
|
125922
|
-
);
|
|
126469
|
+
const idx = walletsCache.findIndex((w) => isOwnedSigningAddress(w.address));
|
|
125923
126470
|
if (idx >= 0) {
|
|
125924
126471
|
walletsCache[idx] = { ...walletsCache[idx], status: "approved" };
|
|
125925
126472
|
}
|
|
@@ -126192,15 +126739,15 @@ function killPreviousServeInstances(deps) {
|
|
|
126192
126739
|
|
|
126193
126740
|
// src/commands/autosync-skills.ts
|
|
126194
126741
|
init_esm_shims();
|
|
126195
|
-
import { existsSync as
|
|
126742
|
+
import { existsSync as existsSync15 } from "fs";
|
|
126196
126743
|
import { homedir as homedir12 } from "os";
|
|
126197
|
-
import { join as
|
|
126744
|
+
import { join as join19 } from "path";
|
|
126198
126745
|
|
|
126199
126746
|
// src/compounds/sync-skills.ts
|
|
126200
126747
|
init_esm_shims();
|
|
126201
126748
|
init_paths();
|
|
126202
|
-
import { existsSync as
|
|
126203
|
-
import { join as
|
|
126749
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync10 } from "fs";
|
|
126750
|
+
import { join as join18 } from "path";
|
|
126204
126751
|
var MANAGED_MARKER = "<!-- ametyst-managed: sync-skills -->";
|
|
126205
126752
|
var GITIGNORE_HEADER = "# ametyst-managed: sync-skills \u2014 pointer skills generated for this account; never commit them.";
|
|
126206
126753
|
var GITIGNORE_HEADER_2 = "# Maintained by `ametyst serve` on every boot. Real skills without the managed marker are not listed.";
|
|
@@ -126244,22 +126791,22 @@ ${taskRunSection(slug, kind)}`;
|
|
|
126244
126791
|
}
|
|
126245
126792
|
function isManaged(file) {
|
|
126246
126793
|
try {
|
|
126247
|
-
return
|
|
126794
|
+
return readFileSync14(file, "utf8").includes(MANAGED_MARKER);
|
|
126248
126795
|
} catch {
|
|
126249
126796
|
return false;
|
|
126250
126797
|
}
|
|
126251
126798
|
}
|
|
126252
126799
|
function listManagedDirs(root2) {
|
|
126253
|
-
return
|
|
126800
|
+
return readdirSync5(root2, { withFileTypes: true }).filter((e) => e.isDirectory() && isManaged(join18(root2, e.name, "SKILL.md"))).map((e) => e.name).sort();
|
|
126254
126801
|
}
|
|
126255
126802
|
function buildGitignoreContent(managedSlugs) {
|
|
126256
126803
|
return [GITIGNORE_HEADER, GITIGNORE_HEADER_2, ".gitignore", ...managedSlugs.map((s) => `/${s}/`)].join("\n") + "\n";
|
|
126257
126804
|
}
|
|
126258
126805
|
function maintainGitignore(root2) {
|
|
126259
|
-
const file =
|
|
126806
|
+
const file = join18(root2, ".gitignore");
|
|
126260
126807
|
const managed = listManagedDirs(root2);
|
|
126261
|
-
if (
|
|
126262
|
-
const current =
|
|
126808
|
+
if (existsSync14(file)) {
|
|
126809
|
+
const current = readFileSync14(file, "utf8");
|
|
126263
126810
|
const firstLine = current.split(/\r?\n/, 1)[0];
|
|
126264
126811
|
if (firstLine !== GITIGNORE_HEADER) {
|
|
126265
126812
|
console.error(
|
|
@@ -126317,26 +126864,26 @@ async function syncSkills(opts = {}) {
|
|
|
126317
126864
|
if (!desired.has(dir)) desired.set(dir, item);
|
|
126318
126865
|
}
|
|
126319
126866
|
const root2 = skillsRoot(target, global2);
|
|
126320
|
-
|
|
126867
|
+
mkdirSync9(root2, { recursive: true });
|
|
126321
126868
|
let written = 0;
|
|
126322
126869
|
const skipped = [];
|
|
126323
126870
|
for (const [dir, item] of desired) {
|
|
126324
|
-
const file =
|
|
126325
|
-
if (
|
|
126871
|
+
const file = join18(dir, "SKILL.md");
|
|
126872
|
+
if (existsSync14(file) && !isManaged(file)) {
|
|
126326
126873
|
skipped.push(item.slug);
|
|
126327
126874
|
console.warn(`\u26A0\uFE0F sync-skills: skipping "${item.slug}" \u2014 an unmanaged skill already exists at ${file}`);
|
|
126328
126875
|
continue;
|
|
126329
126876
|
}
|
|
126330
|
-
|
|
126877
|
+
mkdirSync9(dir, { recursive: true });
|
|
126331
126878
|
writeFileSync10(file, buildStubContent(item));
|
|
126332
126879
|
written++;
|
|
126333
126880
|
}
|
|
126334
126881
|
let pruned = 0;
|
|
126335
|
-
for (const entry of
|
|
126882
|
+
for (const entry of readdirSync5(root2, { withFileTypes: true })) {
|
|
126336
126883
|
if (!entry.isDirectory()) continue;
|
|
126337
|
-
const dir =
|
|
126884
|
+
const dir = join18(root2, entry.name);
|
|
126338
126885
|
if (desired.has(dir)) continue;
|
|
126339
|
-
if (!isManaged(
|
|
126886
|
+
if (!isManaged(join18(dir, "SKILL.md"))) continue;
|
|
126340
126887
|
rmSync(dir, { recursive: true, force: true });
|
|
126341
126888
|
pruned++;
|
|
126342
126889
|
}
|
|
@@ -126350,12 +126897,12 @@ function isSkillsAutosyncEnabled(env = process.env) {
|
|
|
126350
126897
|
return !(raw === "0" || raw === "false" || raw === "off" || raw === "no");
|
|
126351
126898
|
}
|
|
126352
126899
|
function detectPresentTargets(deps = {}) {
|
|
126353
|
-
const exists = deps.existsSync ??
|
|
126900
|
+
const exists = deps.existsSync ?? existsSync15;
|
|
126354
126901
|
const cwd = deps.cwd ?? (() => process.cwd());
|
|
126355
126902
|
const home = deps.homedir ?? homedir12;
|
|
126356
126903
|
const targets = [];
|
|
126357
|
-
if (exists(
|
|
126358
|
-
if (exists(
|
|
126904
|
+
if (exists(join19(cwd(), ".claude")) || exists(join19(home(), ".claude"))) targets.push("claude");
|
|
126905
|
+
if (exists(join19(cwd(), ".codex")) || exists(join19(home(), ".codex", "config.toml"))) targets.push("codex");
|
|
126359
126906
|
return targets;
|
|
126360
126907
|
}
|
|
126361
126908
|
function resolveAutosyncTargets(clientName, deps = {}) {
|
|
@@ -127045,14 +127592,14 @@ init_esm_shims();
|
|
|
127045
127592
|
// src/loops/materialize.ts
|
|
127046
127593
|
init_esm_shims();
|
|
127047
127594
|
init_paths();
|
|
127048
|
-
import { mkdirSync as
|
|
127049
|
-
import { join as
|
|
127595
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
|
|
127596
|
+
import { join as join20 } from "path";
|
|
127050
127597
|
var MIRRORED_DEFINITION_FILES = ["SKILL.md", "VISION.md", "README.md"];
|
|
127051
127598
|
function materialize(loop2, fireId, stateDocs = []) {
|
|
127052
127599
|
const dir = loopFireDir(loop2.slug, fireId);
|
|
127053
|
-
|
|
127600
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
127054
127601
|
for (const doc of stateDocs) {
|
|
127055
|
-
writeFileSync11(
|
|
127602
|
+
writeFileSync11(join20(dir, doc.filename), doc.body, { mode: 384 });
|
|
127056
127603
|
}
|
|
127057
127604
|
const files = {
|
|
127058
127605
|
"SKILL.md": loop2.markdownBody ?? "",
|
|
@@ -127067,10 +127614,10 @@ function materialize(loop2, fireId, stateDocs = []) {
|
|
|
127067
127614
|
files["dashboard.manifest.json"] = loop2.dashboardManifest;
|
|
127068
127615
|
}
|
|
127069
127616
|
for (const [name, body] of Object.entries(files)) {
|
|
127070
|
-
writeFileSync11(
|
|
127617
|
+
writeFileSync11(join20(dir, name), body, { mode: 384 });
|
|
127071
127618
|
}
|
|
127072
127619
|
writeFileSync11(
|
|
127073
|
-
|
|
127620
|
+
join20(dir, "STATUS.md"),
|
|
127074
127621
|
`# STATUS \u2014 ${loop2.slug}
|
|
127075
127622
|
|
|
127076
127623
|
loop_id: ${loop2.id}
|
|
@@ -127086,11 +127633,11 @@ queue: not started
|
|
|
127086
127633
|
function mirrorDefinitionFiles(slug, files) {
|
|
127087
127634
|
try {
|
|
127088
127635
|
const root2 = loopDir(slug);
|
|
127089
|
-
|
|
127636
|
+
mkdirSync10(root2, { recursive: true, mode: 448 });
|
|
127090
127637
|
for (const name of [...MIRRORED_DEFINITION_FILES, "dashboard.html", "dashboard.manifest.json"]) {
|
|
127091
127638
|
const body = files[name];
|
|
127092
127639
|
if (body === void 0) continue;
|
|
127093
|
-
writeFileSync11(
|
|
127640
|
+
writeFileSync11(join20(root2, name), body, { mode: 384 });
|
|
127094
127641
|
}
|
|
127095
127642
|
} catch {
|
|
127096
127643
|
}
|
|
@@ -127216,15 +127763,15 @@ function resolveMaxBudgetUsd(opts, env = process.env) {
|
|
|
127216
127763
|
// src/loops/heartbeat.ts
|
|
127217
127764
|
init_esm_shims();
|
|
127218
127765
|
import * as realFs2 from "fs";
|
|
127219
|
-
import { join as
|
|
127766
|
+
import { join as join21 } from "path";
|
|
127220
127767
|
function startHeartbeat(loopDir2, info, deps = {}) {
|
|
127221
127768
|
const fs = deps.fs ?? realFs2;
|
|
127222
127769
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
127223
127770
|
const setI = deps.setInterval ?? globalThis.setInterval;
|
|
127224
127771
|
const clearI = deps.clearInterval ?? globalThis.clearInterval;
|
|
127225
127772
|
const intervalMs = deps.intervalMs ?? 6e4;
|
|
127226
|
-
const stateDir =
|
|
127227
|
-
const path2 =
|
|
127773
|
+
const stateDir = join21(loopDir2, ".state");
|
|
127774
|
+
const path2 = join21(stateDir, "fire.running");
|
|
127228
127775
|
try {
|
|
127229
127776
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
127230
127777
|
fs.writeFileSync(
|
|
@@ -127264,10 +127811,10 @@ ${info.sessionId ? `session=${info.sessionId}
|
|
|
127264
127811
|
init_esm_shims();
|
|
127265
127812
|
import * as realFs3 from "fs";
|
|
127266
127813
|
import { homedir as homedir13 } from "os";
|
|
127267
|
-
import { join as
|
|
127814
|
+
import { join as join22 } from "path";
|
|
127268
127815
|
function transcriptPathFor(cwd, sessionId2, home = homedir13()) {
|
|
127269
127816
|
const slug = cwd.replace(/\//g, "-");
|
|
127270
|
-
return
|
|
127817
|
+
return join22(home, ".claude", "projects", slug, `${sessionId2}.jsonl`);
|
|
127271
127818
|
}
|
|
127272
127819
|
function parseTranscriptStats(jsonl) {
|
|
127273
127820
|
const tokens = {
|
|
@@ -127338,9 +127885,9 @@ function recordFireAccounting(args) {
|
|
|
127338
127885
|
}
|
|
127339
127886
|
}
|
|
127340
127887
|
function appendFireLine(fs, loopDir2, rec) {
|
|
127341
|
-
const stateDir =
|
|
127888
|
+
const stateDir = join22(loopDir2, ".state");
|
|
127342
127889
|
fs.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
127343
|
-
fs.appendFileSync(
|
|
127890
|
+
fs.appendFileSync(join22(stateDir, "fires.jsonl"), JSON.stringify(rec) + "\n", { mode: 384 });
|
|
127344
127891
|
}
|
|
127345
127892
|
function recordFailedLaunch(args) {
|
|
127346
127893
|
const fs = args.deps?.fs ?? realFs3;
|
|
@@ -127369,8 +127916,8 @@ function recordFailedLaunch(args) {
|
|
|
127369
127916
|
init_esm_shims();
|
|
127370
127917
|
import { spawn as spawn2 } from "child_process";
|
|
127371
127918
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
127372
|
-
import { existsSync as
|
|
127373
|
-
import { dirname as dirname8, join as
|
|
127919
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
127920
|
+
import { dirname as dirname8, join as join24 } from "path";
|
|
127374
127921
|
|
|
127375
127922
|
// src/loops/claude-binary.ts
|
|
127376
127923
|
init_esm_shims();
|
|
@@ -127422,7 +127969,7 @@ function ensureClaudeBinary(deps = {}) {
|
|
|
127422
127969
|
// src/loops/concurrency.ts
|
|
127423
127970
|
init_esm_shims();
|
|
127424
127971
|
import * as realFs4 from "fs";
|
|
127425
|
-
import { join as
|
|
127972
|
+
import { join as join23 } from "path";
|
|
127426
127973
|
var DEFAULT_MAX_CONCURRENT_FIRES = 6;
|
|
127427
127974
|
var DEFAULT_HEARTBEAT_STALE_MS = 10 * 6e4;
|
|
127428
127975
|
function pidIsAlive(pid) {
|
|
@@ -127446,14 +127993,14 @@ function liveFires(loopRoot, deps = {}) {
|
|
|
127446
127993
|
const staleMs = deps.staleMs ?? DEFAULT_HEARTBEAT_STALE_MS;
|
|
127447
127994
|
let entries;
|
|
127448
127995
|
try {
|
|
127449
|
-
entries = fs.readdirSync(
|
|
127996
|
+
entries = fs.readdirSync(join23(loopRoot, "fires"));
|
|
127450
127997
|
} catch {
|
|
127451
127998
|
return [];
|
|
127452
127999
|
}
|
|
127453
128000
|
const out = [];
|
|
127454
128001
|
for (const entry of entries) {
|
|
127455
128002
|
try {
|
|
127456
|
-
const beat =
|
|
128003
|
+
const beat = join23(loopRoot, "fires", String(entry), ".state", "fire.running");
|
|
127457
128004
|
const st = fs.statSync(beat);
|
|
127458
128005
|
const heartbeatAgeMs = now() - Number(st.mtimeMs);
|
|
127459
128006
|
if (!(heartbeatAgeMs <= staleMs)) continue;
|
|
@@ -127481,7 +128028,7 @@ var SHIPBACK_SIGNAL_TIMEOUT_MS = 8e3;
|
|
|
127481
128028
|
function preserveRefusedConstraints(slug, fireId, body) {
|
|
127482
128029
|
try {
|
|
127483
128030
|
const path2 = refusedConstraintsPath(slug, fireId);
|
|
127484
|
-
|
|
128031
|
+
mkdirSync11(dirname8(path2), { recursive: true, mode: 448 });
|
|
127485
128032
|
writeFileSync12(path2, body, { mode: 384 });
|
|
127486
128033
|
return { path: path2 };
|
|
127487
128034
|
} catch (err) {
|
|
@@ -127560,8 +128107,8 @@ async function runLoop(loopId, opts = {}) {
|
|
|
127560
128107
|
console.log(
|
|
127561
128108
|
`Loop ${loop2.slug}: ${est.steps} steps (${est.paidSteps} paid), est \u20AC${est.estCostEur ?? "?"} \u2014 ${capLabel}`
|
|
127562
128109
|
);
|
|
127563
|
-
const statusPath =
|
|
127564
|
-
const statusBefore =
|
|
128110
|
+
const statusPath = join24(dir, "STATUS.md");
|
|
128111
|
+
const statusBefore = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
|
|
127565
128112
|
writeFileSync12(
|
|
127566
128113
|
statusPath,
|
|
127567
128114
|
statusBefore.replace(
|
|
@@ -127620,8 +128167,8 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127620
128167
|
}
|
|
127621
128168
|
async function shipConstraints() {
|
|
127622
128169
|
try {
|
|
127623
|
-
const constraintsPath =
|
|
127624
|
-
const materializedConstraints =
|
|
128170
|
+
const constraintsPath = join24(dir, "CONSTRAINTS.md");
|
|
128171
|
+
const materializedConstraints = existsSync16(constraintsPath) ? readFileSync15(constraintsPath, "utf-8") : void 0;
|
|
127625
128172
|
if (materializedConstraints === void 0) return;
|
|
127626
128173
|
const boot = loop2.constraintsMd ?? "";
|
|
127627
128174
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
@@ -127696,7 +128243,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127696
128243
|
void Promise.race([shipBackConstraints().then(() => "shipped"), deadline]).then((outcome) => {
|
|
127697
128244
|
if (outcome === "timeout") {
|
|
127698
128245
|
console.error(
|
|
127699
|
-
`Loop ${loop2.slug}: the ship-back did not finish within ${SHIPBACK_SIGNAL_TIMEOUT_MS}ms of the signal \u2014 exiting anyway so the shutdown is not held open. Nothing was discarded: this fire's rules remain at ${
|
|
128246
|
+
`Loop ${loop2.slug}: the ship-back did not finish within ${SHIPBACK_SIGNAL_TIMEOUT_MS}ms of the signal \u2014 exiting anyway so the shutdown is not held open. Nothing was discarded: this fire's rules remain at ${join24(dir, "CONSTRAINTS.md")}, and any state doc that did not get shipped remains beside it in ${dir}. The next fire can recover them by hand from there \u2014 nothing in the CLI reads a prior fire's folder automatically. \u26D4 Constraints are shipped FIRST, so the state docs are the likelier victim of this deadline \u2014 though a constraints ship-back that alone exceeds it truncates the rules too.`
|
|
127700
128247
|
);
|
|
127701
128248
|
}
|
|
127702
128249
|
}).finally(() => process.exit(130));
|
|
@@ -127732,7 +128279,7 @@ blast_radius: steps=${est.steps} paid=${est.paidSteps} estEur=${est.estCostEur ?
|
|
|
127732
128279
|
startedAtEpochMs
|
|
127733
128280
|
});
|
|
127734
128281
|
}
|
|
127735
|
-
const statusAfter =
|
|
128282
|
+
const statusAfter = existsSync16(statusPath) ? readFileSync15(statusPath, "utf-8") : "";
|
|
127736
128283
|
const clean2 = exitCode === 0 && /(vision\s*done|queue\s*drained|status:\s*done)/i.test(statusAfter) && !/(brake|crash|crashed|errored)/i.test(statusAfter);
|
|
127737
128284
|
await shipBackConstraints();
|
|
127738
128285
|
process.off("SIGINT", onSignal);
|
|
@@ -127770,9 +128317,9 @@ async function showLoop(loopId) {
|
|
|
127770
128317
|
// src/loops/schedule.ts
|
|
127771
128318
|
init_esm_shims();
|
|
127772
128319
|
init_paths();
|
|
127773
|
-
import { writeFileSync as writeFileSync13, mkdirSync as
|
|
128320
|
+
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync12, rmSync as rmSync3, existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync16, accessSync as accessSync2, constants as constants2 } from "fs";
|
|
127774
128321
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
127775
|
-
import { join as
|
|
128322
|
+
import { join as join25, dirname as dirname9 } from "path";
|
|
127776
128323
|
import { homedir as homedir14 } from "os";
|
|
127777
128324
|
var LOOP_KIND = {
|
|
127778
128325
|
labelPrefix: "xyz.ametyst.loop.",
|
|
@@ -127783,7 +128330,7 @@ var LOOP_KIND = {
|
|
|
127783
128330
|
return args;
|
|
127784
128331
|
},
|
|
127785
128332
|
stateDir(slug, cwd) {
|
|
127786
|
-
return
|
|
128333
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
|
|
127787
128334
|
}
|
|
127788
128335
|
};
|
|
127789
128336
|
var TASK_KIND = {
|
|
@@ -127795,7 +128342,7 @@ var TASK_KIND = {
|
|
|
127795
128342
|
return args;
|
|
127796
128343
|
},
|
|
127797
128344
|
stateDir(slug, cwd) {
|
|
127798
|
-
return
|
|
128345
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "loops", safeSlug2(slug, this.noun), ".state");
|
|
127799
128346
|
}
|
|
127800
128347
|
};
|
|
127801
128348
|
var COMPOUND_KIND = {
|
|
@@ -127807,7 +128354,7 @@ var COMPOUND_KIND = {
|
|
|
127807
128354
|
return args;
|
|
127808
128355
|
},
|
|
127809
128356
|
stateDir(slug, cwd) {
|
|
127810
|
-
return
|
|
128357
|
+
return join25(cwd, `.ametyst${ENV_SUFFIX}`, "compounds", safeSlug2(slug, this.noun), ".state");
|
|
127811
128358
|
}
|
|
127812
128359
|
};
|
|
127813
128360
|
function safeSlug2(slug, noun) {
|
|
@@ -127834,7 +128381,7 @@ function parseAt(at) {
|
|
|
127834
128381
|
return { hour, minute };
|
|
127835
128382
|
}
|
|
127836
128383
|
function plistPath(kind, home, slug) {
|
|
127837
|
-
return
|
|
128384
|
+
return join25(home, "Library", "LaunchAgents", `${label(kind, slug)}.plist`);
|
|
127838
128385
|
}
|
|
127839
128386
|
function launchctl(args) {
|
|
127840
128387
|
try {
|
|
@@ -127850,7 +128397,7 @@ function isLoaded(lbl) {
|
|
|
127850
128397
|
}
|
|
127851
128398
|
function readInteractiveDefaultModel(home) {
|
|
127852
128399
|
try {
|
|
127853
|
-
const raw =
|
|
128400
|
+
const raw = readFileSync16(join25(home, ".claude", "settings.json"), "utf-8");
|
|
127854
128401
|
const model = JSON.parse(String(raw)).model;
|
|
127855
128402
|
return typeof model === "string" && model.trim() ? model.trim() : void 0;
|
|
127856
128403
|
} catch {
|
|
@@ -127952,9 +128499,9 @@ ${progArgs}
|
|
|
127952
128499
|
${envEntries}
|
|
127953
128500
|
</dict>
|
|
127954
128501
|
<key>StandardOutPath</key>
|
|
127955
|
-
<string>${escapeXml(
|
|
128502
|
+
<string>${escapeXml(join25(stateDir, LAUNCHD_OUT))}</string>
|
|
127956
128503
|
<key>StandardErrorPath</key>
|
|
127957
|
-
<string>${escapeXml(
|
|
128504
|
+
<string>${escapeXml(join25(stateDir, LAUNCHD_ERR))}</string>
|
|
127958
128505
|
<key>AbandonProcessGroup</key>
|
|
127959
128506
|
<true/>
|
|
127960
128507
|
${scheduleBlock}
|
|
@@ -128020,8 +128567,8 @@ function schedule(kind, slug, opts = {}) {
|
|
|
128020
128567
|
const path2 = plistPath(kind, home, slug);
|
|
128021
128568
|
const stateDir2 = kind.stateDir(slug, cwd);
|
|
128022
128569
|
assertWritable(cwd, "working directory");
|
|
128023
|
-
|
|
128024
|
-
|
|
128570
|
+
mkdirSync12(dirname9(path2), { recursive: true });
|
|
128571
|
+
mkdirSync12(stateDir2, { recursive: true });
|
|
128025
128572
|
assertWritable(stateDir2, "log directory");
|
|
128026
128573
|
writeFileSync13(path2, plistXml(lbl, args, scheduleBlock, cwd, jobEnv, stateDir2), { mode: 384 });
|
|
128027
128574
|
if (isLoaded(lbl)) {
|
|
@@ -128044,7 +128591,7 @@ launchctl said: ${loaded.output.trim()}` : "")
|
|
|
128044
128591
|
}
|
|
128045
128592
|
const stateDir = kind.stateDir(slug, cwd);
|
|
128046
128593
|
assertWritable(cwd, "working directory");
|
|
128047
|
-
|
|
128594
|
+
mkdirSync12(stateDir, { recursive: true });
|
|
128048
128595
|
assertWritable(stateDir, "log directory");
|
|
128049
128596
|
const cronEnv = { PATH: envPath };
|
|
128050
128597
|
if (model) cronEnv.ANTHROPIC_MODEL = model;
|
|
@@ -128077,9 +128624,9 @@ function list(kind, opts = {}) {
|
|
|
128077
128624
|
const home = opts.home ?? homedir14();
|
|
128078
128625
|
const prefix = kind.labelPrefix;
|
|
128079
128626
|
if (platform === "darwin") {
|
|
128080
|
-
const dir =
|
|
128081
|
-
if (!
|
|
128082
|
-
return
|
|
128627
|
+
const dir = join25(home, "Library", "LaunchAgents");
|
|
128628
|
+
if (!existsSync17(dir)) return [];
|
|
128629
|
+
return readdirSync6(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => f.slice(prefix.length, -".plist".length));
|
|
128083
128630
|
}
|
|
128084
128631
|
return readCrontab().split("\n").filter((l) => l.includes(`# ${prefix}`)).map((l) => l.trimEnd().slice(l.trimEnd().lastIndexOf(prefix) + prefix.length));
|
|
128085
128632
|
}
|
|
@@ -128139,13 +128686,13 @@ function listEntries(kind, opts = {}) {
|
|
|
128139
128686
|
const home = opts.home ?? homedir14();
|
|
128140
128687
|
const prefix = kind.labelPrefix;
|
|
128141
128688
|
if (platform === "darwin") {
|
|
128142
|
-
const dir =
|
|
128143
|
-
if (!
|
|
128144
|
-
return
|
|
128689
|
+
const dir = join25(home, "Library", "LaunchAgents");
|
|
128690
|
+
if (!existsSync17(dir)) return [];
|
|
128691
|
+
return readdirSync6(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".plist")).map((f) => {
|
|
128145
128692
|
const slug = f.slice(prefix.length, -".plist".length);
|
|
128146
128693
|
let envKeys = [];
|
|
128147
128694
|
try {
|
|
128148
|
-
envKeys = plistEnvKeys(String(
|
|
128695
|
+
envKeys = plistEnvKeys(String(readFileSync16(join25(dir, f), "utf-8")));
|
|
128149
128696
|
} catch {
|
|
128150
128697
|
envKeys = [];
|
|
128151
128698
|
}
|