@candledottv/cli 0.13.2 → 0.13.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/README.md +2 -0
- package/dist/index.js +1071 -186
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -524,7 +524,7 @@ var init_terminal_theme = __esm(() => {
|
|
|
524
524
|
});
|
|
525
525
|
|
|
526
526
|
// src/version.ts
|
|
527
|
-
var CLI_VERSION = "0.13.
|
|
527
|
+
var CLI_VERSION = "0.13.3";
|
|
528
528
|
|
|
529
529
|
// src/presentation.ts
|
|
530
530
|
function terminalStyle(deps, stream, machine = false) {
|
|
@@ -56836,17 +56836,17 @@ var require_stream = __commonJS((exports) => {
|
|
|
56836
56836
|
this.view[this.start] = char;
|
|
56837
56837
|
this.start += 1;
|
|
56838
56838
|
}
|
|
56839
|
-
appendUint16(
|
|
56839
|
+
appendUint16(num2) {
|
|
56840
56840
|
this.ensureCapacity(2);
|
|
56841
|
-
const value = new Uint16Array([
|
|
56841
|
+
const value = new Uint16Array([num2]);
|
|
56842
56842
|
const view = new Uint8Array(value.buffer);
|
|
56843
56843
|
this.view[this.start] = view[1];
|
|
56844
56844
|
this.view[this.start + 1] = view[0];
|
|
56845
56845
|
this.start += 2;
|
|
56846
56846
|
}
|
|
56847
|
-
appendUint24(
|
|
56847
|
+
appendUint24(num2) {
|
|
56848
56848
|
this.ensureCapacity(3);
|
|
56849
|
-
const value = new Uint32Array([
|
|
56849
|
+
const value = new Uint32Array([num2]);
|
|
56850
56850
|
const view = new Uint8Array(value.buffer);
|
|
56851
56851
|
this.view[this.start] = view[2];
|
|
56852
56852
|
this.view[this.start + 1] = view[1];
|
|
@@ -57091,9 +57091,9 @@ var require_tag = __commonJS((exports) => {
|
|
|
57091
57091
|
isUniversal() {
|
|
57092
57092
|
return this.class === TAG_CLASS.UNIVERSAL;
|
|
57093
57093
|
}
|
|
57094
|
-
isContextSpecific(
|
|
57094
|
+
isContextSpecific(num2) {
|
|
57095
57095
|
const res = this.class === TAG_CLASS.CONTEXT_SPECIFIC;
|
|
57096
|
-
return
|
|
57096
|
+
return num2 !== undefined ? res && this.number === num2 : res;
|
|
57097
57097
|
}
|
|
57098
57098
|
isBoolean() {
|
|
57099
57099
|
return this.isUniversal() && this.number === UNIVERSAL_TAG.BOOLEAN;
|
|
@@ -58780,14 +58780,14 @@ var require_key = __commonJS((exports) => {
|
|
|
58780
58780
|
value: ext.subs[ext.subs.length - 1].value
|
|
58781
58781
|
};
|
|
58782
58782
|
});
|
|
58783
|
-
const
|
|
58783
|
+
const identity4 = {
|
|
58784
58784
|
extensions: { issuer },
|
|
58785
58785
|
subjectAlternativeName: cert.subjectAltName,
|
|
58786
58786
|
oids
|
|
58787
58787
|
};
|
|
58788
58788
|
return {
|
|
58789
58789
|
key: core_1.crypto.createPublicKey(cert.publicKey),
|
|
58790
|
-
identity:
|
|
58790
|
+
identity: identity4
|
|
58791
58791
|
};
|
|
58792
58792
|
}
|
|
58793
58793
|
});
|
|
@@ -59555,8 +59555,8 @@ var require_merkle = __commonJS((exports) => {
|
|
|
59555
59555
|
function innerProofSize(index, size) {
|
|
59556
59556
|
return bitLength(index ^ size - BigInt(1));
|
|
59557
59557
|
}
|
|
59558
|
-
function onesCount(
|
|
59559
|
-
return
|
|
59558
|
+
function onesCount(num2) {
|
|
59559
|
+
return num2.toString(2).split("1").length - 1;
|
|
59560
59560
|
}
|
|
59561
59561
|
function bitLength(n) {
|
|
59562
59562
|
if (n === 0n) {
|
|
@@ -59803,15 +59803,15 @@ var require_verifier2 = __commonJS((exports) => {
|
|
|
59803
59803
|
});
|
|
59804
59804
|
}
|
|
59805
59805
|
}
|
|
59806
|
-
verifyPolicy(policy,
|
|
59806
|
+
verifyPolicy(policy, identity4) {
|
|
59807
59807
|
if (policy.subjectAlternativeName) {
|
|
59808
|
-
(0, policy_1.verifySubjectAlternativeName)(policy.subjectAlternativeName,
|
|
59808
|
+
(0, policy_1.verifySubjectAlternativeName)(policy.subjectAlternativeName, identity4.subjectAlternativeName);
|
|
59809
59809
|
}
|
|
59810
59810
|
if (policy.extensions) {
|
|
59811
|
-
(0, policy_1.verifyExtensions)(policy.extensions,
|
|
59811
|
+
(0, policy_1.verifyExtensions)(policy.extensions, identity4.extensions);
|
|
59812
59812
|
}
|
|
59813
59813
|
if (policy.oids) {
|
|
59814
|
-
(0, policy_1.verifyOIDs)(policy.oids,
|
|
59814
|
+
(0, policy_1.verifyOIDs)(policy.oids, identity4.oids);
|
|
59815
59815
|
}
|
|
59816
59816
|
}
|
|
59817
59817
|
}
|
|
@@ -60261,9 +60261,9 @@ class ParseStatus2 {
|
|
|
60261
60261
|
}
|
|
60262
60262
|
static async mergeObjectAsync(status, pairs) {
|
|
60263
60263
|
const syncPairs = [];
|
|
60264
|
-
for (const
|
|
60265
|
-
const key = await
|
|
60266
|
-
const value = await
|
|
60264
|
+
for (const pair2 of pairs) {
|
|
60265
|
+
const key = await pair2.key;
|
|
60266
|
+
const value = await pair2.value;
|
|
60267
60267
|
syncPairs.push({
|
|
60268
60268
|
key,
|
|
60269
60269
|
value
|
|
@@ -60273,8 +60273,8 @@ class ParseStatus2 {
|
|
|
60273
60273
|
}
|
|
60274
60274
|
static mergeObjectSync(status, pairs) {
|
|
60275
60275
|
const finalObject = {};
|
|
60276
|
-
for (const
|
|
60277
|
-
const { key, value } =
|
|
60276
|
+
for (const pair2 of pairs) {
|
|
60277
|
+
const { key, value } = pair2;
|
|
60278
60278
|
if (key.status === "aborted")
|
|
60279
60279
|
return INVALID2;
|
|
60280
60280
|
if (value.status === "aborted")
|
|
@@ -60283,7 +60283,7 @@ class ParseStatus2 {
|
|
|
60283
60283
|
status.dirty();
|
|
60284
60284
|
if (value.status === "dirty")
|
|
60285
60285
|
status.dirty();
|
|
60286
|
-
if (key.value !== "__proto__" && (typeof value.value !== "undefined" ||
|
|
60286
|
+
if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair2.alwaysSet)) {
|
|
60287
60287
|
finalObject[key.value] = value.value;
|
|
60288
60288
|
}
|
|
60289
60289
|
}
|
|
@@ -62266,13 +62266,13 @@ var init_types3 = __esm(() => {
|
|
|
62266
62266
|
if (ctx.common.async) {
|
|
62267
62267
|
return Promise.resolve().then(async () => {
|
|
62268
62268
|
const syncPairs = [];
|
|
62269
|
-
for (const
|
|
62270
|
-
const key = await
|
|
62271
|
-
const value = await
|
|
62269
|
+
for (const pair2 of pairs) {
|
|
62270
|
+
const key = await pair2.key;
|
|
62271
|
+
const value = await pair2.value;
|
|
62272
62272
|
syncPairs.push({
|
|
62273
62273
|
key,
|
|
62274
62274
|
value,
|
|
62275
|
-
alwaysSet:
|
|
62275
|
+
alwaysSet: pair2.alwaysSet
|
|
62276
62276
|
});
|
|
62277
62277
|
}
|
|
62278
62278
|
return syncPairs;
|
|
@@ -62799,9 +62799,9 @@ var init_types3 = __esm(() => {
|
|
|
62799
62799
|
if (ctx.common.async) {
|
|
62800
62800
|
const finalMap = new Map;
|
|
62801
62801
|
return Promise.resolve().then(async () => {
|
|
62802
|
-
for (const
|
|
62803
|
-
const key = await
|
|
62804
|
-
const value = await
|
|
62802
|
+
for (const pair2 of pairs) {
|
|
62803
|
+
const key = await pair2.key;
|
|
62804
|
+
const value = await pair2.value;
|
|
62805
62805
|
if (key.status === "aborted" || value.status === "aborted") {
|
|
62806
62806
|
return INVALID2;
|
|
62807
62807
|
}
|
|
@@ -62814,9 +62814,9 @@ var init_types3 = __esm(() => {
|
|
|
62814
62814
|
});
|
|
62815
62815
|
} else {
|
|
62816
62816
|
const finalMap = new Map;
|
|
62817
|
-
for (const
|
|
62818
|
-
const key =
|
|
62819
|
-
const value =
|
|
62817
|
+
for (const pair2 of pairs) {
|
|
62818
|
+
const key = pair2.key;
|
|
62819
|
+
const value = pair2.value;
|
|
62820
62820
|
if (key.status === "aborted" || value.status === "aborted") {
|
|
62821
62821
|
return INVALID2;
|
|
62822
62822
|
}
|
|
@@ -63771,7 +63771,7 @@ __export(exports_util, {
|
|
|
63771
63771
|
joinValues: () => joinValues,
|
|
63772
63772
|
issue: () => issue,
|
|
63773
63773
|
isPlainObject: () => isPlainObject2,
|
|
63774
|
-
isObject: () =>
|
|
63774
|
+
isObject: () => isObject5,
|
|
63775
63775
|
hexToUint8Array: () => hexToUint8Array,
|
|
63776
63776
|
getSizableOrigin: () => getSizableOrigin,
|
|
63777
63777
|
getParsedType: () => getParsedType3,
|
|
@@ -63938,11 +63938,11 @@ function esc(str) {
|
|
|
63938
63938
|
function slugify(input) {
|
|
63939
63939
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
63940
63940
|
}
|
|
63941
|
-
function
|
|
63941
|
+
function isObject5(data) {
|
|
63942
63942
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
63943
63943
|
}
|
|
63944
63944
|
function isPlainObject2(o) {
|
|
63945
|
-
if (
|
|
63945
|
+
if (isObject5(o) === false)
|
|
63946
63946
|
return false;
|
|
63947
63947
|
const ctor = o.constructor;
|
|
63948
63948
|
if (ctor === undefined)
|
|
@@ -63950,7 +63950,7 @@ function isPlainObject2(o) {
|
|
|
63950
63950
|
if (typeof ctor !== "function")
|
|
63951
63951
|
return true;
|
|
63952
63952
|
const prot = ctor.prototype;
|
|
63953
|
-
if (
|
|
63953
|
+
if (isObject5(prot) === false)
|
|
63954
63954
|
return false;
|
|
63955
63955
|
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
|
|
63956
63956
|
return false;
|
|
@@ -65831,13 +65831,13 @@ var init_schemas = __esm(() => {
|
|
|
65831
65831
|
}
|
|
65832
65832
|
return propValues;
|
|
65833
65833
|
});
|
|
65834
|
-
const
|
|
65834
|
+
const isObject6 = isObject5;
|
|
65835
65835
|
const catchall = def.catchall;
|
|
65836
65836
|
let value;
|
|
65837
65837
|
inst._zod.parse = (payload, ctx) => {
|
|
65838
65838
|
value ?? (value = _normalized.value);
|
|
65839
65839
|
const input = payload.value;
|
|
65840
|
-
if (!
|
|
65840
|
+
if (!isObject6(input)) {
|
|
65841
65841
|
payload.issues.push({
|
|
65842
65842
|
expected: "object",
|
|
65843
65843
|
code: "invalid_type",
|
|
@@ -65935,7 +65935,7 @@ var init_schemas = __esm(() => {
|
|
|
65935
65935
|
return (payload, ctx) => fn(shape, payload, ctx);
|
|
65936
65936
|
};
|
|
65937
65937
|
let fastpass;
|
|
65938
|
-
const
|
|
65938
|
+
const isObject6 = isObject5;
|
|
65939
65939
|
const jit = !globalConfig.jitless;
|
|
65940
65940
|
const allowsEval2 = allowsEval;
|
|
65941
65941
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -65944,7 +65944,7 @@ var init_schemas = __esm(() => {
|
|
|
65944
65944
|
inst._zod.parse = (payload, ctx) => {
|
|
65945
65945
|
value ?? (value = _normalized.value);
|
|
65946
65946
|
const input = payload.value;
|
|
65947
|
-
if (!
|
|
65947
|
+
if (!isObject6(input)) {
|
|
65948
65948
|
payload.issues.push({
|
|
65949
65949
|
expected: "object",
|
|
65950
65950
|
code: "invalid_type",
|
|
@@ -66048,7 +66048,7 @@ var init_schemas = __esm(() => {
|
|
|
66048
66048
|
});
|
|
66049
66049
|
inst._zod.parse = (payload, ctx) => {
|
|
66050
66050
|
const input = payload.value;
|
|
66051
|
-
if (!
|
|
66051
|
+
if (!isObject5(input)) {
|
|
66052
66052
|
payload.issues.push({
|
|
66053
66053
|
code: "invalid_type",
|
|
66054
66054
|
expected: "object",
|
|
@@ -71662,10 +71662,10 @@ class McpServer2 {
|
|
|
71662
71662
|
return registeredResourceTemplate;
|
|
71663
71663
|
}
|
|
71664
71664
|
}
|
|
71665
|
-
_createRegisteredResource(name2,
|
|
71665
|
+
_createRegisteredResource(name2, title2, uri, metadata, readCallback) {
|
|
71666
71666
|
const registeredResource = {
|
|
71667
71667
|
name: name2,
|
|
71668
|
-
title,
|
|
71668
|
+
title: title2,
|
|
71669
71669
|
metadata,
|
|
71670
71670
|
readCallback,
|
|
71671
71671
|
enabled: true,
|
|
@@ -71694,10 +71694,10 @@ class McpServer2 {
|
|
|
71694
71694
|
this._registeredResources[uri] = registeredResource;
|
|
71695
71695
|
return registeredResource;
|
|
71696
71696
|
}
|
|
71697
|
-
_createRegisteredResourceTemplate(name2,
|
|
71697
|
+
_createRegisteredResourceTemplate(name2, title2, template, metadata, readCallback) {
|
|
71698
71698
|
const registeredResourceTemplate = {
|
|
71699
71699
|
resourceTemplate: template,
|
|
71700
|
-
title,
|
|
71700
|
+
title: title2,
|
|
71701
71701
|
metadata,
|
|
71702
71702
|
readCallback,
|
|
71703
71703
|
enabled: true,
|
|
@@ -71731,9 +71731,9 @@ class McpServer2 {
|
|
|
71731
71731
|
}
|
|
71732
71732
|
return registeredResourceTemplate;
|
|
71733
71733
|
}
|
|
71734
|
-
_createRegisteredPrompt(name2,
|
|
71734
|
+
_createRegisteredPrompt(name2, title2, description, argsSchema, callback) {
|
|
71735
71735
|
const registeredPrompt = {
|
|
71736
|
-
title,
|
|
71736
|
+
title: title2,
|
|
71737
71737
|
description,
|
|
71738
71738
|
argsSchema: argsSchema === undefined ? undefined : objectFromShape(argsSchema),
|
|
71739
71739
|
callback,
|
|
@@ -71772,10 +71772,10 @@ class McpServer2 {
|
|
|
71772
71772
|
}
|
|
71773
71773
|
return registeredPrompt;
|
|
71774
71774
|
}
|
|
71775
|
-
_createRegisteredTool(name2,
|
|
71775
|
+
_createRegisteredTool(name2, title2, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
|
|
71776
71776
|
validateAndWarnToolName2(name2);
|
|
71777
71777
|
const registeredTool = {
|
|
71778
|
-
title,
|
|
71778
|
+
title: title2,
|
|
71779
71779
|
description,
|
|
71780
71780
|
inputSchema: getZodSchemaObject2(inputSchema),
|
|
71781
71781
|
outputSchema: getZodSchemaObject2(outputSchema),
|
|
@@ -71849,8 +71849,8 @@ class McpServer2 {
|
|
|
71849
71849
|
if (this._registeredTools[name2]) {
|
|
71850
71850
|
throw new Error(`Tool ${name2} is already registered`);
|
|
71851
71851
|
}
|
|
71852
|
-
const { title, description, inputSchema, outputSchema, annotations, _meta } = config2;
|
|
71853
|
-
return this._createRegisteredTool(name2,
|
|
71852
|
+
const { title: title2, description, inputSchema, outputSchema, annotations, _meta } = config2;
|
|
71853
|
+
return this._createRegisteredTool(name2, title2, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
|
|
71854
71854
|
}
|
|
71855
71855
|
prompt(name2, ...rest) {
|
|
71856
71856
|
if (this._registeredPrompts[name2]) {
|
|
@@ -71874,8 +71874,8 @@ class McpServer2 {
|
|
|
71874
71874
|
if (this._registeredPrompts[name2]) {
|
|
71875
71875
|
throw new Error(`Prompt ${name2} is already registered`);
|
|
71876
71876
|
}
|
|
71877
|
-
const { title, description, argsSchema } = config2;
|
|
71878
|
-
const registeredPrompt = this._createRegisteredPrompt(name2,
|
|
71877
|
+
const { title: title2, description, argsSchema } = config2;
|
|
71878
|
+
const registeredPrompt = this._createRegisteredPrompt(name2, title2, description, argsSchema, cb);
|
|
71879
71879
|
this.setPromptRequestHandlers();
|
|
71880
71880
|
this.sendPromptListChanged();
|
|
71881
71881
|
return registeredPrompt;
|
|
@@ -74269,6 +74269,17 @@ var HELP = {
|
|
|
74269
74269
|
],
|
|
74270
74270
|
env: ENV_API
|
|
74271
74271
|
},
|
|
74272
|
+
scan: {
|
|
74273
|
+
group: "Trade",
|
|
74274
|
+
summary: "The token card for one contract: market, all-time high, safety facts, tracked wallets",
|
|
74275
|
+
intro: "The token card for one pasted contract",
|
|
74276
|
+
description: "Prints the token card a Quant group gets when a contract is pasted, from the public card route (GET /api/v1/tokens/:chain/:mint/card): ticker, name, chain and launchpad, then price, market cap, the all-time high since launch, 5 minute change, liquidity, volume and buys and sells, the bundles, snipers, insiders, fresh-wallet and wallet-age facts, tracked wallets and signals in the last hour, the same-name token that leads, whether Candle's forensics covers it, and the Candle page. Facts only: there is no score and no verdict. A field with no source prints a dash, never a guess, and a Sources block names where each figure came from. The chain is read from the address (0x is Hood, base58 is Solana); --chain overrides it, and any other address is refused before a request. Free for every plan: needs no key and sends none. Off on a deployment until TOKEN_CARD_ENABLED is on; until then the command answers TOKEN_CARD_DISABLED.",
|
|
74277
|
+
usage: ["candle scan <contract address> [--chain solana|hood] [--json]"],
|
|
74278
|
+
rows: [],
|
|
74279
|
+
flags: [{ invocation: "--chain <chain>", description: "solana or hood; read from the address when omitted" }],
|
|
74280
|
+
examples: ["candle scan <mint>", "candle scan 0x<token> --json"],
|
|
74281
|
+
env: ["CANDLE_API_URL"]
|
|
74282
|
+
},
|
|
74272
74283
|
portfolio: {
|
|
74273
74284
|
group: "Account",
|
|
74274
74285
|
intro: "Holdings, prices and value of every wallet",
|
|
@@ -74377,13 +74388,19 @@ var HELP = {
|
|
|
74377
74388
|
},
|
|
74378
74389
|
token: {
|
|
74379
74390
|
group: "Account",
|
|
74380
|
-
summary: "Token events, cluster analysis, forensics and the vamp finder",
|
|
74381
|
-
|
|
74391
|
+
summary: "Token About pages, events, cluster analysis, forensics and the vamp finder",
|
|
74392
|
+
intro: "Token About pages, events, clusters, forensics, vamp finder",
|
|
74393
|
+
description: "about, claim, section, history and flag are the token's About page: three shared sections (thesis, tokenomics, lore) any eligible holder may edit, with full history. about and history are public and send no key. about --capabilities reads this account's own eligibility (token-pages:read). claim, section set, section restore and flag act as the key's account and need token-pages:write; neither scope is in any preset, so grant them with keys create --scopes token-pages:read,token-pages:write. Candle checks on every write that the account's Candle-known wallets hold at least 0.5% of the token's current supply (any positive balance to flag); the scope is never a holding. A creator proof is a message, never a transaction: claim --creator --challenge prints the exact bytes to sign (and --message-out writes them to a file), you sign them on your own machine, for example with candle sign message, and claim --creator --challenge-id <id> --signature <sig> sends the signature; a creator wallet already among the account's known wallets needs no signature. Section text comes from --file and is checked here first (valid UTF-8, at most 32 KiB, no control characters but newline and tab, thesis 280 characters, tokenomics and lore 5,000); the file is only read. Every write sends an Idempotency-Key and retries once on no answer with the same key. Exit codes: 0 done, 1 refused (the code is in --json), 2 usage or local input refused (nothing sent), 3 no answer so the outcome is unknown (rerun with the printed --idempotency-key), 4 revision conflict (your file is untouched; read history, merge, rerun with the latest --base-revision). Token page switches ship off; a switched-off server answers feature_off or not_found. events lists one token's signals, callouts and tracked-wallet trades, one event per line: time, kind, name or short address, action, size. Needs no key; a configured key is sent so wallet group names follow its tier. Off on a deployment until TOKEN_EVENTS_ENABLED is on; until then the command answers TOKEN_EVENTS_DISABLED. clusters reads third-party estimates of linked Solana wallets. Exchange, pool and service wallets may be grouped together; the figures do not establish common ownership or wrongdoing. Unavailable analysis is a normal result and exits 0. Needs no key and sends none. forensics and vamp-candidates send the profile's API key when it has one. Where the API offers x402 per-use payments, forensics past the free rate and the vamp finder without a key (or past a Free account's daily allowance) answer with a price: the CLI pays it in USDC from a Solana vault key, at a terminal, after a typed confirm (or --yes), within the limits candle spend sets, or prints the price and pays nothing. A paid call that Candle then fails to serve is credited, never refunded.",
|
|
74382
74394
|
usage: [
|
|
74383
74395
|
"candle token clusters <mint> [--chain solana|hood] [--json]",
|
|
74384
74396
|
"candle token events <mint> [--chain solana|hood] [--kind trade|signal|callout] [--since <iso-8601>] [--limit <n>] [--before <cursor>] [--json]",
|
|
74385
74397
|
"candle token forensics <mint> [--chain solana|hood] [--wallet <vault key>] [--yes] [--json]",
|
|
74386
|
-
"candle token vamp-candidates [--limit <n>] [--min-copies <n>] [--stage final_stretch|graduated|all] [--wallet <vault key>] [--yes] [--json]"
|
|
74398
|
+
"candle token vamp-candidates [--limit <n>] [--min-copies <n>] [--stage final_stretch|graduated|all] [--wallet <vault key>] [--yes] [--json]",
|
|
74399
|
+
"candle token about <address> [--chain solana|hood] [--capabilities] [--json]",
|
|
74400
|
+
"candle token claim <address> [--creator [--challenge [--message-out <path>] | --challenge-id <id> --signature <sig>]] [--json]",
|
|
74401
|
+
"candle token section set|restore <address> <thesis|tokenomics|lore> --file <path>|--version-id <id> --base-revision <n> [--json]",
|
|
74402
|
+
"candle token history <address> <thesis|tokenomics|lore> [--limit <n>] [--cursor <c>] [--json]",
|
|
74403
|
+
"candle token flag <address> --content <id> --reason <reason> [--detail <text>] [--json]"
|
|
74387
74404
|
],
|
|
74388
74405
|
rows: [
|
|
74389
74406
|
{ invocation: "clusters <mint>", description: "Read cluster analysis, or its unavailable reason" },
|
|
@@ -74395,7 +74412,21 @@ var HELP = {
|
|
|
74395
74412
|
{
|
|
74396
74413
|
invocation: "vamp-candidates",
|
|
74397
74414
|
description: "Tokens near graduation whose ticker others copy; may cost a per-use payment"
|
|
74398
|
-
}
|
|
74415
|
+
},
|
|
74416
|
+
{
|
|
74417
|
+
invocation: "about <address>",
|
|
74418
|
+
description: "The token's About page: claim, sections and measured supply; --capabilities adds your eligibility"
|
|
74419
|
+
},
|
|
74420
|
+
{
|
|
74421
|
+
invocation: "claim <address>",
|
|
74422
|
+
description: "Join the page's editors as a holder, or as its creator with --creator (a signed message)"
|
|
74423
|
+
},
|
|
74424
|
+
{
|
|
74425
|
+
invocation: "section set|restore <address> <section>",
|
|
74426
|
+
description: "Write a section from --file, or restore an earlier --version-id, at --base-revision"
|
|
74427
|
+
},
|
|
74428
|
+
{ invocation: "history <address> <section>", description: "A section's versions, newest first" },
|
|
74429
|
+
{ invocation: "flag <address>", description: "Report one exact version to Candle's review queue" }
|
|
74399
74430
|
],
|
|
74400
74431
|
flags: [
|
|
74401
74432
|
{ invocation: "--chain <chain>", description: "solana (default) or hood; Hood cluster analysis is unavailable" },
|
|
@@ -74403,7 +74434,10 @@ var HELP = {
|
|
|
74403
74434
|
invocation: "--kind <kind>",
|
|
74404
74435
|
description: "events: trade, signal or callout; filters the page locally (the route has no kind filter)"
|
|
74405
74436
|
},
|
|
74406
|
-
{
|
|
74437
|
+
{
|
|
74438
|
+
invocation: "--limit <n>",
|
|
74439
|
+
description: "events, history or vamp-candidates: how many to read (events 1 to 1000, history 1 to 50)"
|
|
74440
|
+
},
|
|
74407
74441
|
{ invocation: "--min-copies <n>", description: "vamp-candidates: copies in the last 24h, at least" },
|
|
74408
74442
|
{ invocation: "--since <iso-8601>", description: "events: list only events at or after this time" },
|
|
74409
74443
|
{ invocation: "--before <cursor>", description: "events: continue the page a previous answer's cursor names" },
|
|
@@ -74412,9 +74446,24 @@ var HELP = {
|
|
|
74412
74446
|
invocation: "--wallet <vault key>",
|
|
74413
74447
|
description: "The Solana vault key that pays, by label or address (needed when the vault has several)"
|
|
74414
74448
|
},
|
|
74415
|
-
{ invocation: "--yes", description: "Pay without the typed confirm (the vault unlock still prompts)" }
|
|
74449
|
+
{ invocation: "--yes", description: "Pay without the typed confirm (the vault unlock still prompts)" },
|
|
74450
|
+
{ invocation: "--file <path>", description: "section set: the section text, UTF-8; an empty file needs --clear" },
|
|
74451
|
+
{
|
|
74452
|
+
invocation: "--base-revision <n>",
|
|
74453
|
+
description: "section set|restore: the revision you started from (0 first)"
|
|
74454
|
+
},
|
|
74455
|
+
{ invocation: "--version-id <id>", description: "section restore: the earlier version to copy into a new one" },
|
|
74456
|
+
{ invocation: "--content <id>", description: "flag: the exact version id being reported" },
|
|
74457
|
+
{
|
|
74458
|
+
invocation: "--reason <reason>",
|
|
74459
|
+
description: "flag: spam, scam, abuse, misleading, unsafe_link or other; --detail adds up to 500 characters"
|
|
74460
|
+
},
|
|
74461
|
+
{ invocation: "--idempotency-key <key>", description: "Writes: reuse a key to retry without writing twice" }
|
|
74416
74462
|
],
|
|
74417
74463
|
examples: [
|
|
74464
|
+
"candle token about <mint> --capabilities",
|
|
74465
|
+
"candle token section set <mint> thesis --file ./thesis.txt --base-revision 3",
|
|
74466
|
+
"candle token claim <mint> --creator --challenge --message-out ./proof.txt",
|
|
74418
74467
|
"candle token clusters <mint>",
|
|
74419
74468
|
"candle token clusters <mint> --json",
|
|
74420
74469
|
"candle token events <mint> --kind signal --limit 50",
|
|
@@ -97343,6 +97392,190 @@ async function quantTerms(args, ctx) {
|
|
|
97343
97392
|
return 1;
|
|
97344
97393
|
}
|
|
97345
97394
|
|
|
97395
|
+
// src/commands/scan.ts
|
|
97396
|
+
init_args();
|
|
97397
|
+
init_client();
|
|
97398
|
+
init_human_output();
|
|
97399
|
+
init_presentation();
|
|
97400
|
+
init_render();
|
|
97401
|
+
var USAGE10 = "Usage: candle scan <contract address> [--chain solana|hood] [--json]";
|
|
97402
|
+
var OTHER_CHAIN = "Candle trades Solana and Hood today; that is not a Solana or Hood contract address.";
|
|
97403
|
+
var HOOD_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
|
|
97404
|
+
var SOLANA_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
97405
|
+
function chainOfAddress(address) {
|
|
97406
|
+
if (HOOD_ADDRESS.test(address))
|
|
97407
|
+
return "hood";
|
|
97408
|
+
if (SOLANA_ADDRESS.test(address))
|
|
97409
|
+
return "solana";
|
|
97410
|
+
return null;
|
|
97411
|
+
}
|
|
97412
|
+
var isObject2 = (value) => value !== null && typeof value === "object";
|
|
97413
|
+
function readCard(body) {
|
|
97414
|
+
if (!isObject2(body) || body.success !== true || !isObject2(body.card))
|
|
97415
|
+
return null;
|
|
97416
|
+
const card2 = body.card;
|
|
97417
|
+
const blocks = ["identity", "market", "forensics", "events", "hacc", "sameName", "sources", "links"];
|
|
97418
|
+
if (typeof card2.mint !== "string" || card2.chain !== "solana" && card2.chain !== "hood")
|
|
97419
|
+
return null;
|
|
97420
|
+
if (!blocks.every((block2) => isObject2(card2[block2])))
|
|
97421
|
+
return null;
|
|
97422
|
+
return card2;
|
|
97423
|
+
}
|
|
97424
|
+
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
97425
|
+
function usd(value) {
|
|
97426
|
+
const n = num(value);
|
|
97427
|
+
return n === null ? null : formatUsd(n, { compact: true });
|
|
97428
|
+
}
|
|
97429
|
+
function percentChange(value) {
|
|
97430
|
+
const n = num(value);
|
|
97431
|
+
if (n === null)
|
|
97432
|
+
return null;
|
|
97433
|
+
const rounded = Number(n.toFixed(1));
|
|
97434
|
+
return `${rounded > 0 ? "+" : ""}${rounded}%`;
|
|
97435
|
+
}
|
|
97436
|
+
function title(card2) {
|
|
97437
|
+
const { symbol, name: name2, launchpad, isCandle } = card2.identity;
|
|
97438
|
+
const parts = [
|
|
97439
|
+
symbol || shortAddress2(card2.mint),
|
|
97440
|
+
name2 && name2 !== symbol ? name2 : null,
|
|
97441
|
+
card2.chain === "hood" ? "Hood" : "Solana",
|
|
97442
|
+
launchpad,
|
|
97443
|
+
isCandle === true ? "launched on Candle" : null
|
|
97444
|
+
];
|
|
97445
|
+
return parts.filter((part) => typeof part === "string" && part.length > 0).join(" · ");
|
|
97446
|
+
}
|
|
97447
|
+
function fact(card2, key) {
|
|
97448
|
+
if (card2.hacc.state !== "ok")
|
|
97449
|
+
return null;
|
|
97450
|
+
const field = card2.hacc.facts.fields?.[key];
|
|
97451
|
+
return field?.state === "ok" && field.display ? field.display : null;
|
|
97452
|
+
}
|
|
97453
|
+
function pair(a, b, missing) {
|
|
97454
|
+
return a === null && b === null ? null : `${a ?? missing} / ${b ?? missing}`;
|
|
97455
|
+
}
|
|
97456
|
+
function eventsLine(card2, missing) {
|
|
97457
|
+
const events = card2.events;
|
|
97458
|
+
if (events.state !== "ok")
|
|
97459
|
+
return `${missing} (${events.state})`;
|
|
97460
|
+
const parts = [
|
|
97461
|
+
`${events.trackedBuys} bought, ${events.trackedSells} sold`,
|
|
97462
|
+
`${events.signals} signal${events.signals === 1 ? "" : "s"}`,
|
|
97463
|
+
`${events.callouts} callout${events.callouts === 1 ? "" : "s"}`
|
|
97464
|
+
];
|
|
97465
|
+
const names = Array.isArray(events.names) && events.names.length > 0 ? ` (${events.names.join(", ")})` : "";
|
|
97466
|
+
return `${parts.join(" · ")}${names}`;
|
|
97467
|
+
}
|
|
97468
|
+
function sameNameLine(card2, missing) {
|
|
97469
|
+
const same = card2.sameName;
|
|
97470
|
+
if (same.state === "off" || same.state === "unavailable")
|
|
97471
|
+
return `${missing} (${same.state})`;
|
|
97472
|
+
if (same.state === "none")
|
|
97473
|
+
return "none found";
|
|
97474
|
+
const display = same.display && same.display !== "–" ? same.display : missing;
|
|
97475
|
+
return same.trailing === true ? `${display} (this token trails)` : display;
|
|
97476
|
+
}
|
|
97477
|
+
function forensicsLine(card2, missing) {
|
|
97478
|
+
switch (card2.forensics.state) {
|
|
97479
|
+
case "ok":
|
|
97480
|
+
return `read; the full report is candle token forensics ${card2.mint}${card2.chain === "hood" ? " --chain hood" : ""}`;
|
|
97481
|
+
case "not_covered":
|
|
97482
|
+
return "not covered for this token (bundles, snipers and insiders are unknown, not clean)";
|
|
97483
|
+
default:
|
|
97484
|
+
return `${missing} (unavailable)`;
|
|
97485
|
+
}
|
|
97486
|
+
}
|
|
97487
|
+
function athLine(card2) {
|
|
97488
|
+
const ath = card2.ath;
|
|
97489
|
+
if (!ath || num(ath.marketCapUsd) === null)
|
|
97490
|
+
return null;
|
|
97491
|
+
return `${formatUsd(ath.marketCapUsd, { compact: true })} on ${formatTime(ath.at, { date: true, now: card2.generatedAt })} (daily high, FDV)`;
|
|
97492
|
+
}
|
|
97493
|
+
async function scan(args, ctx) {
|
|
97494
|
+
const parsed = parseArgs(args, { valueFlags: ["--chain"] });
|
|
97495
|
+
if ("error" in parsed) {
|
|
97496
|
+
writeUsageFailure(ctx.deps, parsed.error, ctx.json);
|
|
97497
|
+
return 2;
|
|
97498
|
+
}
|
|
97499
|
+
const named = parsed.values["--chain"];
|
|
97500
|
+
if (parsed.positionals.length !== 1 || named !== undefined && named !== "solana" && named !== "hood") {
|
|
97501
|
+
writeUsageFailure(ctx.deps, USAGE10, ctx.json);
|
|
97502
|
+
return 2;
|
|
97503
|
+
}
|
|
97504
|
+
const address = parsed.positionals[0].trim();
|
|
97505
|
+
const found = chainOfAddress(address);
|
|
97506
|
+
if (found === null) {
|
|
97507
|
+
writeUsageFailure(ctx.deps, `${OTHER_CHAIN}
|
|
97508
|
+
${USAGE10}`, ctx.json);
|
|
97509
|
+
return 2;
|
|
97510
|
+
}
|
|
97511
|
+
if (named !== undefined && named !== found) {
|
|
97512
|
+
const label = (c) => c === "hood" ? "Hood" : "Solana";
|
|
97513
|
+
writeUsageFailure(ctx.deps, `That address is a ${label(found)} contract, not a ${label(named)} one. Candle trades Solana and Hood today.
|
|
97514
|
+
${USAGE10}`, ctx.json);
|
|
97515
|
+
return 2;
|
|
97516
|
+
}
|
|
97517
|
+
const chain2 = found;
|
|
97518
|
+
const result = await apiRequest(`/api/v1/tokens/${chain2}/${encodeURIComponent(address)}/card`, {
|
|
97519
|
+
apiUrl: ctx.apiUrl,
|
|
97520
|
+
auth: "none",
|
|
97521
|
+
credentials: {},
|
|
97522
|
+
env: ctx.deps.env,
|
|
97523
|
+
fetch: ctx.deps.fetch
|
|
97524
|
+
});
|
|
97525
|
+
if (!result.ok) {
|
|
97526
|
+
writeFailure(ctx.deps, result, { apiUrl: ctx.apiUrl, authType: "none" }, ctx.json);
|
|
97527
|
+
return 1;
|
|
97528
|
+
}
|
|
97529
|
+
const card2 = readCard(result.body);
|
|
97530
|
+
if (!card2) {
|
|
97531
|
+
writeFailure(ctx.deps, {
|
|
97532
|
+
ok: false,
|
|
97533
|
+
status: result.status,
|
|
97534
|
+
code: "BAD_RESPONSE",
|
|
97535
|
+
message: "The card answer was not readable.",
|
|
97536
|
+
raw: result.body
|
|
97537
|
+
}, { apiUrl: ctx.apiUrl, authType: "none" }, ctx.json);
|
|
97538
|
+
return 1;
|
|
97539
|
+
}
|
|
97540
|
+
if (ctx.json) {
|
|
97541
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data: { card: card2 } })}
|
|
97542
|
+
`);
|
|
97543
|
+
return 0;
|
|
97544
|
+
}
|
|
97545
|
+
const missing = symbols(terminalStyle(ctx.deps, "stdout")).missing;
|
|
97546
|
+
const m = card2.market;
|
|
97547
|
+
const buys = num(m.buys5m);
|
|
97548
|
+
const sells = num(m.sells5m);
|
|
97549
|
+
const rows = [
|
|
97550
|
+
["Contract", card2.mint],
|
|
97551
|
+
["Price", num(m.priceUsd) === null ? null : formatPrice(m.priceUsd)],
|
|
97552
|
+
["Market cap", usd(m.marketCapUsd)],
|
|
97553
|
+
["All-time high", athLine(card2)],
|
|
97554
|
+
["5m change", percentChange(m.change5mPct)],
|
|
97555
|
+
["Liquidity", usd(m.liquidityUsd)],
|
|
97556
|
+
["Volume 5m", usd(m.volume5mUsd)],
|
|
97557
|
+
["Buys / sells 5m", pair(buys === null ? null : String(buys), sells === null ? null : String(sells), missing)],
|
|
97558
|
+
["Bundles", fact(card2, "bundles")],
|
|
97559
|
+
["Snipers", fact(card2, "snipers")],
|
|
97560
|
+
["Insiders", fact(card2, "insiders")],
|
|
97561
|
+
["Fresh 1d / 7d", pair(fact(card2, "fresh_1d"), fact(card2, "fresh_7d"), missing)],
|
|
97562
|
+
["Mean wallet age", fact(card2, "avg_wallet_age")],
|
|
97563
|
+
["Tracked, 1h", eventsLine(card2, missing)],
|
|
97564
|
+
["Same name", sameNameLine(card2, missing)],
|
|
97565
|
+
["Forensics", forensicsLine(card2, missing)],
|
|
97566
|
+
["Market as of", num(m.updatedAt) === null ? null : formatTime(m.updatedAt, { now: card2.generatedAt })],
|
|
97567
|
+
["Candle page", card2.links.token]
|
|
97568
|
+
];
|
|
97569
|
+
writeFields(ctx.deps, "stdout", rows, title(card2));
|
|
97570
|
+
const sources2 = Object.entries(card2.sources).filter(([, text3]) => typeof text3 === "string");
|
|
97571
|
+
if (sources2.length > 0) {
|
|
97572
|
+
ctx.deps.stdout.write(`
|
|
97573
|
+
`);
|
|
97574
|
+
writeFields(ctx.deps, "stdout", sources2, "Sources");
|
|
97575
|
+
}
|
|
97576
|
+
return 0;
|
|
97577
|
+
}
|
|
97578
|
+
|
|
97346
97579
|
// src/commands/setup.ts
|
|
97347
97580
|
init_human_output();
|
|
97348
97581
|
init_presentation();
|
|
@@ -97354,8 +97587,8 @@ init_profiles();
|
|
|
97354
97587
|
init_render();
|
|
97355
97588
|
var SKILLS_CLAUDE_COMMAND = "/plugin marketplace add candledottv/agentic";
|
|
97356
97589
|
var CODING_AGENTS_DOCS = "https://docs.candle.tv/developers/coding-agents";
|
|
97357
|
-
function section(deps,
|
|
97358
|
-
writeHeading(deps, "stdout",
|
|
97590
|
+
function section(deps, title2) {
|
|
97591
|
+
writeHeading(deps, "stdout", title2);
|
|
97359
97592
|
}
|
|
97360
97593
|
async function setup(args, ctx) {
|
|
97361
97594
|
const { deps, apiUrl, json } = ctx;
|
|
@@ -99097,7 +99330,7 @@ async function teeRename(args, ctx) {
|
|
|
99097
99330
|
});
|
|
99098
99331
|
const preview = await call2({
|
|
99099
99332
|
dryRun: true,
|
|
99100
|
-
renames: pairs.map((
|
|
99333
|
+
renames: pairs.map((pair2) => ({ wallet: pair2.from, label: pair2.to }))
|
|
99101
99334
|
});
|
|
99102
99335
|
if (!preview.ok)
|
|
99103
99336
|
return serverFailure(ctx, preview, pairs, batch);
|
|
@@ -99388,7 +99621,7 @@ init_deps();
|
|
|
99388
99621
|
init_human_output();
|
|
99389
99622
|
init_render();
|
|
99390
99623
|
var KINDS = ["trade", "signal", "callout"];
|
|
99391
|
-
var
|
|
99624
|
+
var USAGE11 = "Usage: candle token events <mint> [--chain solana|hood] [--kind trade|signal|callout] [--since <iso-8601>] [--limit <n>] [--before <cursor>] [--json]";
|
|
99392
99625
|
function readEventsBody(body) {
|
|
99393
99626
|
if (body === null || typeof body !== "object" || !Array.isArray(body.events))
|
|
99394
99627
|
return null;
|
|
@@ -99446,11 +99679,11 @@ async function tokenEvents(args, ctx) {
|
|
|
99446
99679
|
const since = parsed.values["--since"];
|
|
99447
99680
|
const before = parsed.values["--before"];
|
|
99448
99681
|
if (parsed.positionals.length !== 1 || chain2 !== "solana" && chain2 !== "hood") {
|
|
99449
|
-
writeUsageFailure(ctx.deps,
|
|
99682
|
+
writeUsageFailure(ctx.deps, USAGE11, ctx.json);
|
|
99450
99683
|
return 2;
|
|
99451
99684
|
}
|
|
99452
99685
|
if (kind !== undefined && !KINDS.includes(kind)) {
|
|
99453
|
-
writeUsageFailure(ctx.deps,
|
|
99686
|
+
writeUsageFailure(ctx.deps, USAGE11, ctx.json);
|
|
99454
99687
|
return 2;
|
|
99455
99688
|
}
|
|
99456
99689
|
if (limit !== undefined && !/^[1-9][0-9]{0,3}$/.test(limit) || Number(limit) > 1000) {
|
|
@@ -99523,13 +99756,657 @@ async function tokenEvents(args, ctx) {
|
|
|
99523
99756
|
return 0;
|
|
99524
99757
|
}
|
|
99525
99758
|
|
|
99759
|
+
// src/commands/token-pages.ts
|
|
99760
|
+
init_esm();
|
|
99761
|
+
init_args();
|
|
99762
|
+
init_client();
|
|
99763
|
+
init_deps();
|
|
99764
|
+
init_human_output();
|
|
99765
|
+
init_presentation();
|
|
99766
|
+
init_render();
|
|
99767
|
+
import { createHash as createHash5, randomUUID as randomUUID10 } from "node:crypto";
|
|
99768
|
+
var EXIT_OUTCOME_UNKNOWN = 3;
|
|
99769
|
+
var EXIT_REVISION_CONFLICT = 4;
|
|
99770
|
+
var TOKEN_PAGE_LIMITS = { thesis: 280, tokenomics: 5000, lore: 5000, update: 1000, comment: 500 };
|
|
99771
|
+
var TOKEN_PAGE_BODY_CAP_BYTES = 32 * 1024;
|
|
99772
|
+
var TOKEN_PAGE_SECTIONS = ["thesis", "tokenomics", "lore"];
|
|
99773
|
+
var FLAG_REASONS = ["spam", "scam", "abuse", "misleading", "unsafe_link", "other"];
|
|
99774
|
+
var FLAG_DETAIL_LIMIT = 500;
|
|
99775
|
+
var USAGE12 = {
|
|
99776
|
+
about: "Usage: candle token about <address> [--chain solana|hood] [--capabilities]",
|
|
99777
|
+
claim: "Usage: candle token claim <address> [--chain solana|hood] [--creator [--challenge [--message-out <path>] | --challenge-id <id> --signature <sig>]] [--idempotency-key <key>]",
|
|
99778
|
+
section: `Usage: candle token section set <address> <thesis|tokenomics|lore> --file <path> --base-revision <n> [--clear] [--chain solana|hood] [--idempotency-key <key>]
|
|
99779
|
+
candle token section restore <address> <thesis|tokenomics|lore> --version-id <id> --base-revision <n> [--chain solana|hood] [--idempotency-key <key>]`,
|
|
99780
|
+
history: "Usage: candle token history <address> <thesis|tokenomics|lore> [--chain solana|hood] [--limit <n>] [--cursor <c>]",
|
|
99781
|
+
flag: "Usage: candle token flag <address> --content <id> --reason <spam|scam|abuse|misleading|unsafe_link|other> [--detail <text>] [--chain solana|hood] [--idempotency-key <key>]"
|
|
99782
|
+
};
|
|
99783
|
+
var REFUSALS = {
|
|
99784
|
+
feature_off: "This token page action is switched off on this Candle server. Nothing was written.",
|
|
99785
|
+
not_found: "No such token page, item or version here, or token pages are switched off on this Candle server.",
|
|
99786
|
+
insufficient_holding: "This account's Candle-known wallets hold less than 0.5% of the token's current supply (any positive balance is enough to flag). Nothing was written.",
|
|
99787
|
+
eligibility_unavailable: "Candle could not read complete, fresh wallet and supply evidence for this account just now. Nothing was written; try again shortly.",
|
|
99788
|
+
restricted: "This account is restricted from writing on this token page.",
|
|
99789
|
+
revision_conflict: "The section changed since the base revision you gave.",
|
|
99790
|
+
idempotency_conflict: "This idempotency key was already used for a different request. Nothing was written.",
|
|
99791
|
+
idempotency_key_required: "The request carried no usable Idempotency-Key.",
|
|
99792
|
+
challenge_used: "That creator challenge was already used, or the creator evidence changed. Request a new one.",
|
|
99793
|
+
invalid_signature: "The signature does not verify for that challenge, or the challenge expired (5 minutes). Request a new one.",
|
|
99794
|
+
creator_unknown: "Candle has no verified creator for this token, so there is nothing to prove.",
|
|
99795
|
+
creator_proof_unsupported: "The verified creator cannot sign a message (a contract or multisig), so creator proof is not available.",
|
|
99796
|
+
invalid_text: "Candle refused the text: too long, empty, or holding invalid Unicode or control characters.",
|
|
99797
|
+
invalid_body: "Candle refused the request body.",
|
|
99798
|
+
invalid_token: "That is not a valid address on that chain.",
|
|
99799
|
+
invalid_section: "Sections are thesis, tokenomics and lore.",
|
|
99800
|
+
invalid_query: "Candle refused the query (limit is 1-50; the cursor must come from this list).",
|
|
99801
|
+
no_identity_wallet: "This key's account has no wallet Candle can use as its identity.",
|
|
99802
|
+
rate_limited: "Too many token page requests; wait a minute and try again.",
|
|
99803
|
+
unavailable: "Token pages are unavailable on this Candle server just now.",
|
|
99804
|
+
measurements_unavailable: "Measurements are unavailable just now; try again shortly."
|
|
99805
|
+
};
|
|
99806
|
+
var SECTION_SUGGESTION = "Your file was not changed. Read the newer text with candle token history, merge, then rerun with --base-revision set to the latest revision.";
|
|
99807
|
+
function usageFor(ctx, line) {
|
|
99808
|
+
return (message) => {
|
|
99809
|
+
writeUsageFailure(ctx.deps, message ? `${message}
|
|
99810
|
+
${line}` : line, ctx.json);
|
|
99811
|
+
return 2;
|
|
99812
|
+
};
|
|
99813
|
+
}
|
|
99814
|
+
function chainOf(values) {
|
|
99815
|
+
const chain2 = values["--chain"] ?? "solana";
|
|
99816
|
+
return chain2 === "solana" || chain2 === "hood" ? chain2 : undefined;
|
|
99817
|
+
}
|
|
99818
|
+
function pagePath(chain2, address, rest = "") {
|
|
99819
|
+
return `/api/v1/token-pages/${chain2}/${encodeURIComponent(address)}${rest}`;
|
|
99820
|
+
}
|
|
99821
|
+
function refusalOf2(result) {
|
|
99822
|
+
if (result.ok)
|
|
99823
|
+
return null;
|
|
99824
|
+
const raw = result.raw;
|
|
99825
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
99826
|
+
return null;
|
|
99827
|
+
const { success: success2, error, ...extra } = raw;
|
|
99828
|
+
if (success2 !== false || typeof error !== "string")
|
|
99829
|
+
return null;
|
|
99830
|
+
return { status: result.status, code: error, extra };
|
|
99831
|
+
}
|
|
99832
|
+
function writeRefusal(ctx, refusal3, suggestion) {
|
|
99833
|
+
const message = REFUSALS[refusal3.code] ?? `Candle refused the request (${refusal3.code}).`;
|
|
99834
|
+
if (ctx.json) {
|
|
99835
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: false, code: refusal3.code, status: refusal3.status, message, ...refusal3.extra, ...suggestion ? { suggestion } : {} })}
|
|
99836
|
+
`);
|
|
99837
|
+
return;
|
|
99838
|
+
}
|
|
99839
|
+
ctx.deps.stderr.write(`${suggestion ? `${message} ${suggestion}` : message}
|
|
99840
|
+
`);
|
|
99841
|
+
}
|
|
99842
|
+
function writeApiFailure(ctx, result, authType) {
|
|
99843
|
+
const refusal3 = refusalOf2(result);
|
|
99844
|
+
if (refusal3) {
|
|
99845
|
+
writeRefusal(ctx, refusal3);
|
|
99846
|
+
return 1;
|
|
99847
|
+
}
|
|
99848
|
+
writeFailure(ctx.deps, result, { apiUrl: ctx.apiUrl, authType }, ctx.json);
|
|
99849
|
+
return 1;
|
|
99850
|
+
}
|
|
99851
|
+
function dataOf(result) {
|
|
99852
|
+
const body = result.ok ? result.body : null;
|
|
99853
|
+
return body && typeof body === "object" && "data" in body ? body.data : body;
|
|
99854
|
+
}
|
|
99855
|
+
async function read2(ctx, path, apiKey) {
|
|
99856
|
+
return apiRequest(path, {
|
|
99857
|
+
apiUrl: ctx.apiUrl,
|
|
99858
|
+
...apiKey ? { auth: "key", credentials: { apiKey } } : { auth: "none", credentials: {} },
|
|
99859
|
+
fetch: ctx.deps.fetch,
|
|
99860
|
+
env: ctx.deps.env
|
|
99861
|
+
});
|
|
99862
|
+
}
|
|
99863
|
+
async function requireKey(ctx) {
|
|
99864
|
+
const key = await resolveApiKey(ctx.deps, ctx.profile);
|
|
99865
|
+
if (key)
|
|
99866
|
+
return key;
|
|
99867
|
+
writeLocalFailure(ctx.deps, {
|
|
99868
|
+
code: "API_KEY_REQUIRED",
|
|
99869
|
+
message: "This needs an API key with the token-pages scopes.",
|
|
99870
|
+
suggestion: "Create one with: candle keys create --scopes token-pages:read,token-pages:write"
|
|
99871
|
+
}, ctx.json);
|
|
99872
|
+
return;
|
|
99873
|
+
}
|
|
99874
|
+
async function write(ctx, input) {
|
|
99875
|
+
const send = async () => {
|
|
99876
|
+
try {
|
|
99877
|
+
return await apiRequest(input.path, {
|
|
99878
|
+
apiUrl: ctx.apiUrl,
|
|
99879
|
+
method: input.method,
|
|
99880
|
+
body: input.body,
|
|
99881
|
+
auth: "key",
|
|
99882
|
+
credentials: { apiKey: input.apiKey },
|
|
99883
|
+
headers: { "Idempotency-Key": input.idempotencyKey },
|
|
99884
|
+
fetch: ctx.deps.fetch,
|
|
99885
|
+
env: ctx.deps.env
|
|
99886
|
+
});
|
|
99887
|
+
} catch {
|
|
99888
|
+
return { ok: false, status: 0, message: "The response was cut off before it finished.", raw: undefined };
|
|
99889
|
+
}
|
|
99890
|
+
};
|
|
99891
|
+
const first = await send();
|
|
99892
|
+
if (first.ok || first.status !== 0 || first.code)
|
|
99893
|
+
return first;
|
|
99894
|
+
const second = await send();
|
|
99895
|
+
if (second.ok || second.status !== 0 || second.code)
|
|
99896
|
+
return second;
|
|
99897
|
+
return "unknown";
|
|
99898
|
+
}
|
|
99899
|
+
function writeUnknown(ctx, idempotencyKey) {
|
|
99900
|
+
writeLocalFailure(ctx.deps, {
|
|
99901
|
+
code: "OUTCOME_UNKNOWN",
|
|
99902
|
+
message: "Candle did not answer, twice, so whether this write landed is not known.",
|
|
99903
|
+
suggestion: `Rerun the same command with --idempotency-key ${idempotencyKey}: Candle returns the first result instead of writing twice.`,
|
|
99904
|
+
details: { idempotencyKey }
|
|
99905
|
+
}, ctx.json);
|
|
99906
|
+
return EXIT_OUTCOME_UNKNOWN;
|
|
99907
|
+
}
|
|
99908
|
+
function idempotencyKeyOf(values, usage11) {
|
|
99909
|
+
const given = values["--idempotency-key"];
|
|
99910
|
+
if (given === undefined)
|
|
99911
|
+
return randomUUID10();
|
|
99912
|
+
if (given.length < 1 || given.length > 128)
|
|
99913
|
+
return usage11("--idempotency-key must be 1 to 128 characters.");
|
|
99914
|
+
return given;
|
|
99915
|
+
}
|
|
99916
|
+
function sectionOf(value) {
|
|
99917
|
+
return TOKEN_PAGE_SECTIONS.includes(value ?? "") ? value : undefined;
|
|
99918
|
+
}
|
|
99919
|
+
var REVISION = /^(0|[1-9][0-9]{0,18})$/;
|
|
99920
|
+
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
99921
|
+
function bpsPercent(bps2) {
|
|
99922
|
+
if (bps2 === null || bps2 === undefined || !/^\d+$/.test(bps2))
|
|
99923
|
+
return;
|
|
99924
|
+
const n = BigInt(bps2);
|
|
99925
|
+
return `${n / 100n}.${(n % 100n).toString().padStart(2, "0")}%`;
|
|
99926
|
+
}
|
|
99927
|
+
function identity3(who2) {
|
|
99928
|
+
if (!who2)
|
|
99929
|
+
return;
|
|
99930
|
+
if (who2.kind === "candle")
|
|
99931
|
+
return "Candle";
|
|
99932
|
+
if (who2.kind === "agent")
|
|
99933
|
+
return terminalText(who2.name ?? "Agent");
|
|
99934
|
+
return terminalText(who2.name ?? who2.account);
|
|
99935
|
+
}
|
|
99936
|
+
function sectionText(bytes, section2) {
|
|
99937
|
+
if (bytes.length > TOKEN_PAGE_BODY_CAP_BYTES)
|
|
99938
|
+
return { error: `The file is over ${TOKEN_PAGE_BODY_CAP_BYTES} bytes.` };
|
|
99939
|
+
let decoded;
|
|
99940
|
+
try {
|
|
99941
|
+
decoded = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
99942
|
+
} catch {
|
|
99943
|
+
return { error: "The file is not valid UTF-8." };
|
|
99944
|
+
}
|
|
99945
|
+
const text3 = decoded.replace(/\r\n?/g, `
|
|
99946
|
+
`);
|
|
99947
|
+
for (const char of text3) {
|
|
99948
|
+
const c = char.codePointAt(0) ?? 0;
|
|
99949
|
+
if (c < 32 && c !== 10 && c !== 9 || c === 127 || c >= 128 && c < 160 || c >= 55296 && c <= 57343)
|
|
99950
|
+
return { error: `The file holds a control character (U+${c.toString(16).toUpperCase().padStart(4, "0")}).` };
|
|
99951
|
+
}
|
|
99952
|
+
const length = [...text3].length;
|
|
99953
|
+
const limit = TOKEN_PAGE_LIMITS[section2];
|
|
99954
|
+
if (length > limit)
|
|
99955
|
+
return { error: `${section2} is at most ${limit} characters; the file has ${length}.` };
|
|
99956
|
+
return { text: text3 };
|
|
99957
|
+
}
|
|
99958
|
+
function signatureFor(chain2, given) {
|
|
99959
|
+
const value = given.trim();
|
|
99960
|
+
if (chain2 === "hood")
|
|
99961
|
+
return /^0x[0-9a-fA-F]{130}$/.test(value) ? value : undefined;
|
|
99962
|
+
try {
|
|
99963
|
+
const bytes = value.endsWith("=") || /[+/]/.test(value) ? base64.decode(value) : base58.decode(value);
|
|
99964
|
+
return bytes.length === 64 ? base64.encode(bytes) : undefined;
|
|
99965
|
+
} catch {
|
|
99966
|
+
return;
|
|
99967
|
+
}
|
|
99968
|
+
}
|
|
99969
|
+
function factValue(fact2) {
|
|
99970
|
+
if (fact2.raw === undefined)
|
|
99971
|
+
return fact2.status;
|
|
99972
|
+
const value = fact2.decimals ? rawUnits2(fact2.raw, fact2.decimals) : fact2.raw;
|
|
99973
|
+
return `${value}${fact2.unit === "token" ? "" : ` ${fact2.unit}`} (${fact2.status}, ${fact2.coverage}, ${terminalText(fact2.source)}${fact2.measuredAt ? `, ${fact2.measuredAt}` : ""})`;
|
|
99974
|
+
}
|
|
99975
|
+
function rawUnits2(raw, decimals) {
|
|
99976
|
+
if (!/^\d+$/.test(raw))
|
|
99977
|
+
return raw;
|
|
99978
|
+
const padded = raw.padStart(decimals + 1, "0");
|
|
99979
|
+
const whole = padded.slice(0, padded.length - decimals);
|
|
99980
|
+
const fraction = padded.slice(padded.length - decimals).replace(/0+$/, "");
|
|
99981
|
+
return fraction ? `${whole}.${fraction}` : whole;
|
|
99982
|
+
}
|
|
99983
|
+
function writeAbout(ctx, about) {
|
|
99984
|
+
const claim = about.claim;
|
|
99985
|
+
writeFields(ctx.deps, "stdout", [
|
|
99986
|
+
["Token", `${about.token.chain} ${about.token.address}`],
|
|
99987
|
+
["Revision", about.revision],
|
|
99988
|
+
[
|
|
99989
|
+
"Claim",
|
|
99990
|
+
claim.state === "unclaimed" ? "unclaimed" : `claimed by ${claim.state === "dev" ? "dev" : "community"}`
|
|
99991
|
+
],
|
|
99992
|
+
["Lead", identity3(claim.lead) ?? "no current eligible lead"],
|
|
99993
|
+
["Eligible", claim.eligibleAccounts === null ? "refreshing" : String(claim.eligibleAccounts)],
|
|
99994
|
+
[
|
|
99995
|
+
"Switches",
|
|
99996
|
+
Object.entries(about.features).map(([name2, on]) => `${name2} ${on ? "on" : "off"}`).join(", ")
|
|
99997
|
+
]
|
|
99998
|
+
], "About");
|
|
99999
|
+
for (const section2 of TOKEN_PAGE_SECTIONS) {
|
|
100000
|
+
const v = about.sections[section2];
|
|
100001
|
+
if (!v)
|
|
100002
|
+
continue;
|
|
100003
|
+
writeHeading(ctx.deps, "stdout", `${section2} (revision ${v.revision})`);
|
|
100004
|
+
writeFields(ctx.deps, "stdout", [
|
|
100005
|
+
["Written by", `${identity3(v.author)}${v.dev ? " (dev)" : ""}`],
|
|
100006
|
+
["Share then", bpsPercent(v.shareBps)],
|
|
100007
|
+
["At", v.createdAt],
|
|
100008
|
+
["Version", v.id]
|
|
100009
|
+
]);
|
|
100010
|
+
if (v.newerAwaitingReview)
|
|
100011
|
+
notice(ctx.deps, "stdout", "note", "A newer version is hidden pending review.");
|
|
100012
|
+
if (v.linksWithheld)
|
|
100013
|
+
notice(ctx.deps, "stdout", "note", "Links are withheld pending review.");
|
|
100014
|
+
ctx.deps.stdout.write(`
|
|
100015
|
+
${terminalTextKeepingLines(v.text)}
|
|
100016
|
+
`);
|
|
100017
|
+
}
|
|
100018
|
+
writeHeading(ctx.deps, "stdout", "Supply, measured by Candle");
|
|
100019
|
+
const facts = about.facts.filter((f) => f.status === "known" || f.status === "stale");
|
|
100020
|
+
if (!facts.length)
|
|
100021
|
+
ctx.deps.stdout.write(` Measurements unavailable
|
|
100022
|
+
`);
|
|
100023
|
+
else
|
|
100024
|
+
writeFields(ctx.deps, "stdout", facts.map((f) => [terminalText(f.metric), factValue(f)]));
|
|
100025
|
+
}
|
|
100026
|
+
function terminalTextKeepingLines(text3) {
|
|
100027
|
+
return text3.split(`
|
|
100028
|
+
`).map((line) => terminalText(line.replace(/\t/g, " "))).join(`
|
|
100029
|
+
`);
|
|
100030
|
+
}
|
|
100031
|
+
async function tokenAbout(args, ctx) {
|
|
100032
|
+
const usage11 = usageFor(ctx, USAGE12.about);
|
|
100033
|
+
const parsed = parseArgs(args, { valueFlags: ["--chain"], booleanFlags: ["--capabilities"] });
|
|
100034
|
+
if ("error" in parsed)
|
|
100035
|
+
return usage11(parsed.error);
|
|
100036
|
+
const chain2 = chainOf(parsed.values);
|
|
100037
|
+
const address = parsed.positionals[0];
|
|
100038
|
+
if (parsed.positionals.length !== 1 || !address)
|
|
100039
|
+
return usage11();
|
|
100040
|
+
if (!chain2)
|
|
100041
|
+
return usage11("--chain must be solana or hood.");
|
|
100042
|
+
const withCapabilities = parsed.booleans.has("--capabilities");
|
|
100043
|
+
const apiKey = withCapabilities ? await requireKey(ctx) : undefined;
|
|
100044
|
+
if (withCapabilities && !apiKey)
|
|
100045
|
+
return 1;
|
|
100046
|
+
const summary = await read2(ctx, pagePath(chain2, address));
|
|
100047
|
+
if (!summary.ok)
|
|
100048
|
+
return writeApiFailure(ctx, summary, "none");
|
|
100049
|
+
let capabilities;
|
|
100050
|
+
if (apiKey) {
|
|
100051
|
+
const own = await read2(ctx, pagePath(chain2, address, "/capabilities"), apiKey);
|
|
100052
|
+
if (!own.ok)
|
|
100053
|
+
return writeApiFailure(ctx, own, "key");
|
|
100054
|
+
capabilities = dataOf(own);
|
|
100055
|
+
}
|
|
100056
|
+
const about = dataOf(summary);
|
|
100057
|
+
if (ctx.json) {
|
|
100058
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data: about, ...apiKey ? { capabilities } : {} })}
|
|
100059
|
+
`);
|
|
100060
|
+
return 0;
|
|
100061
|
+
}
|
|
100062
|
+
writeAbout(ctx, about);
|
|
100063
|
+
if (apiKey) {
|
|
100064
|
+
const c = capabilities;
|
|
100065
|
+
writeFields(ctx.deps, "stdout", [
|
|
100066
|
+
["Can claim", c.canClaim ? "yes" : "no"],
|
|
100067
|
+
["Can edit", c.canEdit ? "yes" : "no"],
|
|
100068
|
+
["Can flag", c.canFlag ? "yes" : "no"],
|
|
100069
|
+
["Creator", c.creatorProven ? "proven" : "not proven"],
|
|
100070
|
+
["Share now", bpsPercent(c.shareBps)],
|
|
100071
|
+
["Observed", c.observedAt],
|
|
100072
|
+
["Why not", c.refusal ? REFUSALS[c.refusal] ?? c.refusal : undefined]
|
|
100073
|
+
], "This account");
|
|
100074
|
+
}
|
|
100075
|
+
return 0;
|
|
100076
|
+
}
|
|
100077
|
+
async function tokenClaim(args, ctx) {
|
|
100078
|
+
const usage11 = usageFor(ctx, USAGE12.claim);
|
|
100079
|
+
const parsed = parseArgs(args, {
|
|
100080
|
+
valueFlags: ["--chain", "--challenge-id", "--signature", "--message-out", "--idempotency-key"],
|
|
100081
|
+
booleanFlags: ["--creator", "--challenge"],
|
|
100082
|
+
pathFlags: ["--message-out"]
|
|
100083
|
+
});
|
|
100084
|
+
if ("error" in parsed)
|
|
100085
|
+
return usage11(parsed.error);
|
|
100086
|
+
const { values, booleans } = parsed;
|
|
100087
|
+
const chain2 = chainOf(values);
|
|
100088
|
+
const address = parsed.positionals[0];
|
|
100089
|
+
if (parsed.positionals.length !== 1 || !address)
|
|
100090
|
+
return usage11();
|
|
100091
|
+
if (!chain2)
|
|
100092
|
+
return usage11("--chain must be solana or hood.");
|
|
100093
|
+
const creator = booleans.has("--creator");
|
|
100094
|
+
const challenge = booleans.has("--challenge");
|
|
100095
|
+
const challengeId = values["--challenge-id"];
|
|
100096
|
+
const given = values["--signature"];
|
|
100097
|
+
if ((challenge || challengeId !== undefined || given !== undefined) && !creator)
|
|
100098
|
+
return usage11("--challenge, --challenge-id and --signature are for --creator.");
|
|
100099
|
+
if (challenge && (challengeId !== undefined || given !== undefined))
|
|
100100
|
+
return usage11("--challenge asks for a new challenge; send the signature in a second run.");
|
|
100101
|
+
if (challengeId === undefined !== (given === undefined))
|
|
100102
|
+
return usage11("--challenge-id and --signature go together.");
|
|
100103
|
+
if (values["--message-out"] !== undefined && !challenge)
|
|
100104
|
+
return usage11("--message-out goes with --challenge.");
|
|
100105
|
+
if (challengeId !== undefined && !UUID2.test(challengeId))
|
|
100106
|
+
return usage11("--challenge-id is the id the challenge printed.");
|
|
100107
|
+
let signature;
|
|
100108
|
+
if (given !== undefined) {
|
|
100109
|
+
signature = signatureFor(chain2, given);
|
|
100110
|
+
if (!signature)
|
|
100111
|
+
return usage11(chain2 === "hood" ? "--signature must be the 0x-hex message signature (65 bytes)." : "--signature must be the 64-byte Ed25519 signature, base58 or base64.");
|
|
100112
|
+
}
|
|
100113
|
+
const idempotencyKey = idempotencyKeyOf(values, usage11);
|
|
100114
|
+
if (typeof idempotencyKey === "number")
|
|
100115
|
+
return idempotencyKey;
|
|
100116
|
+
const apiKey = await requireKey(ctx);
|
|
100117
|
+
if (!apiKey)
|
|
100118
|
+
return 1;
|
|
100119
|
+
if (challenge)
|
|
100120
|
+
return creatorChallenge(ctx, chain2, address, apiKey, values["--message-out"]);
|
|
100121
|
+
const body = { kind: creator ? "creator" : "holder", ...challengeId ? { challengeId, signature } : {} };
|
|
100122
|
+
const result = await write(ctx, {
|
|
100123
|
+
method: "POST",
|
|
100124
|
+
path: pagePath(chain2, address, "/claims"),
|
|
100125
|
+
body,
|
|
100126
|
+
apiKey,
|
|
100127
|
+
idempotencyKey
|
|
100128
|
+
});
|
|
100129
|
+
if (result === "unknown")
|
|
100130
|
+
return writeUnknown(ctx, idempotencyKey);
|
|
100131
|
+
if (!result.ok) {
|
|
100132
|
+
const refusal3 = refusalOf2(result);
|
|
100133
|
+
if (refusal3?.code === "invalid_signature" && creator && !challengeId) {
|
|
100134
|
+
writeRefusal(ctx, refusal3, `This account's known wallets do not include the verified creator, so the claim needs a signed challenge: candle token claim ${address} --chain ${chain2} --creator --challenge`);
|
|
100135
|
+
return 1;
|
|
100136
|
+
}
|
|
100137
|
+
return writeApiFailure(ctx, result, "key");
|
|
100138
|
+
}
|
|
100139
|
+
const data = dataOf(result);
|
|
100140
|
+
if (ctx.json)
|
|
100141
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, idempotencyKey })}
|
|
100142
|
+
`);
|
|
100143
|
+
else
|
|
100144
|
+
writeFields(ctx.deps, "stdout", [
|
|
100145
|
+
["Kind", data.kind],
|
|
100146
|
+
["Creator", data.creatorProven ? "proven" : "not proven"],
|
|
100147
|
+
["Revision", data.revision]
|
|
100148
|
+
], "Claimed");
|
|
100149
|
+
return 0;
|
|
100150
|
+
}
|
|
100151
|
+
async function creatorChallenge(ctx, chain2, address, apiKey, messageOut) {
|
|
100152
|
+
const result = await apiRequest(pagePath(chain2, address, "/creator-challenges"), {
|
|
100153
|
+
apiUrl: ctx.apiUrl,
|
|
100154
|
+
method: "POST",
|
|
100155
|
+
body: {},
|
|
100156
|
+
auth: "key",
|
|
100157
|
+
credentials: { apiKey },
|
|
100158
|
+
fetch: ctx.deps.fetch,
|
|
100159
|
+
env: ctx.deps.env
|
|
100160
|
+
});
|
|
100161
|
+
if (!result.ok)
|
|
100162
|
+
return writeApiFailure(ctx, result, "key");
|
|
100163
|
+
const data = dataOf(result);
|
|
100164
|
+
const bytes = new TextEncoder().encode(data.message);
|
|
100165
|
+
const sha2564 = createHash5("sha256").update(bytes).digest("hex");
|
|
100166
|
+
if (messageOut !== undefined) {
|
|
100167
|
+
try {
|
|
100168
|
+
await ctx.deps.writeFile(messageOut, data.message);
|
|
100169
|
+
} catch (error) {
|
|
100170
|
+
writeLocalFailure(ctx.deps, {
|
|
100171
|
+
code: "MESSAGE_OUT_FAILED",
|
|
100172
|
+
message: `The challenge could not be written to ${messageOut}: ${error instanceof Error ? error.message : error}.`,
|
|
100173
|
+
details: { challengeId: data.challengeId }
|
|
100174
|
+
}, ctx.json);
|
|
100175
|
+
return 1;
|
|
100176
|
+
}
|
|
100177
|
+
}
|
|
100178
|
+
const next = `candle token claim ${address} --chain ${chain2} --creator --challenge-id ${data.challengeId} --signature <signature>`;
|
|
100179
|
+
if (ctx.json) {
|
|
100180
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, byteLength: bytes.length, sha256: sha2564, ...messageOut ? { messageOut } : {}, next })}
|
|
100181
|
+
`);
|
|
100182
|
+
return 0;
|
|
100183
|
+
}
|
|
100184
|
+
writeFields(ctx.deps, "stdout", [
|
|
100185
|
+
["Challenge", data.challengeId],
|
|
100186
|
+
["Expires", data.expiresAt],
|
|
100187
|
+
["Bytes", String(bytes.length)],
|
|
100188
|
+
["sha256", sha2564],
|
|
100189
|
+
["Written to", messageOut]
|
|
100190
|
+
], "Creator challenge (a message, not a transaction)");
|
|
100191
|
+
ctx.deps.stdout.write(`
|
|
100192
|
+
Sign exactly these UTF-8 bytes (between the lines, no trailing newline) with the creator wallet:
|
|
100193
|
+
-----
|
|
100194
|
+
${data.message}
|
|
100195
|
+
-----
|
|
100196
|
+
`);
|
|
100197
|
+
notice(ctx.deps, "stdout", "note", `Sign it on your own machine, for example candle sign message --wallet <external> --file ${messageOut ?? "<file holding exactly these bytes>"}, then run: ${next}`);
|
|
100198
|
+
return 0;
|
|
100199
|
+
}
|
|
100200
|
+
var SECTION_FLAGS = {
|
|
100201
|
+
set: { valueFlags: ["--chain", "--file", "--base-revision", "--idempotency-key"], booleanFlags: ["--clear"] },
|
|
100202
|
+
restore: { valueFlags: ["--chain", "--version-id", "--base-revision", "--idempotency-key"], booleanFlags: [] }
|
|
100203
|
+
};
|
|
100204
|
+
async function tokenSection(args, ctx) {
|
|
100205
|
+
const usage11 = usageFor(ctx, USAGE12.section);
|
|
100206
|
+
const action2 = args[0];
|
|
100207
|
+
if (action2 !== "set" && action2 !== "restore")
|
|
100208
|
+
return usage11();
|
|
100209
|
+
const spec = SECTION_FLAGS[action2];
|
|
100210
|
+
const parsed = parseArgs(args.slice(1), {
|
|
100211
|
+
valueFlags: [...spec.valueFlags],
|
|
100212
|
+
booleanFlags: [...spec.booleanFlags],
|
|
100213
|
+
...action2 === "set" ? { pathFlags: ["--file"] } : {}
|
|
100214
|
+
});
|
|
100215
|
+
if ("error" in parsed)
|
|
100216
|
+
return usage11(parsed.error);
|
|
100217
|
+
const { values } = parsed;
|
|
100218
|
+
const chain2 = chainOf(values);
|
|
100219
|
+
const [address, sectionName] = parsed.positionals;
|
|
100220
|
+
if (parsed.positionals.length !== 2 || !address)
|
|
100221
|
+
return usage11();
|
|
100222
|
+
if (!chain2)
|
|
100223
|
+
return usage11("--chain must be solana or hood.");
|
|
100224
|
+
const section2 = sectionOf(sectionName);
|
|
100225
|
+
if (!section2)
|
|
100226
|
+
return usage11("The section is thesis, tokenomics or lore.");
|
|
100227
|
+
const base2 = values["--base-revision"];
|
|
100228
|
+
if (base2 === undefined || !REVISION.test(base2))
|
|
100229
|
+
return usage11("--base-revision is the section's current revision (0 for a first write), from candle token about.");
|
|
100230
|
+
const idempotencyKey = idempotencyKeyOf(values, usage11);
|
|
100231
|
+
if (typeof idempotencyKey === "number")
|
|
100232
|
+
return idempotencyKey;
|
|
100233
|
+
let body;
|
|
100234
|
+
let path;
|
|
100235
|
+
let method;
|
|
100236
|
+
if (action2 === "set") {
|
|
100237
|
+
const file = values["--file"];
|
|
100238
|
+
if (file === undefined)
|
|
100239
|
+
return usage11("--file is required: the section text comes from a file.");
|
|
100240
|
+
let bytes;
|
|
100241
|
+
try {
|
|
100242
|
+
bytes = await ctx.deps.readBytes(file);
|
|
100243
|
+
} catch (error) {
|
|
100244
|
+
writeLocalFailure(ctx.deps, {
|
|
100245
|
+
code: "FILE_UNREADABLE",
|
|
100246
|
+
message: `${file} could not be read: ${error instanceof Error ? error.message : error}.`
|
|
100247
|
+
}, ctx.json);
|
|
100248
|
+
return 2;
|
|
100249
|
+
}
|
|
100250
|
+
const checked = sectionText(bytes, section2);
|
|
100251
|
+
if ("error" in checked) {
|
|
100252
|
+
writeLocalFailure(ctx.deps, { code: "TEXT_REFUSED", message: `${checked.error} Nothing was sent.` }, ctx.json);
|
|
100253
|
+
return 2;
|
|
100254
|
+
}
|
|
100255
|
+
const empty = checked.text.trim().length === 0;
|
|
100256
|
+
if (empty && !parsed.booleans.has("--clear"))
|
|
100257
|
+
return usage11("The file is empty. An empty write clears the section; pass --clear to mean that.");
|
|
100258
|
+
if (!empty && parsed.booleans.has("--clear"))
|
|
100259
|
+
return usage11("--clear is for an empty file.");
|
|
100260
|
+
body = { text: checked.text, baseRevision: base2 };
|
|
100261
|
+
path = pagePath(chain2, address, `/sections/${section2}`);
|
|
100262
|
+
method = "PUT";
|
|
100263
|
+
} else {
|
|
100264
|
+
const versionId = values["--version-id"];
|
|
100265
|
+
if (versionId === undefined || !UUID2.test(versionId))
|
|
100266
|
+
return usage11("--version-id is a version id from candle token history.");
|
|
100267
|
+
body = { versionId, baseRevision: base2 };
|
|
100268
|
+
path = pagePath(chain2, address, `/sections/${section2}/restore`);
|
|
100269
|
+
method = "POST";
|
|
100270
|
+
}
|
|
100271
|
+
const apiKey = await requireKey(ctx);
|
|
100272
|
+
if (!apiKey)
|
|
100273
|
+
return 1;
|
|
100274
|
+
const result = await write(ctx, { method, path, body, apiKey, idempotencyKey });
|
|
100275
|
+
if (result === "unknown")
|
|
100276
|
+
return writeUnknown(ctx, idempotencyKey);
|
|
100277
|
+
if (!result.ok) {
|
|
100278
|
+
const refusal3 = refusalOf2(result);
|
|
100279
|
+
if (refusal3?.code === "revision_conflict") {
|
|
100280
|
+
const latest = refusal3.extra.latest;
|
|
100281
|
+
writeRefusal(ctx, refusal3, `${latest?.revision !== undefined ? `The latest is revision ${latest.revision}. ` : ""}${action2 === "set" ? SECTION_SUGGESTION : "Rerun with --base-revision set to the latest revision."}`);
|
|
100282
|
+
return EXIT_REVISION_CONFLICT;
|
|
100283
|
+
}
|
|
100284
|
+
return writeApiFailure(ctx, result, "key");
|
|
100285
|
+
}
|
|
100286
|
+
const data = dataOf(result);
|
|
100287
|
+
if (ctx.json)
|
|
100288
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, idempotencyKey })}
|
|
100289
|
+
`);
|
|
100290
|
+
else
|
|
100291
|
+
writeFields(ctx.deps, "stdout", [
|
|
100292
|
+
["Revision", data.revision],
|
|
100293
|
+
["Version", data.id]
|
|
100294
|
+
], action2 === "set" ? `${section2} saved` : `${section2} restored as a new version`);
|
|
100295
|
+
return 0;
|
|
100296
|
+
}
|
|
100297
|
+
async function tokenHistory(args, ctx) {
|
|
100298
|
+
const usage11 = usageFor(ctx, USAGE12.history);
|
|
100299
|
+
const parsed = parseArgs(args, { valueFlags: ["--chain", "--limit", "--cursor"] });
|
|
100300
|
+
if ("error" in parsed)
|
|
100301
|
+
return usage11(parsed.error);
|
|
100302
|
+
const { values } = parsed;
|
|
100303
|
+
const chain2 = chainOf(values);
|
|
100304
|
+
const [address, sectionName] = parsed.positionals;
|
|
100305
|
+
if (parsed.positionals.length !== 2 || !address)
|
|
100306
|
+
return usage11();
|
|
100307
|
+
if (!chain2)
|
|
100308
|
+
return usage11("--chain must be solana or hood.");
|
|
100309
|
+
const section2 = sectionOf(sectionName);
|
|
100310
|
+
if (!section2)
|
|
100311
|
+
return usage11("The section is thesis, tokenomics or lore.");
|
|
100312
|
+
const limit = values["--limit"];
|
|
100313
|
+
if (limit !== undefined && (!/^[1-9]\d?$/.test(limit) || Number(limit) > 50))
|
|
100314
|
+
return usage11("--limit is a whole number from 1 to 50.");
|
|
100315
|
+
const query = new URLSearchParams({
|
|
100316
|
+
...limit !== undefined ? { limit } : {},
|
|
100317
|
+
...values["--cursor"] !== undefined ? { cursor: values["--cursor"] } : {}
|
|
100318
|
+
});
|
|
100319
|
+
const result = await read2(ctx, pagePath(chain2, address, `/sections/${section2}/history${query.size ? `?${query}` : ""}`));
|
|
100320
|
+
if (!result.ok)
|
|
100321
|
+
return writeApiFailure(ctx, result, "none");
|
|
100322
|
+
const data = dataOf(result);
|
|
100323
|
+
if (ctx.json) {
|
|
100324
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data })}
|
|
100325
|
+
`);
|
|
100326
|
+
return 0;
|
|
100327
|
+
}
|
|
100328
|
+
if (!data.items.length)
|
|
100329
|
+
ctx.deps.stdout.write(`No ${section2} versions.
|
|
100330
|
+
`);
|
|
100331
|
+
for (const item of data.items) {
|
|
100332
|
+
writeHeading(ctx.deps, "stdout", `revision ${item.revision}`);
|
|
100333
|
+
writeFields(ctx.deps, "stdout", [
|
|
100334
|
+
["Version", item.id],
|
|
100335
|
+
["Written by", `${identity3(item.author)}${item.dev ? " (dev)" : ""}`],
|
|
100336
|
+
["Share then", bpsPercent(item.shareBps)],
|
|
100337
|
+
["At", item.createdAt],
|
|
100338
|
+
["Restored from", item.restoredFrom ?? undefined],
|
|
100339
|
+
["State", item.tombstone ?? "visible"]
|
|
100340
|
+
]);
|
|
100341
|
+
if (item.text !== null)
|
|
100342
|
+
ctx.deps.stdout.write(`
|
|
100343
|
+
${terminalTextKeepingLines(item.text)}
|
|
100344
|
+
`);
|
|
100345
|
+
}
|
|
100346
|
+
if (data.nextCursor)
|
|
100347
|
+
ctx.deps.stdout.write(`
|
|
100348
|
+
More: --cursor ${data.nextCursor}
|
|
100349
|
+
`);
|
|
100350
|
+
return 0;
|
|
100351
|
+
}
|
|
100352
|
+
async function tokenFlag(args, ctx) {
|
|
100353
|
+
const usage11 = usageFor(ctx, USAGE12.flag);
|
|
100354
|
+
const parsed = parseArgs(args, { valueFlags: ["--chain", "--content", "--reason", "--detail", "--idempotency-key"] });
|
|
100355
|
+
if ("error" in parsed)
|
|
100356
|
+
return usage11(parsed.error);
|
|
100357
|
+
const { values } = parsed;
|
|
100358
|
+
const chain2 = chainOf(values);
|
|
100359
|
+
const address = parsed.positionals[0];
|
|
100360
|
+
if (parsed.positionals.length !== 1 || !address)
|
|
100361
|
+
return usage11();
|
|
100362
|
+
if (!chain2)
|
|
100363
|
+
return usage11("--chain must be solana or hood.");
|
|
100364
|
+
const contentId = values["--content"];
|
|
100365
|
+
if (contentId === undefined || !UUID2.test(contentId))
|
|
100366
|
+
return usage11("--content is the exact version id being reported (candle token about or history).");
|
|
100367
|
+
const reason = values["--reason"];
|
|
100368
|
+
if (!reason || !FLAG_REASONS.includes(reason))
|
|
100369
|
+
return usage11(`--reason is one of ${FLAG_REASONS.join(", ")}.`);
|
|
100370
|
+
const detail = values["--detail"];
|
|
100371
|
+
if (detail !== undefined && [...detail].length > FLAG_DETAIL_LIMIT)
|
|
100372
|
+
return usage11(`--detail is at most ${FLAG_DETAIL_LIMIT} characters.`);
|
|
100373
|
+
const idempotencyKey = idempotencyKeyOf(values, usage11);
|
|
100374
|
+
if (typeof idempotencyKey === "number")
|
|
100375
|
+
return idempotencyKey;
|
|
100376
|
+
const apiKey = await requireKey(ctx);
|
|
100377
|
+
if (!apiKey)
|
|
100378
|
+
return 1;
|
|
100379
|
+
const body = { contentId, reason, ...detail !== undefined ? { detail } : {} };
|
|
100380
|
+
const result = await write(ctx, {
|
|
100381
|
+
method: "POST",
|
|
100382
|
+
path: pagePath(chain2, address, "/flags"),
|
|
100383
|
+
body,
|
|
100384
|
+
apiKey,
|
|
100385
|
+
idempotencyKey
|
|
100386
|
+
});
|
|
100387
|
+
if (result === "unknown")
|
|
100388
|
+
return writeUnknown(ctx, idempotencyKey);
|
|
100389
|
+
if (!result.ok)
|
|
100390
|
+
return writeApiFailure(ctx, result, "key");
|
|
100391
|
+
const data = dataOf(result);
|
|
100392
|
+
if (ctx.json)
|
|
100393
|
+
ctx.deps.stdout.write(`${JSON.stringify({ ok: true, data, idempotencyKey })}
|
|
100394
|
+
`);
|
|
100395
|
+
else
|
|
100396
|
+
writeFields(ctx.deps, "stdout", [
|
|
100397
|
+
["Content", data.contentId],
|
|
100398
|
+
["Hidden", data.hidden ? "yes, pending Candle review" : "no"]
|
|
100399
|
+
], "Reported");
|
|
100400
|
+
return 0;
|
|
100401
|
+
}
|
|
100402
|
+
|
|
99526
100403
|
// src/commands/token-paid.ts
|
|
99527
100404
|
init_args();
|
|
99528
100405
|
init_deps();
|
|
99529
100406
|
init_render();
|
|
99530
100407
|
init_solana_endpoint();
|
|
99531
100408
|
init_solana_lite();
|
|
99532
|
-
import { randomUUID as
|
|
100409
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
99533
100410
|
|
|
99534
100411
|
// src/x402/pay.ts
|
|
99535
100412
|
init_client();
|
|
@@ -99548,7 +100425,7 @@ class X402DecodeError extends Error {
|
|
|
99548
100425
|
}
|
|
99549
100426
|
}
|
|
99550
100427
|
var BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
99551
|
-
var
|
|
100428
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
99552
100429
|
var isString = (value) => typeof value === "string";
|
|
99553
100430
|
function encodeHeader(value) {
|
|
99554
100431
|
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
|
@@ -99563,12 +100440,12 @@ function decodeHeader(value) {
|
|
|
99563
100440
|
} catch {
|
|
99564
100441
|
throw new X402DecodeError("not JSON");
|
|
99565
100442
|
}
|
|
99566
|
-
if (!
|
|
100443
|
+
if (!isObject3(parsed))
|
|
99567
100444
|
throw new X402DecodeError("not a JSON object");
|
|
99568
100445
|
return parsed;
|
|
99569
100446
|
}
|
|
99570
100447
|
function parseRequirements(value) {
|
|
99571
|
-
if (!
|
|
100448
|
+
if (!isObject3(value))
|
|
99572
100449
|
throw new X402DecodeError("requirements are not an object");
|
|
99573
100450
|
const { scheme, network: network2, amount, asset, payTo, maxTimeoutSeconds, extra } = value;
|
|
99574
100451
|
if (!isString(scheme) || !isString(network2) || !isString(amount) || !isString(asset) || !isString(payTo)) {
|
|
@@ -99577,7 +100454,7 @@ function parseRequirements(value) {
|
|
|
99577
100454
|
if (typeof maxTimeoutSeconds !== "number" || !Number.isInteger(maxTimeoutSeconds)) {
|
|
99578
100455
|
throw new X402DecodeError("maxTimeoutSeconds is not an integer");
|
|
99579
100456
|
}
|
|
99580
|
-
if (extra !== undefined && !
|
|
100457
|
+
if (extra !== undefined && !isObject3(extra))
|
|
99581
100458
|
throw new X402DecodeError("extra is not an object");
|
|
99582
100459
|
return { scheme, network: network2, amount, asset, payTo, maxTimeoutSeconds, ...extra ? { extra } : {} };
|
|
99583
100460
|
}
|
|
@@ -99586,7 +100463,7 @@ function decodePaymentRequired(header) {
|
|
|
99586
100463
|
if (object4.x402Version !== X402_VERSION)
|
|
99587
100464
|
throw new X402DecodeError("x402Version is not 2");
|
|
99588
100465
|
const resource = object4.resource;
|
|
99589
|
-
if (!
|
|
100466
|
+
if (!isObject3(resource) || !isString(resource.url))
|
|
99590
100467
|
throw new X402DecodeError("resource is missing");
|
|
99591
100468
|
if (!Array.isArray(object4.accepts))
|
|
99592
100469
|
throw new X402DecodeError("accepts is not an array");
|
|
@@ -99601,7 +100478,7 @@ function decodePaymentRequired(header) {
|
|
|
99601
100478
|
...isString(resource.mimeType) ? { mimeType: resource.mimeType } : {}
|
|
99602
100479
|
},
|
|
99603
100480
|
accepts: object4.accepts.map(parseRequirements),
|
|
99604
|
-
...
|
|
100481
|
+
...isObject3(object4.extensions) ? { extensions: object4.extensions } : {}
|
|
99605
100482
|
};
|
|
99606
100483
|
}
|
|
99607
100484
|
function paymentRequiredOf(headers2) {
|
|
@@ -99647,14 +100524,14 @@ function encodePaymentPayload(args) {
|
|
|
99647
100524
|
// src/x402/siwx.ts
|
|
99648
100525
|
init_esm();
|
|
99649
100526
|
var SIWX_EXTENSION = "sign-in-with-x";
|
|
99650
|
-
var
|
|
100527
|
+
var isObject4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
99651
100528
|
function spendChallengeOf(extensions, apiHost, network2) {
|
|
99652
100529
|
const extension = extensions?.[SIWX_EXTENSION];
|
|
99653
|
-
if (!
|
|
100530
|
+
if (!isObject4(extension) || !isObject4(extension.info))
|
|
99654
100531
|
return null;
|
|
99655
100532
|
const info3 = extension.info;
|
|
99656
100533
|
const chains = Array.isArray(extension.supportedChains) ? extension.supportedChains : [];
|
|
99657
|
-
const chain2 = chains.find((c) =>
|
|
100534
|
+
const chain2 = chains.find((c) => isObject4(c) && c.chainId === network2 && c.type === "ed25519");
|
|
99658
100535
|
if (!chain2)
|
|
99659
100536
|
return null;
|
|
99660
100537
|
const { domain, uri, version, nonce, issuedAt, expirationTime } = info3;
|
|
@@ -100121,7 +100998,7 @@ async function runPaidRead(ctx, input) {
|
|
|
100121
100998
|
blockhash: blockhashFor(ctx),
|
|
100122
100999
|
env: ctx.deps.env,
|
|
100123
101000
|
now: ctx.deps.now,
|
|
100124
|
-
newId:
|
|
101001
|
+
newId: randomUUID11
|
|
100125
101002
|
});
|
|
100126
101003
|
const line = paymentLine(paid);
|
|
100127
101004
|
if (line && !ctx.json)
|
|
@@ -100216,7 +101093,7 @@ init_render();
|
|
|
100216
101093
|
init_solana_verify();
|
|
100217
101094
|
init_trading();
|
|
100218
101095
|
init_swap();
|
|
100219
|
-
var
|
|
101096
|
+
var USAGE13 = "Usage: candle transfer --to <address|wallet name|vault> --asset <SOL|USDC|CNDL|ETH|USDG>|--mint <mint>|--token <0x...> --amount <decimal|max> [--wallet <name>] [--rpc-url <url>] [--yes] [--json]";
|
|
100220
101097
|
var TRANSFER_ASSETS = [...Object.keys(BASES), ...Object.keys(HOOD_BASES)];
|
|
100221
101098
|
var BASE58_ADDRESS = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
100222
101099
|
async function readLinkedWallets(ctx, key) {
|
|
@@ -100279,9 +101156,9 @@ function describeDestination(destination) {
|
|
|
100279
101156
|
return `address ${destination.address} (not one of this account's linked wallets; Candle decides whether it is allowed)`;
|
|
100280
101157
|
}
|
|
100281
101158
|
}
|
|
100282
|
-
function writePreview(ctx,
|
|
101159
|
+
function writePreview(ctx, title2, rows, note) {
|
|
100283
101160
|
const style = terminalStyle(ctx.deps, "stderr");
|
|
100284
|
-
ctx.deps.stderr.write(`${heading(
|
|
101161
|
+
ctx.deps.stderr.write(`${heading(title2, style)}
|
|
100285
101162
|
|
|
100286
101163
|
${fields(rows, style).join(`
|
|
100287
101164
|
`)}
|
|
@@ -100312,7 +101189,7 @@ async function transfer(args, ctx) {
|
|
|
100312
101189
|
const flags2 = parsed.values;
|
|
100313
101190
|
const asset = flags2["--asset"]?.toUpperCase();
|
|
100314
101191
|
if (parsed.positionals.length !== 0 || !flags2["--to"] || !flags2["--amount"] || [flags2["--asset"], flags2["--mint"], flags2["--token"]].filter(Boolean).length !== 1 || asset !== undefined && !TRANSFER_ASSETS.includes(asset) || flags2["--mint"] !== undefined && !BASE58_ADDRESS.test(flags2["--mint"]) || flags2["--token"] !== undefined && !checkEvmAddress(flags2["--token"]).ok) {
|
|
100315
|
-
writeUsageFailure(ctx.deps,
|
|
101192
|
+
writeUsageFailure(ctx.deps, USAGE13, ctx.json);
|
|
100316
101193
|
return 2;
|
|
100317
101194
|
}
|
|
100318
101195
|
const to = flags2["--to"];
|
|
@@ -100569,7 +101446,7 @@ import { randomBytes as randomBytes11 } from "node:crypto";
|
|
|
100569
101446
|
init_presentation();
|
|
100570
101447
|
var FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
100571
101448
|
var INTERVAL_MS3 = 80;
|
|
100572
|
-
function stepReporter(
|
|
101449
|
+
function stepReporter(write2, tty, ascii = false) {
|
|
100573
101450
|
let detach = () => {};
|
|
100574
101451
|
let timer = null;
|
|
100575
101452
|
let frame = 0;
|
|
@@ -100580,7 +101457,7 @@ function stepReporter(write, tty, ascii = false) {
|
|
|
100580
101457
|
clearInterval(timer);
|
|
100581
101458
|
timer = null;
|
|
100582
101459
|
if (tty && current !== null)
|
|
100583
|
-
|
|
101460
|
+
write2("\r\x1B[2K");
|
|
100584
101461
|
current = null;
|
|
100585
101462
|
};
|
|
100586
101463
|
return {
|
|
@@ -100588,7 +101465,7 @@ function stepReporter(write, tty, ascii = false) {
|
|
|
100588
101465
|
clearSpinner();
|
|
100589
101466
|
current = label;
|
|
100590
101467
|
if (!tty) {
|
|
100591
|
-
|
|
101468
|
+
write2(`${label}...
|
|
100592
101469
|
`);
|
|
100593
101470
|
return;
|
|
100594
101471
|
}
|
|
@@ -100607,21 +101484,21 @@ function stepReporter(write, tty, ascii = false) {
|
|
|
100607
101484
|
process.off("SIGTERM", sigterm);
|
|
100608
101485
|
};
|
|
100609
101486
|
timer = setInterval(() => {
|
|
100610
|
-
|
|
101487
|
+
write2(`\r\x1B[2K${FRAMES[frame % FRAMES.length]} ${current}`);
|
|
100611
101488
|
frame++;
|
|
100612
101489
|
}, INTERVAL_MS3);
|
|
100613
101490
|
if (typeof timer === "object" && "unref" in timer)
|
|
100614
101491
|
timer.unref();
|
|
100615
|
-
|
|
101492
|
+
write2(`${FRAMES[0]} ${label}`);
|
|
100616
101493
|
},
|
|
100617
101494
|
done(label) {
|
|
100618
101495
|
clearSpinner();
|
|
100619
|
-
|
|
101496
|
+
write2(`${ascii ? "OK" : "✓"} ${label}
|
|
100620
101497
|
`);
|
|
100621
101498
|
},
|
|
100622
101499
|
fail(label) {
|
|
100623
101500
|
clearSpinner();
|
|
100624
|
-
|
|
101501
|
+
write2(`${ascii ? "FAIL" : "✗"} ${label}
|
|
100625
101502
|
`);
|
|
100626
101503
|
},
|
|
100627
101504
|
stop: clearSpinner
|
|
@@ -100643,7 +101520,7 @@ init_release();
|
|
|
100643
101520
|
|
|
100644
101521
|
// src/release-assets.ts
|
|
100645
101522
|
init_release();
|
|
100646
|
-
import { createHash as
|
|
101523
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
100647
101524
|
|
|
100648
101525
|
// src/bun-crypto-shim.ts
|
|
100649
101526
|
import crypto3, { KeyObject } from "node:crypto";
|
|
@@ -100683,7 +101560,7 @@ installBunCryptoShim();
|
|
|
100683
101560
|
var import_bundle = __toESM(require_dist3(), 1);
|
|
100684
101561
|
var import_protobuf_specs = __toESM(require_dist2(), 1);
|
|
100685
101562
|
var import_verify = __toESM(require_dist5(), 1);
|
|
100686
|
-
import { createHash as
|
|
101563
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
100687
101564
|
// src/sigstore-trusted-root.json
|
|
100688
101565
|
var sigstore_trusted_root_default = {
|
|
100689
101566
|
mediaType: "application/vnd.dev.sigstore.trustedroot+json;version=0.1",
|
|
@@ -100843,7 +101720,7 @@ function verifyReleaseAsset(bytes, bundleJson, identityUri, issuer) {
|
|
|
100843
101720
|
return { ok: false, reason: LEGACY_BUNDLE_REASON };
|
|
100844
101721
|
try {
|
|
100845
101722
|
const bundle = import_bundle.bundleFromJSON(bundleJson);
|
|
100846
|
-
const digest =
|
|
101723
|
+
const digest = createHash6("sha256").update(bytes).digest();
|
|
100847
101724
|
const content = bundle.content;
|
|
100848
101725
|
if (content.$case === "messageSignature") {
|
|
100849
101726
|
const claimed = content.messageSignature.messageDigest?.digest;
|
|
@@ -100906,7 +101783,7 @@ async function downloadSums(deps, base2, tag) {
|
|
|
100906
101783
|
}
|
|
100907
101784
|
}
|
|
100908
101785
|
function checkReleaseAsset(deps, opts) {
|
|
100909
|
-
const actual =
|
|
101786
|
+
const actual = createHash7("sha256").update(opts.bytes).digest("hex");
|
|
100910
101787
|
const fromSums = opts.sums.split(`
|
|
100911
101788
|
`).map((line) => line.trim().split(/\s+/)).find((parts) => parts[1] === opts.name)?.[0];
|
|
100912
101789
|
if (actual !== opts.expectedSha256 || actual !== fromSums) {
|
|
@@ -104290,7 +105167,7 @@ async function readLamports(addresses, solana, ctx) {
|
|
|
104290
105167
|
}
|
|
104291
105168
|
async function readEvmBalances(addresses, rpcUrl, fetchFn) {
|
|
104292
105169
|
const rpc = createEvmRpc(rpcUrl, fetchFn);
|
|
104293
|
-
const
|
|
105170
|
+
const read3 = {
|
|
104294
105171
|
host: rpcHostOf2(rpcUrl),
|
|
104295
105172
|
chainId: undefined,
|
|
104296
105173
|
requests: 0,
|
|
@@ -104299,30 +105176,30 @@ async function readEvmBalances(addresses, rpcUrl, fetchFn) {
|
|
|
104299
105176
|
unavailable: []
|
|
104300
105177
|
};
|
|
104301
105178
|
try {
|
|
104302
|
-
|
|
104303
|
-
|
|
105179
|
+
read3.requests += 1;
|
|
105180
|
+
read3.chainId = await rpc.chainId();
|
|
104304
105181
|
} catch (error) {
|
|
104305
|
-
|
|
104306
|
-
|
|
104307
|
-
return
|
|
105182
|
+
read3.unavailable.push(...addresses);
|
|
105183
|
+
read3.failure = error instanceof Error ? error.message : String(error);
|
|
105184
|
+
return read3;
|
|
104308
105185
|
}
|
|
104309
|
-
const hood =
|
|
105186
|
+
const hood = read3.chainId === BigInt(HOOD_CHAIN_ID);
|
|
104310
105187
|
for (const address of addresses) {
|
|
104311
105188
|
try {
|
|
104312
|
-
|
|
104313
|
-
|
|
105189
|
+
read3.requests += 1;
|
|
105190
|
+
read3.wei.set(address, await rpc.getBalance(address));
|
|
104314
105191
|
if (hood) {
|
|
104315
|
-
|
|
104316
|
-
|
|
105192
|
+
read3.requests += 1;
|
|
105193
|
+
read3.usdg.set(address, await rpc.erc20BalanceOf(HOOD_USDG_ADDRESS, address));
|
|
104317
105194
|
}
|
|
104318
105195
|
} catch (error) {
|
|
104319
|
-
|
|
104320
|
-
|
|
104321
|
-
|
|
104322
|
-
|
|
105196
|
+
read3.wei.delete(address);
|
|
105197
|
+
read3.usdg.delete(address);
|
|
105198
|
+
read3.unavailable.push(address);
|
|
105199
|
+
read3.failure ??= error instanceof Error ? error.message : String(error);
|
|
104323
105200
|
}
|
|
104324
105201
|
}
|
|
104325
|
-
return
|
|
105202
|
+
return read3;
|
|
104326
105203
|
}
|
|
104327
105204
|
function evmRequestsPlanned(count3) {
|
|
104328
105205
|
return `${1 + count3} to ${1 + 2 * count3}`;
|
|
@@ -105195,12 +106072,12 @@ async function promoteFresh(ctx, parsed, fromLabel, toKey) {
|
|
|
105195
106072
|
});
|
|
105196
106073
|
}
|
|
105197
106074
|
async function refuseWithoutRoom(ctx) {
|
|
105198
|
-
const
|
|
105199
|
-
if (!
|
|
105200
|
-
notice(ctx.deps, "stderr", "note", unreadableRoomLine(
|
|
106075
|
+
const read3 = await readAccountRoom(ctx);
|
|
106076
|
+
if (!read3.ok) {
|
|
106077
|
+
notice(ctx.deps, "stderr", "note", unreadableRoomLine(read3.reason).trim());
|
|
105201
106078
|
return;
|
|
105202
106079
|
}
|
|
105203
|
-
const refusal3 = singleRoomRefusal(
|
|
106080
|
+
const refusal3 = singleRoomRefusal(read3.room);
|
|
105204
106081
|
if (refusal3 !== null)
|
|
105205
106082
|
throw refusal3;
|
|
105206
106083
|
}
|
|
@@ -107023,15 +107900,15 @@ function parseCounts(count3, teeCount, externalCount, rpcUrl, evmCount, evmTeeCo
|
|
|
107023
107900
|
if (typeof evmTeeParsed === "object" && evmTeeParsed !== null)
|
|
107024
107901
|
return evmTeeParsed;
|
|
107025
107902
|
const allOmitted = vaultCount === undefined && teeParsed === undefined && externalParsed === undefined;
|
|
107026
|
-
const
|
|
107903
|
+
const scan2 = rpcUrl !== undefined;
|
|
107027
107904
|
const resolve6 = (value) => {
|
|
107028
107905
|
if (value !== undefined)
|
|
107029
107906
|
return value;
|
|
107030
|
-
return
|
|
107907
|
+
return scan2 ? undefined : 1;
|
|
107031
107908
|
};
|
|
107032
|
-
const solanaVault = allOmitted && !
|
|
107033
|
-
const solanaTee = allOmitted && !
|
|
107034
|
-
const solanaExternal = allOmitted && !
|
|
107909
|
+
const solanaVault = allOmitted && !scan2 ? 1 : resolve6(vaultCount);
|
|
107910
|
+
const solanaTee = allOmitted && !scan2 ? 1 : resolve6(teeParsed);
|
|
107911
|
+
const solanaExternal = allOmitted && !scan2 ? 1 : resolve6(externalParsed);
|
|
107035
107912
|
return {
|
|
107036
107913
|
solanaVault,
|
|
107037
107914
|
solanaTee,
|
|
@@ -107133,20 +108010,20 @@ async function addressLooksUsed(rpc, address) {
|
|
|
107133
108010
|
return rpc.hasSignatureHistory(address);
|
|
107134
108011
|
}
|
|
107135
108012
|
async function matchAgainstAccount(ctx, apiKey, derived) {
|
|
107136
|
-
const
|
|
107137
|
-
if (!
|
|
108013
|
+
const read3 = await completeLinkedWalletRead(ctx, apiKey);
|
|
108014
|
+
if (!read3.complete) {
|
|
107138
108015
|
return {
|
|
107139
108016
|
matched: [],
|
|
107140
108017
|
unmatched: [],
|
|
107141
|
-
account:
|
|
108018
|
+
account: read3.account || undefined,
|
|
107142
108019
|
complete: false,
|
|
107143
|
-
reason:
|
|
108020
|
+
reason: read3.incompleteReason
|
|
107144
108021
|
};
|
|
107145
108022
|
}
|
|
107146
108023
|
ctx.deps.stdout.write(`
|
|
107147
|
-
This profile acts as account ${
|
|
108024
|
+
This profile acts as account ${read3.account}.
|
|
107148
108025
|
`);
|
|
107149
|
-
const confirmed = await typedConfirm(ctx, "account",
|
|
108026
|
+
const confirmed = await typedConfirm(ctx, "account", read3.account, `record exposure against account ${read3.account}`);
|
|
107150
108027
|
if (!confirmed) {
|
|
107151
108028
|
return {
|
|
107152
108029
|
matched: [],
|
|
@@ -107160,7 +108037,7 @@ This profile acts as account ${read2.account}.
|
|
|
107160
108037
|
const matched = [];
|
|
107161
108038
|
const unmatched = [];
|
|
107162
108039
|
const externalListed = [];
|
|
107163
|
-
for (const row of
|
|
108040
|
+
for (const row of read3.rows) {
|
|
107164
108041
|
if (typeof row.address !== "string")
|
|
107165
108042
|
continue;
|
|
107166
108043
|
const entry = derived.byAddress.get(lookupKey(row.address));
|
|
@@ -107171,7 +108048,7 @@ This profile acts as account ${read2.account}.
|
|
|
107171
108048
|
else
|
|
107172
108049
|
unmatched.push(row.address);
|
|
107173
108050
|
}
|
|
107174
|
-
return { matched, unmatched, externalListed, account:
|
|
108051
|
+
return { matched, unmatched, externalListed, account: read3.account, complete: true };
|
|
107175
108052
|
}
|
|
107176
108053
|
async function writeRestoredIndex(ctx, vault, derived, matches3, counts) {
|
|
107177
108054
|
const now = new Date(ctx.deps.now()).toISOString();
|
|
@@ -107414,17 +108291,17 @@ async function vaultReconcileExposure(args, ctx) {
|
|
|
107414
108291
|
suggestion: "Run: candle auth login"
|
|
107415
108292
|
});
|
|
107416
108293
|
}
|
|
107417
|
-
const
|
|
107418
|
-
if (!
|
|
107419
|
-
throw new VaultError("VAULT_UNREADABLE", `The linked-wallet read did not complete: ${
|
|
108294
|
+
const read3 = await completeLinkedWalletRead(ctx, apiKey);
|
|
108295
|
+
if (!read3.complete) {
|
|
108296
|
+
throw new VaultError("VAULT_UNREADABLE", `The linked-wallet read did not complete: ${read3.incompleteReason}. Nothing was flagged.`, {
|
|
107420
108297
|
suggestion: "A partial list that happened to list nothing would read as good news, so no exposure is recorded from an incomplete read."
|
|
107421
108298
|
});
|
|
107422
108299
|
}
|
|
107423
108300
|
const recorded = vault.index.hd.discovery?.account;
|
|
107424
|
-
if (recorded !== undefined && recorded !== "" && recorded !==
|
|
107425
|
-
throw new VaultError("EXPOSURE_ACCOUNT_MISMATCH", `This vault recorded its discovery against account ${recorded}, and this profile acts as ${
|
|
108301
|
+
if (recorded !== undefined && recorded !== "" && recorded !== read3.account) {
|
|
108302
|
+
throw new VaultError("EXPOSURE_ACCOUNT_MISMATCH", `This vault recorded its discovery against account ${recorded}, and this profile acts as ${read3.account}.`, { suggestion: "Switch profile and run it again. Nothing was flagged." });
|
|
107426
108303
|
}
|
|
107427
|
-
const listed = new Set(
|
|
108304
|
+
const listed = new Set(read3.rows.map((row) => lookupKey(row.address)));
|
|
107428
108305
|
const entries = vault.index.entries.map((entry) => entry.role !== "external" && listed.has(lookupKey(entry.address)) && !entry.exposure.everRemoteExposed ? { ...entry, exposure: { ...entry.exposure, everRemoteExposed: true } } : entry);
|
|
107429
108306
|
const added = entries.filter((entry, i) => entry !== vault.index.entries[i]);
|
|
107430
108307
|
const exposedIndexes = { ...vault.index.hd.exposedIndexes };
|
|
@@ -107464,8 +108341,8 @@ async function vaultReconcileExposure(args, ctx) {
|
|
|
107464
108341
|
if (ctx.json) {
|
|
107465
108342
|
writeJson(deps, {
|
|
107466
108343
|
ok: true,
|
|
107467
|
-
account:
|
|
107468
|
-
listed:
|
|
108344
|
+
account: read3.account,
|
|
108345
|
+
listed: read3.rows.length,
|
|
107469
108346
|
newlyFlagged: added.length,
|
|
107470
108347
|
cleared: 0,
|
|
107471
108348
|
exposureReconciledAt: now
|
|
@@ -107473,8 +108350,8 @@ async function vaultReconcileExposure(args, ctx) {
|
|
|
107473
108350
|
return 0;
|
|
107474
108351
|
}
|
|
107475
108352
|
writeFields(deps, "stdout", [
|
|
107476
|
-
["Account",
|
|
107477
|
-
["Linked wallets read",
|
|
108353
|
+
["Account", read3.account],
|
|
108354
|
+
["Linked wallets read", read3.rows.length],
|
|
107478
108355
|
["Newly flagged remotely exposed", added.length]
|
|
107479
108356
|
], "Exposure reconciled");
|
|
107480
108357
|
notice(deps, "stdout", "note", `Nothing was cleared. This command only ever adds exposure: no read can establish that an address is cold.
|
|
@@ -107725,25 +108602,25 @@ async function vaultStatus(args, ctx) {
|
|
|
107725
108602
|
}
|
|
107726
108603
|
const style = terminalStyle(deps, "stdout");
|
|
107727
108604
|
const miss = symbols(style).missing;
|
|
107728
|
-
const
|
|
108605
|
+
const write2 = (line = "") => deps.stdout.write(`${line}
|
|
107729
108606
|
`);
|
|
107730
|
-
|
|
107731
|
-
|
|
107732
|
-
|
|
107733
|
-
|
|
108607
|
+
write2(path);
|
|
108608
|
+
write2("");
|
|
108609
|
+
write2(heading("Vault", style));
|
|
108610
|
+
write2("");
|
|
107734
108611
|
for (const line of fields([
|
|
107735
108612
|
["Version", String(file.version)],
|
|
107736
108613
|
["Generation", String(file.generation)],
|
|
107737
108614
|
["Updated", formatTime(file.updatedAt)]
|
|
107738
108615
|
], style))
|
|
107739
|
-
|
|
107740
|
-
|
|
107741
|
-
|
|
107742
|
-
|
|
107743
|
-
|
|
108616
|
+
write2(line);
|
|
108617
|
+
write2("");
|
|
108618
|
+
write2(heading("Factors", style));
|
|
108619
|
+
write2("");
|
|
108620
|
+
write2(`${" ".repeat(2)}${envelopes.length} factors, ${recoverable} recoverable (domains counted once)`);
|
|
107744
108621
|
for (const envelope of envelopes) {
|
|
107745
|
-
|
|
107746
|
-
|
|
108622
|
+
write2("");
|
|
108623
|
+
write2(`${" ".repeat(2)}${envelope.id}`);
|
|
107747
108624
|
const rows = [
|
|
107748
108625
|
["Factor", envelope.factor],
|
|
107749
108626
|
["Domain", envelope.domain],
|
|
@@ -107757,30 +108634,30 @@ async function vaultStatus(args, ctx) {
|
|
|
107757
108634
|
if (envelope.sharedDomainNote)
|
|
107758
108635
|
rows.push(["Note", envelope.sharedDomainNote]);
|
|
107759
108636
|
for (const line of fields(rows, style, 4))
|
|
107760
|
-
|
|
108637
|
+
write2(line);
|
|
107761
108638
|
}
|
|
107762
108639
|
if (recoverable === 1) {
|
|
107763
108640
|
notice(deps, "stdout", "warning", "This vault has exactly one recoverable factor. Lose it and no copy of this file can be opened; the recovery phrase is then the only route, and it restores derived keys only.");
|
|
107764
108641
|
}
|
|
107765
|
-
|
|
107766
|
-
|
|
108642
|
+
write2("");
|
|
108643
|
+
write2(heading("This machine", style));
|
|
107767
108644
|
if (sidecar) {
|
|
107768
|
-
|
|
108645
|
+
write2("");
|
|
107769
108646
|
for (const line of fields([
|
|
107770
108647
|
["Record", "vault.state.json, cleartext, best effort"],
|
|
107771
108648
|
["Last generation seen", String(sidecar.lastGeneration)]
|
|
107772
108649
|
], style))
|
|
107773
|
-
|
|
108650
|
+
write2(line);
|
|
107774
108651
|
if (sidecar.lastVerifiedBackupAt && backup) {
|
|
107775
108652
|
for (const line of lastBackupLines(sidecar, backup))
|
|
107776
|
-
|
|
108653
|
+
write2(line);
|
|
107777
108654
|
}
|
|
107778
108655
|
if (sidecar.lastBackupSharedDomainAccepted) {
|
|
107779
|
-
|
|
108656
|
+
write2(" shared domain accepted for the last backup destination (an unsealed copy, every envelope included)");
|
|
107780
108657
|
}
|
|
107781
108658
|
if (sidecar.removedEnvelopeIds.length > 0) {
|
|
107782
|
-
|
|
107783
|
-
|
|
108659
|
+
write2(` removed envelopes ${sidecar.removedEnvelopeIds.join(", ")}`);
|
|
108660
|
+
write2(" any copy of this file made while one existed still opens with that factor.");
|
|
107784
108661
|
}
|
|
107785
108662
|
} else {
|
|
107786
108663
|
notice(deps, "stdout", "note", "No vault.state.json beside this vault, so an older copy of it cannot be recognized on this machine.");
|
|
@@ -107796,11 +108673,11 @@ A key it holds that was never imported opens with an earlier release's \`wallets
|
|
|
107796
108673
|
notice(deps, "stdout", "note", "Run with --unlock to list the keys, the derivation counters and the exposure flags, which are inside the encrypted index.");
|
|
107797
108674
|
return 0;
|
|
107798
108675
|
}
|
|
107799
|
-
|
|
107800
|
-
|
|
108676
|
+
write2("");
|
|
108677
|
+
write2(heading(`Keys (${unlocked.entries.length})`, style));
|
|
107801
108678
|
for (const entry of unlocked.entries) {
|
|
107802
|
-
|
|
107803
|
-
|
|
108679
|
+
write2("");
|
|
108680
|
+
write2(`${" ".repeat(2)}${entry.address}`);
|
|
107804
108681
|
const rows = [
|
|
107805
108682
|
["Label", entry.label || miss],
|
|
107806
108683
|
["Role", `${entry.role} (${entry.origin})`],
|
|
@@ -107814,24 +108691,24 @@ A key it holds that was never imported opens with an earlier release's \`wallets
|
|
|
107814
108691
|
rows.push(["Accepted", "an operator asserted this pinned destination's history despite exposureUnknown"]);
|
|
107815
108692
|
}
|
|
107816
108693
|
for (const line of fields(rows, style, 4))
|
|
107817
|
-
|
|
108694
|
+
write2(line);
|
|
107818
108695
|
}
|
|
107819
|
-
|
|
107820
|
-
|
|
108696
|
+
write2("");
|
|
108697
|
+
write2(LIST_POINTER);
|
|
107821
108698
|
if (unlocked.duplicateLabels.length > 0) {
|
|
107822
|
-
|
|
107823
|
-
|
|
108699
|
+
write2("");
|
|
108700
|
+
write2(heading(`Duplicate labels (${unlocked.duplicateLabels.length})`, style));
|
|
107824
108701
|
for (const line of duplicateLabelLines(unlocked.duplicateLabels))
|
|
107825
|
-
|
|
108702
|
+
write2(line);
|
|
107826
108703
|
}
|
|
107827
|
-
|
|
107828
|
-
|
|
107829
|
-
|
|
108704
|
+
write2("");
|
|
108705
|
+
write2(heading("Derivation counters (next index per branch)", style));
|
|
108706
|
+
write2("");
|
|
107830
108707
|
for (const [branch, value] of Object.entries(unlocked.nextIndex)) {
|
|
107831
108708
|
const exposed = unlocked.exposedIndexes[branch] ?? [];
|
|
107832
|
-
|
|
108709
|
+
write2(` ${branch.padEnd(12)} ${value}${exposed.length > 0 ? ` known exposed: ${exposed.join(", ")}` : ""}`);
|
|
107833
108710
|
}
|
|
107834
|
-
|
|
108711
|
+
write2(` recovery phrase exported: ${unlocked.rootExported ? "yes" : "no"}`);
|
|
107835
108712
|
if (unlocked.restored) {
|
|
107836
108713
|
notice(deps, "stdout", "note", "This vault was built by `vault restore --phrase`.\nIts keys are recovery artifacts, not fresh cold keys, and it does not allocate new addresses; `vault new-key` and `vault promote --from` refuse here.");
|
|
107837
108714
|
}
|
|
@@ -108074,7 +108951,7 @@ init_human_output();
|
|
|
108074
108951
|
init_release();
|
|
108075
108952
|
import { dirname as dirname11, join as join15 } from "node:path";
|
|
108076
108953
|
init_key_output();
|
|
108077
|
-
var
|
|
108954
|
+
var USAGE14 = "Usage: candle verify <file> --bundle <path> [--identity <uri>] [--issuer <url>]";
|
|
108078
108955
|
async function resolveIdentity(deps, bundlePath, flag) {
|
|
108079
108956
|
if (flag)
|
|
108080
108957
|
return { kind: "ok", uri: flag, provenance: "identity from --identity" };
|
|
@@ -108106,19 +108983,19 @@ async function verify(args, ctx) {
|
|
|
108106
108983
|
});
|
|
108107
108984
|
if ("error" in parsed) {
|
|
108108
108985
|
writeScreenUsageFailure(deps, `${parsed.error}
|
|
108109
|
-
${
|
|
108986
|
+
${USAGE14}`, json);
|
|
108110
108987
|
return 2;
|
|
108111
108988
|
}
|
|
108112
108989
|
const file = parsed.positionals[0];
|
|
108113
108990
|
if (parsed.positionals.length !== 1 || file === undefined) {
|
|
108114
108991
|
writeScreenUsageFailure(deps, `verify takes exactly one file.
|
|
108115
|
-
${
|
|
108992
|
+
${USAGE14}`, json);
|
|
108116
108993
|
return 2;
|
|
108117
108994
|
}
|
|
108118
108995
|
const bundlePath = parsed.values["--bundle"];
|
|
108119
108996
|
if (!bundlePath) {
|
|
108120
108997
|
writeScreenUsageFailure(deps, `--bundle is required.
|
|
108121
|
-
${
|
|
108998
|
+
${USAGE14}`, json);
|
|
108122
108999
|
return 2;
|
|
108123
109000
|
}
|
|
108124
109001
|
const resolved = await resolveIdentity(deps, bundlePath, parsed.values["--identity"]);
|
|
@@ -108132,10 +109009,10 @@ ${USAGE12}`, json);
|
|
|
108132
109009
|
}
|
|
108133
109010
|
if (resolved.kind === "absent") {
|
|
108134
109011
|
writeScreenUsageFailure(deps, `--identity is required: there is no latest.json beside ${bundlePath} to take the release version from.
|
|
108135
|
-
${
|
|
109012
|
+
${USAGE14}`, json);
|
|
108136
109013
|
return 2;
|
|
108137
109014
|
}
|
|
108138
|
-
const
|
|
109015
|
+
const identity4 = resolved.uri;
|
|
108139
109016
|
const issuer = parsed.values["--issuer"] ?? RELEASE_ISSUER;
|
|
108140
109017
|
let bytes;
|
|
108141
109018
|
let bundleJson;
|
|
@@ -108151,23 +109028,23 @@ ${USAGE12}`, json);
|
|
|
108151
109028
|
writeScreenLocalFailure(deps, { code: "BUNDLE_UNREADABLE", message: `Could not read the bundle ${bundlePath}: ${messageOf3(error)}` }, json);
|
|
108152
109029
|
return 1;
|
|
108153
109030
|
}
|
|
108154
|
-
const result = verifyReleaseAsset(bytes, bundleJson,
|
|
109031
|
+
const result = verifyReleaseAsset(bytes, bundleJson, identity4, issuer);
|
|
108155
109032
|
if (!result.ok) {
|
|
108156
109033
|
writeScreenLocalFailure(deps, {
|
|
108157
109034
|
code: "SIGNATURE_INVALID",
|
|
108158
109035
|
message: `Refusing ${file}: ${result.reason}.`,
|
|
108159
|
-
suggestion: `Checked against ${
|
|
109036
|
+
suggestion: `Checked against ${identity4} (${resolved.provenance}).`
|
|
108160
109037
|
}, json);
|
|
108161
109038
|
return 1;
|
|
108162
109039
|
}
|
|
108163
109040
|
if (json) {
|
|
108164
|
-
deps.stdout.write(`${JSON.stringify({ ok: true, file, identity:
|
|
109041
|
+
deps.stdout.write(`${JSON.stringify({ ok: true, file, identity: identity4, issuer, identitySource: resolved.provenance })}
|
|
108165
109042
|
`);
|
|
108166
109043
|
} else {
|
|
108167
109044
|
writeSuccess(deps, "stdout", "Release signature verified");
|
|
108168
109045
|
writeFields(deps, "stdout", [
|
|
108169
109046
|
["File", file],
|
|
108170
|
-
["Identity",
|
|
109047
|
+
["Identity", identity4],
|
|
108171
109048
|
["Source", resolved.provenance],
|
|
108172
109049
|
["Issuer", issuer]
|
|
108173
109050
|
]);
|
|
@@ -108380,8 +109257,8 @@ init_solana_alt();
|
|
|
108380
109257
|
init_solana_lite();
|
|
108381
109258
|
init_trading();
|
|
108382
109259
|
init_swap();
|
|
108383
|
-
import { randomUUID as
|
|
108384
|
-
var
|
|
109260
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
109261
|
+
var USAGE15 = "Usage: candle wallet close-empty [--wallet embedded|<tee address|label>] [--keep <mint>]... [--client-trade-id <id>] [--yes] [--json]";
|
|
108385
109262
|
var BASE58_ADDRESS2 = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
108386
109263
|
var LISTED_ROWS = 50;
|
|
108387
109264
|
var TOKEN_IX_CLOSE_ACCOUNT3 = 9;
|
|
@@ -108644,7 +109521,7 @@ async function closeTee(ctx, key, wallet3, name2, run2) {
|
|
|
108644
109521
|
async function walletsCloseEmpty(args, ctx) {
|
|
108645
109522
|
const lifted = splitKeep(args);
|
|
108646
109523
|
if ("error" in lifted) {
|
|
108647
|
-
writeUsageFailure(ctx.deps, `${lifted.error}. ${
|
|
109524
|
+
writeUsageFailure(ctx.deps, `${lifted.error}. ${USAGE15}`, ctx.json);
|
|
108648
109525
|
return 2;
|
|
108649
109526
|
}
|
|
108650
109527
|
const parsed = parseArgs(lifted.rest, { valueFlags: ["--wallet", "--client-trade-id"], booleanFlags: ["--yes"] });
|
|
@@ -108653,9 +109530,9 @@ async function walletsCloseEmpty(args, ctx) {
|
|
|
108653
109530
|
return 2;
|
|
108654
109531
|
}
|
|
108655
109532
|
const named = parsed.values["--client-trade-id"];
|
|
108656
|
-
const id = named ?? `close-${
|
|
109533
|
+
const id = named ?? `close-${randomUUID12()}`;
|
|
108657
109534
|
if (parsed.positionals.length !== 0 || !validClientId(id) || !lifted.keep.every((m) => BASE58_ADDRESS2.test(m))) {
|
|
108658
|
-
writeUsageFailure(ctx.deps,
|
|
109535
|
+
writeUsageFailure(ctx.deps, USAGE15, ctx.json);
|
|
108659
109536
|
return 2;
|
|
108660
109537
|
}
|
|
108661
109538
|
const run2 = { id, named: named !== undefined, keep: lifted.keep, yes: parsed.booleans.has("--yes") };
|
|
@@ -109063,9 +109940,15 @@ var COMMANDS = {
|
|
|
109063
109940
|
clusters: tokenClusters,
|
|
109064
109941
|
events: tokenEvents,
|
|
109065
109942
|
forensics: tokenForensics,
|
|
109066
|
-
"vamp-candidates": tokenVampCandidates
|
|
109943
|
+
"vamp-candidates": tokenVampCandidates,
|
|
109944
|
+
about: tokenAbout,
|
|
109945
|
+
claim: tokenClaim,
|
|
109946
|
+
section: tokenSection,
|
|
109947
|
+
history: tokenHistory,
|
|
109948
|
+
flag: tokenFlag
|
|
109067
109949
|
}
|
|
109068
109950
|
},
|
|
109951
|
+
scan: { bare: scan },
|
|
109069
109952
|
spend: {
|
|
109070
109953
|
bare: spendShow,
|
|
109071
109954
|
subcommands: {
|
|
@@ -109227,6 +110110,7 @@ var NEVER_GUARDED = new Set([
|
|
|
109227
110110
|
"verify",
|
|
109228
110111
|
"plans",
|
|
109229
110112
|
"token",
|
|
110113
|
+
"scan",
|
|
109230
110114
|
"spend",
|
|
109231
110115
|
"update",
|
|
109232
110116
|
"help",
|
|
@@ -109384,7 +110268,8 @@ async function runCommand(argv, deps) {
|
|
|
109384
110268
|
const publicPerpsRead = word3 === "perps" && ["markets", "market", "book", "candles"].includes(sub ?? "");
|
|
109385
110269
|
const coreFace = word3 === "core" && sub === "mcp";
|
|
109386
110270
|
const actsAsIdentity = !publicPerpsRead && !coreFace && (word3 !== "mcp" || mcpActsAsIdentity(tokens.slice(1)));
|
|
109387
|
-
|
|
110271
|
+
const tokenPageActs = word3 === "token" && (["claim", "section", "flag"].includes(sub ?? "") || sub === "about" && tokens.includes("--capabilities"));
|
|
110272
|
+
if (ROUTED_COMMANDS.has(word3) && (!NEVER_GUARDED.has(word3) || tokenPageActs) && routesToCommand(cmd, sub) && actsAsIdentity) {
|
|
109388
110273
|
const verdict = await verifyProfileAccount(ctx, config2);
|
|
109389
110274
|
if (!verdict.ok) {
|
|
109390
110275
|
writeLocalFailure(deps, { code: "ACCOUNT_MISMATCH", message: verdict.message, suggestion: verdict.suggestion }, flags2.json);
|