tenjin-cli 0.1.0-alpha.1 → 0.1.0-alpha.3
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/NOTICE.md +33 -0
- package/README.md +167 -16
- package/dist/{_esm-ZH2J4QEH.js → _esm-C5CRWCYU.js} +2 -2
- package/dist/_esm-FEASBWRP.js +881 -0
- package/dist/buy-EE6SEXP6.js +1598 -0
- package/dist/buy-EE6SEXP6.js.map +1 -0
- package/dist/candidate-ERMSYYQZ.js +127 -0
- package/dist/candidate-ERMSYYQZ.js.map +1 -0
- package/dist/{ccip-UCLT5VOW.js → ccip-NQXCEK2U.js} +6 -5
- package/dist/ccip-XR5NE5QK.js +19 -0
- package/dist/chunk-2I4QTOLB.js +11 -0
- package/dist/chunk-2I4QTOLB.js.map +1 -0
- package/dist/chunk-3OV7E5G5.js +18 -0
- package/dist/chunk-3OV7E5G5.js.map +1 -0
- package/dist/chunk-4ONXEEQL.js +152 -0
- package/dist/chunk-4ONXEEQL.js.map +1 -0
- package/dist/chunk-5CLSD5YN.js +209 -0
- package/dist/chunk-5CLSD5YN.js.map +1 -0
- package/dist/chunk-662MSGZB.js +286 -0
- package/dist/chunk-662MSGZB.js.map +1 -0
- package/dist/chunk-7KUW4MJD.js +59 -0
- package/dist/chunk-7KUW4MJD.js.map +1 -0
- package/dist/{doctor-3QBYN46N.js → chunk-AWPAWHNX.js} +88 -93
- package/dist/chunk-AWPAWHNX.js.map +1 -0
- package/dist/chunk-CYOF7ROH.js +70 -0
- package/dist/chunk-CYOF7ROH.js.map +1 -0
- package/dist/chunk-DXTQ3CSI.js +171 -0
- package/dist/chunk-DXTQ3CSI.js.map +1 -0
- package/dist/{chunk-EZROUI5U.js → chunk-ESPNTPQ2.js} +80 -8
- package/dist/chunk-ESPNTPQ2.js.map +1 -0
- package/dist/chunk-FJPLRISB.js +79 -0
- package/dist/chunk-FJPLRISB.js.map +1 -0
- package/dist/chunk-GO75V3NZ.js +160 -0
- package/dist/chunk-GO75V3NZ.js.map +1 -0
- package/dist/chunk-HIVZG55D.js +204 -0
- package/dist/chunk-HIVZG55D.js.map +1 -0
- package/dist/chunk-HSDCI7OV.js +7806 -0
- package/dist/chunk-HSDCI7OV.js.map +1 -0
- package/dist/chunk-JPHG2JW3.js +96 -0
- package/dist/chunk-JPHG2JW3.js.map +1 -0
- package/dist/{chunk-ZPZFPVDN.js → chunk-MUBUQVC3.js} +1 -1
- package/dist/chunk-MUBUQVC3.js.map +1 -0
- package/dist/{chunk-T4LKNY7T.js → chunk-NCE5NSN6.js} +166 -11
- package/dist/chunk-NCE5NSN6.js.map +1 -0
- package/dist/{chunk-YG2DO46P.js → chunk-OYMYCW7J.js} +18 -3
- package/dist/{chunk-YG2DO46P.js.map → chunk-OYMYCW7J.js.map} +1 -1
- package/dist/{chunk-Q53IYV2G.js → chunk-QVX5MWJZ.js} +20 -17
- package/dist/{chunk-Q53IYV2G.js.map → chunk-QVX5MWJZ.js.map} +1 -1
- package/dist/{chunk-CUWUHTIL.js → chunk-RBJPAZFI.js} +16 -202
- package/dist/chunk-RBJPAZFI.js.map +1 -0
- package/dist/chunk-RRN3YTK7.js +6199 -0
- package/dist/chunk-RRN3YTK7.js.map +1 -0
- package/dist/chunk-RY4XKNV3.js +140 -0
- package/dist/chunk-RY4XKNV3.js.map +1 -0
- package/dist/chunk-S3IGX57D.js +14682 -0
- package/dist/chunk-S3IGX57D.js.map +1 -0
- package/dist/chunk-TLCMMYLY.js +126 -0
- package/dist/chunk-TLCMMYLY.js.map +1 -0
- package/dist/{chunk-IPOWB3G3.js → chunk-UGX7YPX2.js} +3314 -306
- package/dist/chunk-UGX7YPX2.js.map +1 -0
- package/dist/chunk-UO22YNY4.js +69 -0
- package/dist/chunk-UO22YNY4.js.map +1 -0
- package/dist/{config-CEFTL5ZL.js → chunk-XHC6FCGZ.js} +115 -18
- package/dist/chunk-XHC6FCGZ.js.map +1 -0
- package/dist/chunk-XJIW7USF.js +217 -0
- package/dist/chunk-XJIW7USF.js.map +1 -0
- package/dist/{chunk-BRJHUQSY.js → chunk-YALGASEU.js} +1548 -1452
- package/dist/chunk-YALGASEU.js.map +1 -0
- package/dist/{chunk-DXPYFVKG.js → chunk-YN5SHIXQ.js} +6 -6
- package/dist/chunk-YN5SHIXQ.js.map +1 -0
- package/dist/chunk-Z52RA3NC.js +137 -0
- package/dist/chunk-Z52RA3NC.js.map +1 -0
- package/dist/{chunk-EXBMF5X7.js → chunk-Z6MFJV5Z.js} +151 -19
- package/dist/chunk-Z6MFJV5Z.js.map +1 -0
- package/dist/chunk-ZABPQJ3M.js +83 -0
- package/dist/chunk-ZABPQJ3M.js.map +1 -0
- package/dist/{chunk-LPRVHM7P.js → chunk-ZMHYXMO6.js} +1655 -114
- package/dist/chunk-ZMHYXMO6.js.map +1 -0
- package/dist/{chunk-3IMLRUZE.js → chunk-ZXTAQYNA.js} +6 -2
- package/dist/{cli-WA65NP4Y.js → cli-SBXI5AMS.js} +177 -177
- package/dist/cli-SBXI5AMS.js.map +1 -0
- package/dist/config-VELDKLHY.js +21 -0
- package/dist/doctor-T3KWNNP2.js +20 -0
- package/dist/doctor-T3KWNNP2.js.map +1 -0
- package/dist/ethersCompat-Df4a6QIZ-3YZIAXDK.js +18 -0
- package/dist/ethersCompat-Df4a6QIZ-3YZIAXDK.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/inspect-LMKOXE4Y.js +96 -0
- package/dist/inspect-LMKOXE4Y.js.map +1 -0
- package/dist/install-LEX3ENQ6.js +387 -0
- package/dist/install-LEX3ENQ6.js.map +1 -0
- package/dist/lookup-ZPN2Q67N.js +129 -0
- package/dist/lookup-ZPN2Q67N.js.map +1 -0
- package/dist/{mine.wasm-GQECMSGN.js → mine.wasm-6PIT3DK6.js} +2 -2
- package/dist/outcome-N7TAK6WR.js +66 -0
- package/dist/outcome-N7TAK6WR.js.map +1 -0
- package/dist/publish-BKBQQAW4.js +1478 -0
- package/dist/publish-BKBQQAW4.js.map +1 -0
- package/dist/{secp256k1-BEZS642W.js → secp256k1-N3ZHU7LB.js} +5 -4
- package/dist/secp256k1-N3ZHU7LB.js.map +1 -0
- package/dist/usdc-JIAJW4SV.js +22 -0
- package/dist/usdc-JIAJW4SV.js.map +1 -0
- package/dist/viemAdapter-By13KInL-ZZE6UV4Y.js +14 -0
- package/dist/viemAdapter-By13KInL-ZZE6UV4Y.js.map +1 -0
- package/dist/{wallet-XD576HF3.js → wallet-CWEMYVIH.js} +28 -22
- package/dist/{wallet-XD576HF3.js.map → wallet-CWEMYVIH.js.map} +1 -1
- package/dist/wallet-PA54CRBL.js +30 -0
- package/dist/wallet-PA54CRBL.js.map +1 -0
- package/package.json +19 -16
- package/skills/tenjin/SKILL.md +199 -0
- package/skills/tenjin-publish/SKILL.md +111 -0
- package/skills/tenjin-search/SKILL.md +133 -0
- package/dist/chunk-BRJHUQSY.js.map +0 -1
- package/dist/chunk-CUWUHTIL.js.map +0 -1
- package/dist/chunk-DXPYFVKG.js.map +0 -1
- package/dist/chunk-EXBMF5X7.js.map +0 -1
- package/dist/chunk-EZROUI5U.js.map +0 -1
- package/dist/chunk-IPOWB3G3.js.map +0 -1
- package/dist/chunk-LPRVHM7P.js.map +0 -1
- package/dist/chunk-T4LKNY7T.js.map +0 -1
- package/dist/chunk-ZPZFPVDN.js.map +0 -1
- package/dist/cli-WA65NP4Y.js.map +0 -1
- package/dist/config-CEFTL5ZL.js.map +0 -1
- package/dist/doctor-3QBYN46N.js.map +0 -1
- package/dist/usdc-44R6XZF2.js +0 -18
- package/dist/wallet-ELBTBMXO.js +0 -20
- /package/dist/{_esm-ZH2J4QEH.js.map → _esm-C5CRWCYU.js.map} +0 -0
- /package/dist/{ccip-UCLT5VOW.js.map → _esm-FEASBWRP.js.map} +0 -0
- /package/dist/{chunk-3IMLRUZE.js.map → ccip-NQXCEK2U.js.map} +0 -0
- /package/dist/{secp256k1-BEZS642W.js.map → ccip-XR5NE5QK.js.map} +0 -0
- /package/dist/{usdc-44R6XZF2.js.map → chunk-ZXTAQYNA.js.map} +0 -0
- /package/dist/{wallet-ELBTBMXO.js.map → config-VELDKLHY.js.map} +0 -0
- /package/dist/{mine.wasm-GQECMSGN.js.map → mine.wasm-6PIT3DK6.js.map} +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
hasCode
|
|
4
|
+
} from "./chunk-2I4QTOLB.js";
|
|
2
5
|
import {
|
|
3
6
|
writeFileAtomicExclusive
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MUBUQVC3.js";
|
|
5
8
|
import {
|
|
6
9
|
CliError,
|
|
7
10
|
external_exports,
|
|
8
11
|
walletPath
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OYMYCW7J.js";
|
|
10
13
|
|
|
11
14
|
// src/lib/wallet/store.ts
|
|
12
15
|
import { readFile, stat } from "fs/promises";
|
|
@@ -112,9 +115,6 @@ function isPreEncryptionRecord(json) {
|
|
|
112
115
|
function isNotFound(err) {
|
|
113
116
|
return hasCode(err, "ENOENT");
|
|
114
117
|
}
|
|
115
|
-
function hasCode(err, code) {
|
|
116
|
-
return typeof err === "object" && err !== null && "code" in err && err.code === code;
|
|
117
|
-
}
|
|
118
118
|
|
|
119
119
|
export {
|
|
120
120
|
PRIVATE_KEY_RE,
|
|
@@ -123,4 +123,4 @@ export {
|
|
|
123
123
|
readWalletRecord,
|
|
124
124
|
writeWalletRecord
|
|
125
125
|
};
|
|
126
|
-
//# sourceMappingURL=chunk-
|
|
126
|
+
//# sourceMappingURL=chunk-YN5SHIXQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/wallet/store.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises';\nimport { z } from 'zod';\nimport * as Keystore from 'ox/Keystore';\nimport { CliError } from '../errors';\nimport { writeFileAtomicExclusive } from '../atomic-json';\nimport { hasCode } from '../errno';\nimport { walletPath } from '../paths';\n\n/** A 0x-prefixed 32-byte hex private key (case-insensitive). */\nexport const PRIVATE_KEY_RE = /^0x[0-9a-f]{64}$/i;\nconst ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;\n\n/**\n * The at-rest shape of a Keystore v3 document (Web3 Secret Storage). Validated\n * structurally on read so an obviously-corrupt keystore fails as\n * WALLET_INVALID_KEY before we ever ask for a passphrase; the real integrity\n * check is the keystore's own MAC, verified by ox at decrypt time. `kdfparams`\n * stays an open object because scrypt and pbkdf2 carry different fields and only\n * ox needs to interpret them.\n */\nconst KeystoreV3Schema = z.object({\n crypto: z.object({\n cipher: z.literal('aes-128-ctr'),\n ciphertext: z.string(),\n cipherparams: z.object({ iv: z.string() }),\n kdf: z.enum(['scrypt', 'pbkdf2']),\n kdfparams: z.record(z.string(), z.unknown()),\n mac: z.string(),\n }),\n id: z.string(),\n version: z.literal(3),\n});\n\n/**\n * The persisted wallet record (schema v2). The private key is NEVER stored in\n * cleartext: `keystore` is a Keystore v3 document (scrypt + AES-128-CTR) and the\n * key is recovered only by decrypting it with the wallet passphrase. `address`\n * stays top-level in cleartext ON PURPOSE so `show`/`balance`/`doctor` keep\n * working without a passphrase; only signing decrypts. The `provider`\n * discriminator keeps the schema from implying every future wallet embeds a\n * keystore. Validated on read (a corrupt file is WALLET_INVALID_KEY, never a\n * silent partial parse).\n */\nexport const WalletRecordSchema = z.object({\n schemaVersion: z.literal(2),\n provider: z.literal('local'),\n address: z.string().regex(ADDRESS_RE, 'expected a 0x-prefixed 20-byte address'),\n keystore: KeystoreV3Schema,\n createdAt: z.string(),\n});\nexport type WalletRecord = Omit<z.infer<typeof WalletRecordSchema>, 'keystore'> & {\n keystore: Keystore.Keystore;\n};\n\nexport async function walletFileExists(dir: string): Promise<boolean> {\n return (await walletFileMode(dir)) !== null;\n}\n\n/** File permission bits (`mode & 0o777`), or null when the wallet file is absent. */\nexport async function walletFileMode(dir: string): Promise<number | null> {\n try {\n return (await stat(walletPath(dir))).mode & 0o777;\n } catch (err) {\n if (isNotFound(err)) return null;\n throw err;\n }\n}\n\n/** Read + validate the wallet record; null when absent, WALLET_INVALID_KEY when corrupt. */\nexport async function readWalletRecord(dir: string): Promise<WalletRecord | null> {\n const path = walletPath(dir);\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n if (isNotFound(err)) return null;\n throw new CliError('WALLET_INVALID_KEY', `Could not read the wallet file at ${path}.`, {\n fix: `Check file permissions on ${path}.`,\n cause: err,\n });\n }\n let json: unknown;\n try {\n json = JSON.parse(raw);\n } catch (err) {\n throw new CliError('WALLET_INVALID_KEY', `The wallet file at ${path} is not valid JSON.`, {\n fix: `Move ${path} aside, then run \\`tenjin wallet create\\`.`,\n cause: err,\n });\n }\n // A pre-encryption record (schema v1 stored the raw key). There is no in-place\n // migration: the old key must be swept and a fresh encrypted wallet created.\n if (isPreEncryptionRecord(json)) {\n throw new CliError(\n 'WALLET_INVALID_KEY',\n `The wallet file at ${path} predates encrypted storage.`,\n {\n fix: `Move ${path} aside and run \\`tenjin wallet create\\`; sweep any funds from the old address first.`,\n },\n );\n }\n const parsed = WalletRecordSchema.safeParse(json);\n if (!parsed.success) {\n throw new CliError(\n 'WALLET_INVALID_KEY',\n `The wallet file at ${path} is not a valid wallet record.`,\n {\n fix: `Move ${path} aside, then run \\`tenjin wallet create\\`.`,\n details: parsed.error.issues,\n },\n );\n }\n return parsed.data as WalletRecord;\n}\n\n/**\n * Persist a validated record at 0600 in a 0700 dir, NO-CLOBBER. The exclusive\n * write — not an earlier existence check — is the authority: two concurrent\n * `create` runs can both pass a pre-check, but only one can win the atomic\n * commit; the loser surfaces as WALLET_EXISTS instead of silently overwriting\n * (and losing) a non-recoverable key.\n */\nexport async function writeWalletRecord(dir: string, record: WalletRecord): Promise<void> {\n const validated = WalletRecordSchema.parse(record);\n const path = walletPath(dir);\n try {\n await writeFileAtomicExclusive(path, `${JSON.stringify(validated, null, 2)}\\n`, {\n mode: 0o600,\n dirMode: 0o700,\n });\n } catch (err) {\n if (hasCode(err, 'EEXIST')) {\n throw new CliError('WALLET_EXISTS', `A wallet already exists at ${path}.`, {\n fix: `Keys are non-recoverable; move it aside first (e.g. \\`mv ${path} ${path}.bak\\`) to create a new one.`,\n cause: err,\n });\n }\n throw err;\n }\n}\n\n/** A cleartext-key record from before encrypted storage: schema v1 or a bare `privateKey`. */\nfunction isPreEncryptionRecord(json: unknown): boolean {\n if (typeof json !== 'object' || json === null) return false;\n const rec = json as Record<string, unknown>;\n return rec.schemaVersion === 1 || 'privateKey' in rec;\n}\n\nfunction isNotFound(err: unknown): boolean {\n return hasCode(err, 'ENOENT');\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
CliError,
|
|
4
|
+
external_exports
|
|
5
|
+
} from "./chunk-OYMYCW7J.js";
|
|
6
|
+
|
|
7
|
+
// src/lib/output.ts
|
|
8
|
+
import { styleText } from "util";
|
|
9
|
+
import { Stream } from "stream";
|
|
10
|
+
|
|
11
|
+
// src/schemas.ts
|
|
12
|
+
var SCHEMA_VERSION = 1;
|
|
13
|
+
var ErrorCodeSchema = external_exports.enum([
|
|
14
|
+
"NODE_UNSUPPORTED",
|
|
15
|
+
"USAGE",
|
|
16
|
+
"CONFIG_INVALID",
|
|
17
|
+
"WALLET_EXISTS",
|
|
18
|
+
"WALLET_MISSING",
|
|
19
|
+
"WALLET_INVALID_KEY",
|
|
20
|
+
"PROVIDER_ERROR",
|
|
21
|
+
"RPC_ERROR",
|
|
22
|
+
"API_UNREACHABLE",
|
|
23
|
+
"CONTRACT_MISMATCH",
|
|
24
|
+
"REFUSED",
|
|
25
|
+
"NETWORK_ERROR",
|
|
26
|
+
"INTERNAL",
|
|
27
|
+
"NOT_IMPLEMENTED",
|
|
28
|
+
// B2 (lookup/inspect/buy/outcome): a policy refusal (exit 3) has its own code
|
|
29
|
+
// so an agent can distinguish a spend-cap block from a generic REFUSED; a
|
|
30
|
+
// payment that failed after approval (exit 4) is PAYMENT_FAILED; the read
|
|
31
|
+
// route's own coded failures (402 preview parse, 409 gate) surface as these.
|
|
32
|
+
"POLICY_REFUSED",
|
|
33
|
+
"PAYMENT_FAILED",
|
|
34
|
+
"RESOURCE_NOT_FOUND",
|
|
35
|
+
"LOOKUP_NOT_FOUND",
|
|
36
|
+
// A 429 from the anonymous lookup/outcome/read limits; error.details carries
|
|
37
|
+
// retryAfterSeconds so a looping agent can back off instead of hammering.
|
|
38
|
+
"RATE_LIMITED",
|
|
39
|
+
// B3 (publish): a publish that needs the human's yes (exit 3) — soft scan
|
|
40
|
+
// findings, or `review` mode always. NEEDS_CONFIRMATION clears with --yes;
|
|
41
|
+
// PUBLISH_BLOCKED is a hard-block finding (a live secret) that no mode or --yes
|
|
42
|
+
// ever clears. PUBLISH_FAILED (exit 4) is a write that failed AFTER approval,
|
|
43
|
+
// mirroring PAYMENT_FAILED's post-decision failure class.
|
|
44
|
+
"NEEDS_CONFIRMATION",
|
|
45
|
+
"PUBLISH_BLOCKED",
|
|
46
|
+
"PUBLISH_FAILED"
|
|
47
|
+
]);
|
|
48
|
+
var MoneySchema = external_exports.object({
|
|
49
|
+
atomic: external_exports.string(),
|
|
50
|
+
usd: external_exports.string()
|
|
51
|
+
});
|
|
52
|
+
var OutputErrorSchema = external_exports.object({
|
|
53
|
+
code: ErrorCodeSchema,
|
|
54
|
+
message: external_exports.string(),
|
|
55
|
+
fix: external_exports.string().optional(),
|
|
56
|
+
details: external_exports.unknown().optional()
|
|
57
|
+
});
|
|
58
|
+
var SuccessEnvelopeSchema = external_exports.object({
|
|
59
|
+
schemaVersion: external_exports.literal(SCHEMA_VERSION),
|
|
60
|
+
command: external_exports.string(),
|
|
61
|
+
ok: external_exports.literal(true),
|
|
62
|
+
data: external_exports.unknown()
|
|
63
|
+
});
|
|
64
|
+
var FailureEnvelopeSchema = external_exports.object({
|
|
65
|
+
schemaVersion: external_exports.literal(SCHEMA_VERSION),
|
|
66
|
+
command: external_exports.string(),
|
|
67
|
+
ok: external_exports.literal(false),
|
|
68
|
+
error: OutputErrorSchema
|
|
69
|
+
});
|
|
70
|
+
var OutputEnvelopeSchema = external_exports.discriminatedUnion("ok", [
|
|
71
|
+
SuccessEnvelopeSchema,
|
|
72
|
+
FailureEnvelopeSchema
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
// src/lib/output.ts
|
|
76
|
+
function defaultIo() {
|
|
77
|
+
return {
|
|
78
|
+
stdout: process.stdout,
|
|
79
|
+
stderr: process.stderr,
|
|
80
|
+
isTTY: Boolean(process.stdout.isTTY)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function emitSuccess(io, command, data, humanLines = [], opts = {}) {
|
|
84
|
+
writeJson(io.stdout, { schemaVersion: SCHEMA_VERSION, command, ok: true, data });
|
|
85
|
+
if (shouldRenderHuman(io, opts) && humanLines.length > 0) {
|
|
86
|
+
writeLines(io.stderr, humanLines);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function emitFailure(io, command, err, opts = {}) {
|
|
90
|
+
const cliErr = normalizeError(err);
|
|
91
|
+
const error = {
|
|
92
|
+
code: cliErr.code,
|
|
93
|
+
message: cliErr.message,
|
|
94
|
+
...cliErr.fix !== void 0 ? { fix: cliErr.fix } : {},
|
|
95
|
+
...cliErr.details !== void 0 ? { details: cliErr.details } : {}
|
|
96
|
+
};
|
|
97
|
+
writeJson(io.stdout, { schemaVersion: SCHEMA_VERSION, command, ok: false, error });
|
|
98
|
+
if (shouldRenderHuman(io, opts)) {
|
|
99
|
+
const lines = [paint(io, "red", `error: ${sanitizeForTerminal(cliErr.message)}`)];
|
|
100
|
+
if (cliErr.fix !== void 0) {
|
|
101
|
+
lines.push(paint(io, "dim", `fix: ${sanitizeForTerminal(cliErr.fix)}`));
|
|
102
|
+
}
|
|
103
|
+
writeLines(io.stderr, lines);
|
|
104
|
+
}
|
|
105
|
+
return cliErr;
|
|
106
|
+
}
|
|
107
|
+
function sanitizeForTerminal(text) {
|
|
108
|
+
return text.replace(/\x1b(?:\[[0-9;?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-Z\\-_])/g, "").replace(/[\x00-\x08\x0a-\x1f\x7f-\x9f]/g, "");
|
|
109
|
+
}
|
|
110
|
+
function normalizeError(err) {
|
|
111
|
+
if (err instanceof CliError) return err;
|
|
112
|
+
if (err instanceof Error) return new CliError("INTERNAL", err.message, { cause: err });
|
|
113
|
+
return new CliError("INTERNAL", "Unexpected error", { details: err });
|
|
114
|
+
}
|
|
115
|
+
function shouldRenderHuman(io, opts) {
|
|
116
|
+
return io.isTTY && opts.json !== true;
|
|
117
|
+
}
|
|
118
|
+
function writeJson(stream, value) {
|
|
119
|
+
stream.write(`${JSON.stringify(value, null, 2)}
|
|
120
|
+
`);
|
|
121
|
+
}
|
|
122
|
+
function writeLines(stream, lines) {
|
|
123
|
+
if (lines.length > 0) stream.write(`${lines.join("\n")}
|
|
124
|
+
`);
|
|
125
|
+
}
|
|
126
|
+
function paint(io, format, text) {
|
|
127
|
+
if (io.stderr instanceof Stream) return styleText(format, text, { stream: io.stderr });
|
|
128
|
+
return styleText(format, text);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
defaultIo,
|
|
133
|
+
emitSuccess,
|
|
134
|
+
emitFailure,
|
|
135
|
+
sanitizeForTerminal
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-Z52RA3NC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/output.ts","../src/schemas.ts"],"sourcesContent":["import { styleText } from 'node:util';\nimport { Stream } from 'node:stream';\nimport { CliError } from './errors';\nimport { SCHEMA_VERSION } from '../schemas';\nimport type { OutputError } from '../schemas';\n\n/**\n * Injected streams + TTY fact. Every command receives one via CommandContext,\n * so tests drive the CLI in-process with memory buffers and no child process.\n * `isTTY` is the real terminal fact; the separate `--json` flag (passed to the\n * emit functions) is what suppresses human rendering — the two are ANDed, never\n * conflated, so `--json` on a TTY still yields pure machine output.\n */\nexport interface Io {\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n isTTY: boolean;\n}\n\nexport function defaultIo(): Io {\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n isTTY: Boolean(process.stdout.isTTY),\n };\n}\n\nexport interface EmitOptions {\n /** The global --json flag. When true, all stderr human rendering is suppressed. */\n json?: boolean;\n}\n\n/**\n * Emit exactly one pretty-printed success envelope to stdout. When on a TTY and\n * --json is off, the optional human lines are rendered to stderr (never stdout —\n * stdout stays a clean single JSON object for the agent that piped us).\n */\nexport function emitSuccess(\n io: Io,\n command: string,\n data: unknown,\n humanLines: string[] = [],\n opts: EmitOptions = {},\n): void {\n writeJson(io.stdout, { schemaVersion: SCHEMA_VERSION, command, ok: true, data });\n if (shouldRenderHuman(io, opts) && humanLines.length > 0) {\n writeLines(io.stderr, humanLines);\n }\n}\n\n/**\n * Emit exactly one pretty-printed failure envelope to stdout and (on a TTY, with\n * --json off) a red error line plus a `fix` line to stderr. Accepts a CliError or\n * any thrown value; anything that is not a CliError normalizes to INTERNAL.\n * Returns the normalized CliError so the caller can read its `exitCode`.\n */\nexport function emitFailure(\n io: Io,\n command: string,\n err: unknown,\n opts: EmitOptions = {},\n): CliError {\n const cliErr = normalizeError(err);\n const error: OutputError = {\n code: cliErr.code,\n message: cliErr.message,\n ...(cliErr.fix !== undefined ? { fix: cliErr.fix } : {}),\n ...(cliErr.details !== undefined ? { details: cliErr.details } : {}),\n };\n writeJson(io.stdout, { schemaVersion: SCHEMA_VERSION, command, ok: false, error });\n if (shouldRenderHuman(io, opts)) {\n // Error messages can embed server-sourced text (api error passthrough) and\n // never carry intentional ANSI, so sanitize before painting.\n const lines = [paint(io, 'red', `error: ${sanitizeForTerminal(cliErr.message)}`)];\n if (cliErr.fix !== undefined) {\n lines.push(paint(io, 'dim', `fix: ${sanitizeForTerminal(cliErr.fix)}`));\n }\n writeLines(io.stderr, lines);\n }\n return cliErr;\n}\n\n/**\n * Strip C0/C1 control characters and ANSI escape sequences from a string headed\n * for a terminal. Commands apply this to every SERVER-sourced string (titles,\n * handles) they put into human lines or the buy confirm prompt; without it a\n * malicious deployment could use cursor-movement escapes to overwrite the very\n * price a human is being asked to confirm. It is not applied to whole human\n * lines here because trusted callers (doctor) paint their own ANSI colors. JSON\n * stdout is untouched (JSON.stringify escapes control characters itself).\n */\nexport function sanitizeForTerminal(text: string): string {\n return (\n text\n // CSI/OSC/charset escape sequences first, then any stray ESC and the rest\n // of C0 (except \\t) plus DEL and the C1 range.\n // eslint-disable-next-line no-control-regex\n .replace(/\\x1b(?:\\[[0-9;?]*[ -/]*[@-~]|\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|[@-Z\\\\-_])/g, '')\n // eslint-disable-next-line no-control-regex\n .replace(/[\\x00-\\x08\\x0a-\\x1f\\x7f-\\x9f]/g, '')\n );\n}\n\n/** CliError passes through; every other thrown value becomes an INTERNAL CliError. */\nexport function normalizeError(err: unknown): CliError {\n if (err instanceof CliError) return err;\n if (err instanceof Error) return new CliError('INTERNAL', err.message, { cause: err });\n return new CliError('INTERNAL', 'Unexpected error', { details: err });\n}\n\nfunction shouldRenderHuman(io: Io, opts: EmitOptions): boolean {\n return io.isTTY && opts.json !== true;\n}\n\nfunction writeJson(stream: NodeJS.WritableStream, value: unknown): void {\n stream.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction writeLines(stream: NodeJS.WritableStream, lines: string[]): void {\n if (lines.length > 0) stream.write(`${lines.join('\\n')}\\n`);\n}\n\n/**\n * Color the human line for stderr. styleText emits ANSI only when the target\n * stream is color-capable (a real TTY with color depth) and honors\n * NO_COLOR/FORCE_COLOR natively (verified on Node 22/23). The `stream` option is\n * passed only when stderr is a genuine Stream — styleText throws on anything\n * else, and a test/redirected sink is not one, so it falls back to the default\n * (process.stdout) capability check and simply comes out plain.\n */\nfunction paint(io: Io, format: Parameters<typeof styleText>[0], text: string): string {\n if (io.stderr instanceof Stream) return styleText(format, text, { stream: io.stderr });\n return styleText(format, text);\n}\n","import { z } from 'zod';\n\n/**\n * Bumped only on a breaking change to any stdout envelope shape. Agents pin on\n * this: a consumer that sees an unexpected value should refuse to parse rather\n * than guess. Every envelope carries it, success or failure.\n */\nexport const SCHEMA_VERSION = 1;\n\n/**\n * The closed set of machine error codes. `error.code` in a failure envelope is\n * always one of these; agents branch on it, humans read `message`/`fix`. The\n * exit-code mapping lives in lib/errors.ts (this enum is the single source of\n * the code names both sides agree on).\n */\nexport const ErrorCodeSchema = z.enum([\n 'NODE_UNSUPPORTED',\n 'USAGE',\n 'CONFIG_INVALID',\n 'WALLET_EXISTS',\n 'WALLET_MISSING',\n 'WALLET_INVALID_KEY',\n 'PROVIDER_ERROR',\n 'RPC_ERROR',\n 'API_UNREACHABLE',\n 'CONTRACT_MISMATCH',\n 'REFUSED',\n 'NETWORK_ERROR',\n 'INTERNAL',\n 'NOT_IMPLEMENTED',\n // B2 (lookup/inspect/buy/outcome): a policy refusal (exit 3) has its own code\n // so an agent can distinguish a spend-cap block from a generic REFUSED; a\n // payment that failed after approval (exit 4) is PAYMENT_FAILED; the read\n // route's own coded failures (402 preview parse, 409 gate) surface as these.\n 'POLICY_REFUSED',\n 'PAYMENT_FAILED',\n 'RESOURCE_NOT_FOUND',\n 'LOOKUP_NOT_FOUND',\n // A 429 from the anonymous lookup/outcome/read limits; error.details carries\n // retryAfterSeconds so a looping agent can back off instead of hammering.\n 'RATE_LIMITED',\n // B3 (publish): a publish that needs the human's yes (exit 3) — soft scan\n // findings, or `review` mode always. NEEDS_CONFIRMATION clears with --yes;\n // PUBLISH_BLOCKED is a hard-block finding (a live secret) that no mode or --yes\n // ever clears. PUBLISH_FAILED (exit 4) is a write that failed AFTER approval,\n // mirroring PAYMENT_FAILED's post-decision failure class.\n 'NEEDS_CONFIRMATION',\n 'PUBLISH_BLOCKED',\n 'PUBLISH_FAILED',\n]);\nexport type ErrorCode = z.infer<typeof ErrorCodeSchema>;\n\n/**\n * All money in machine output is dual-form: `atomic` is the 6-decimal USDC base\n * unit as a decimal string (never a JS number — precision), `usd` is the same\n * value formatted as decimal dollars. Emit both so agents compute on `atomic`\n * and humans read `usd` without either side re-deriving the other.\n */\nexport const MoneySchema = z.object({\n atomic: z.string(),\n usd: z.string(),\n});\nexport type Money = z.infer<typeof MoneySchema>;\n\nexport const OutputErrorSchema = z.object({\n code: ErrorCodeSchema,\n message: z.string(),\n fix: z.string().optional(),\n details: z.unknown().optional(),\n});\nexport type OutputError = z.infer<typeof OutputErrorSchema>;\n\nexport const SuccessEnvelopeSchema = z.object({\n schemaVersion: z.literal(SCHEMA_VERSION),\n command: z.string(),\n ok: z.literal(true),\n data: z.unknown(),\n});\nexport type SuccessEnvelope = z.infer<typeof SuccessEnvelopeSchema>;\n\nexport const FailureEnvelopeSchema = z.object({\n schemaVersion: z.literal(SCHEMA_VERSION),\n command: z.string(),\n ok: z.literal(false),\n error: OutputErrorSchema,\n});\nexport type FailureEnvelope = z.infer<typeof FailureEnvelopeSchema>;\n\n/** The one thing every invocation prints to stdout, discriminated on `ok`. */\nexport const OutputEnvelopeSchema = z.discriminatedUnion('ok', [\n SuccessEnvelopeSchema,\n FailureEnvelopeSchema,\n]);\nexport type OutputEnvelope = z.infer<typeof OutputEnvelopeSchema>;\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { createRequire as __tenjinCreateRequire } from 'node:module'; const require = __tenjinCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
PRIVATE_KEY_RE,
|
|
4
|
+
readWalletRecord,
|
|
5
|
+
walletFileMode,
|
|
6
|
+
writeWalletRecord
|
|
7
|
+
} from "./chunk-YN5SHIXQ.js";
|
|
8
|
+
import {
|
|
9
|
+
withFileLock
|
|
10
|
+
} from "./chunk-YBMMV4Y4.js";
|
|
11
|
+
import {
|
|
12
|
+
evaluateSpendPolicy
|
|
13
|
+
} from "./chunk-UO22YNY4.js";
|
|
14
|
+
import {
|
|
15
|
+
writeFileAtomic
|
|
16
|
+
} from "./chunk-MUBUQVC3.js";
|
|
17
|
+
import {
|
|
18
|
+
CliError,
|
|
19
|
+
external_exports,
|
|
20
|
+
passphraseBlobPath,
|
|
21
|
+
walletPath
|
|
22
|
+
} from "./chunk-OYMYCW7J.js";
|
|
2
23
|
import {
|
|
3
24
|
hashAuthorization,
|
|
4
25
|
hashMessage,
|
|
@@ -7,12 +28,12 @@ import {
|
|
|
7
28
|
publicKeyToAddress,
|
|
8
29
|
serializeSignature,
|
|
9
30
|
serializeTransaction
|
|
10
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-NCE5NSN6.js";
|
|
11
32
|
import {
|
|
12
33
|
hmac,
|
|
13
34
|
secp256k1,
|
|
14
35
|
sha256
|
|
15
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-QVX5MWJZ.js";
|
|
16
37
|
import {
|
|
17
38
|
InvalidAddressError,
|
|
18
39
|
concat3 as concat,
|
|
@@ -27,7 +48,7 @@ import {
|
|
|
27
48
|
slice3 as slice,
|
|
28
49
|
toHex,
|
|
29
50
|
toHex2
|
|
30
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-YALGASEU.js";
|
|
31
52
|
import {
|
|
32
53
|
ahash,
|
|
33
54
|
anumber,
|
|
@@ -39,21 +60,7 @@ import {
|
|
|
39
60
|
rotl,
|
|
40
61
|
swap32IfBE,
|
|
41
62
|
u32
|
|
42
|
-
} from "./chunk-
|
|
43
|
-
import {
|
|
44
|
-
PRIVATE_KEY_RE,
|
|
45
|
-
readWalletRecord,
|
|
46
|
-
walletFileMode,
|
|
47
|
-
writeWalletRecord
|
|
48
|
-
} from "./chunk-DXPYFVKG.js";
|
|
49
|
-
import {
|
|
50
|
-
writeFileAtomic
|
|
51
|
-
} from "./chunk-ZPZFPVDN.js";
|
|
52
|
-
import {
|
|
53
|
-
CliError,
|
|
54
|
-
passphraseBlobPath,
|
|
55
|
-
walletPath
|
|
56
|
-
} from "./chunk-YG2DO46P.js";
|
|
63
|
+
} from "./chunk-5CLSD5YN.js";
|
|
57
64
|
|
|
58
65
|
// node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/pbkdf2.js
|
|
59
66
|
function pbkdf2Init(hash, _password, _salt, _opts) {
|
|
@@ -1166,11 +1173,134 @@ function walletRecord(address, keystore) {
|
|
|
1166
1173
|
};
|
|
1167
1174
|
}
|
|
1168
1175
|
|
|
1176
|
+
// src/lib/wallet/spend.ts
|
|
1177
|
+
import { mkdir, readFile as readFile2 } from "fs/promises";
|
|
1178
|
+
import { randomUUID } from "crypto";
|
|
1179
|
+
import { join } from "path";
|
|
1180
|
+
var DEFAULT_WINDOW_MS = 864e5;
|
|
1181
|
+
var RESERVATION_TTL_MS = 6e5;
|
|
1182
|
+
var ReservationSchema = external_exports.object({
|
|
1183
|
+
id: external_exports.string(),
|
|
1184
|
+
amountAtomic: external_exports.string().regex(/^\d+$/),
|
|
1185
|
+
atMs: external_exports.number()
|
|
1186
|
+
});
|
|
1187
|
+
var LedgerSchema = external_exports.object({
|
|
1188
|
+
schemaVersion: external_exports.literal(2),
|
|
1189
|
+
windowStartMs: external_exports.number(),
|
|
1190
|
+
committedAtomic: external_exports.string().regex(/^\d+$/),
|
|
1191
|
+
reservations: external_exports.array(ReservationSchema)
|
|
1192
|
+
});
|
|
1193
|
+
function emptyLedger(nowMs) {
|
|
1194
|
+
return { schemaVersion: 2, windowStartMs: nowMs, committedAtomic: "0", reservations: [] };
|
|
1195
|
+
}
|
|
1196
|
+
function createLocalSpendAuthorizer(deps) {
|
|
1197
|
+
const windowMs = deps.windowMs ?? DEFAULT_WINDOW_MS;
|
|
1198
|
+
const now = deps.now ?? Date.now;
|
|
1199
|
+
const path = join(deps.dir, "session.json");
|
|
1200
|
+
const lockPath = `${path}.lock`;
|
|
1201
|
+
const freshen = (ledger, nowMs) => {
|
|
1202
|
+
if (ledger === null || nowMs - ledger.windowStartMs >= windowMs) return emptyLedger(nowMs);
|
|
1203
|
+
return {
|
|
1204
|
+
...ledger,
|
|
1205
|
+
reservations: ledger.reservations.filter((r) => nowMs - r.atMs < RESERVATION_TTL_MS)
|
|
1206
|
+
};
|
|
1207
|
+
};
|
|
1208
|
+
const spentOf = (ledger) => ledger.reservations.reduce(
|
|
1209
|
+
(sum, r) => sum + BigInt(r.amountAtomic),
|
|
1210
|
+
BigInt(ledger.committedAtomic)
|
|
1211
|
+
);
|
|
1212
|
+
const persist = async (ledger) => {
|
|
1213
|
+
await writeFileAtomic(path, `${JSON.stringify(ledger, null, 2)}
|
|
1214
|
+
`, {
|
|
1215
|
+
mode: 384,
|
|
1216
|
+
dirMode: 448
|
|
1217
|
+
});
|
|
1218
|
+
};
|
|
1219
|
+
async function withLedger(fn) {
|
|
1220
|
+
await mkdir(deps.dir, { recursive: true, mode: 448 });
|
|
1221
|
+
return withFileLock(lockPath, async () => {
|
|
1222
|
+
const nowMs = now();
|
|
1223
|
+
const ledger = freshen(await readLedger(path), nowMs);
|
|
1224
|
+
return fn(ledger, nowMs);
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
return {
|
|
1228
|
+
policyEnforcement: "client-only",
|
|
1229
|
+
async authorize(req) {
|
|
1230
|
+
return withLedger(async (ledger) => {
|
|
1231
|
+
const sessionSpentAtomic = spentOf(ledger);
|
|
1232
|
+
const evaluation = evaluateSpendPolicy(deps.policy, {
|
|
1233
|
+
amountAtomic: req.amountAtomic,
|
|
1234
|
+
creator: req.creator,
|
|
1235
|
+
...req.maxPriceAtomic !== void 0 ? { maxPriceAtomic: req.maxPriceAtomic } : {},
|
|
1236
|
+
sessionSpentAtomic
|
|
1237
|
+
});
|
|
1238
|
+
const base = {
|
|
1239
|
+
...evaluation,
|
|
1240
|
+
amountAtomic: req.amountAtomic,
|
|
1241
|
+
sessionSpentAtomic,
|
|
1242
|
+
sessionBudgetAtomic: deps.policy.sessionBudgetAtomic,
|
|
1243
|
+
policyEnforcement: "client-only"
|
|
1244
|
+
};
|
|
1245
|
+
if (evaluation.decision === "deny" || deps.policy.sessionBudgetAtomic === 0n) {
|
|
1246
|
+
return base;
|
|
1247
|
+
}
|
|
1248
|
+
const reservation = {
|
|
1249
|
+
id: randomUUID(),
|
|
1250
|
+
amountAtomic: req.amountAtomic.toString(),
|
|
1251
|
+
atMs: now()
|
|
1252
|
+
};
|
|
1253
|
+
await persist({ ...ledger, reservations: [...ledger.reservations, reservation] });
|
|
1254
|
+
return { ...base, reservationId: reservation.id };
|
|
1255
|
+
});
|
|
1256
|
+
},
|
|
1257
|
+
async commit(reservationId, amountAtomic) {
|
|
1258
|
+
await withLedger(async (ledger) => {
|
|
1259
|
+
const reservation = reservationId !== void 0 ? ledger.reservations.find((r) => r.id === reservationId) : void 0;
|
|
1260
|
+
const settled = reservation !== void 0 ? BigInt(reservation.amountAtomic) : amountAtomic;
|
|
1261
|
+
await persist({
|
|
1262
|
+
...ledger,
|
|
1263
|
+
committedAtomic: (BigInt(ledger.committedAtomic) + settled).toString(),
|
|
1264
|
+
reservations: reservationId !== void 0 ? ledger.reservations.filter((r) => r.id !== reservationId) : ledger.reservations
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
},
|
|
1268
|
+
async release(reservationId) {
|
|
1269
|
+
if (reservationId === void 0) return;
|
|
1270
|
+
await withLedger(async (ledger) => {
|
|
1271
|
+
if (!ledger.reservations.some((r) => r.id === reservationId)) return;
|
|
1272
|
+
await persist({
|
|
1273
|
+
...ledger,
|
|
1274
|
+
reservations: ledger.reservations.filter((r) => r.id !== reservationId)
|
|
1275
|
+
});
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
async function readLedger(path) {
|
|
1281
|
+
let raw;
|
|
1282
|
+
try {
|
|
1283
|
+
raw = await readFile2(path, "utf8");
|
|
1284
|
+
} catch {
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
try {
|
|
1288
|
+
const parsed = LedgerSchema.safeParse(JSON.parse(raw));
|
|
1289
|
+
return parsed.success ? parsed.data : null;
|
|
1290
|
+
} catch {
|
|
1291
|
+
return null;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1169
1295
|
// src/lib/wallet/index.ts
|
|
1170
1296
|
function resolveWalletProvider(ctx, opts = {}) {
|
|
1171
1297
|
if (opts.provider !== void 0) return opts.provider;
|
|
1172
1298
|
return createLocalProvider({ dir: ctx.dataDir, env: process.env });
|
|
1173
1299
|
}
|
|
1300
|
+
function resolveSpendAuthorizer(ctx, policy, opts = {}) {
|
|
1301
|
+
if (opts.authorizer !== void 0) return opts.authorizer;
|
|
1302
|
+
return createLocalSpendAuthorizer({ dir: ctx.dataDir, policy });
|
|
1303
|
+
}
|
|
1174
1304
|
async function describeWallet(provider) {
|
|
1175
1305
|
try {
|
|
1176
1306
|
return await provider.describe();
|
|
@@ -1187,7 +1317,9 @@ async function describeWallet(provider) {
|
|
|
1187
1317
|
export {
|
|
1188
1318
|
resolvePassphraseForCreate,
|
|
1189
1319
|
createLocalWallet,
|
|
1320
|
+
createLocalSpendAuthorizer,
|
|
1190
1321
|
resolveWalletProvider,
|
|
1322
|
+
resolveSpendAuthorizer,
|
|
1191
1323
|
describeWallet
|
|
1192
1324
|
};
|
|
1193
1325
|
/*! Bundled license information:
|
|
@@ -1195,4 +1327,4 @@ export {
|
|
|
1195
1327
|
@noble/ciphers/esm/utils.js:
|
|
1196
1328
|
(*! noble-ciphers - MIT License (c) 2023 Paul Miller (paulmillr.com) *)
|
|
1197
1329
|
*/
|
|
1198
|
-
//# sourceMappingURL=chunk-
|
|
1330
|
+
//# sourceMappingURL=chunk-Z6MFJV5Z.js.map
|