@toon-protocol/rig 2.3.0 → 2.4.1
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 +30 -8
- package/dist/{chunk-CW4HJNMU.js → chunk-XGFBDUQX.js} +37 -5
- package/dist/chunk-XGFBDUQX.js.map +1 -0
- package/dist/cli/rig.js +518 -34
- package/dist/cli/rig.js.map +1 -1
- package/dist/{standalone-mode-JLGAUMRF.js → standalone-mode-VH2XPOPK.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-CW4HJNMU.js.map +0 -1
- /package/dist/{standalone-mode-JLGAUMRF.js.map → standalone-mode-VH2XPOPK.js.map} +0 -0
package/dist/cli/rig.js
CHANGED
|
@@ -19,9 +19,14 @@ import {
|
|
|
19
19
|
writeGitObjects
|
|
20
20
|
} from "../chunk-W2SDL2PE.js";
|
|
21
21
|
import {
|
|
22
|
+
DEFAULT_KEYSTORE_PASSWORD,
|
|
22
23
|
MissingIdentityError,
|
|
24
|
+
clientConfigPath,
|
|
25
|
+
clientKeystorePath,
|
|
26
|
+
linkKeystoreInClientConfig,
|
|
27
|
+
readClientConfigFile,
|
|
23
28
|
resolveIdentity
|
|
24
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-XGFBDUQX.js";
|
|
25
30
|
import {
|
|
26
31
|
COMMENT_KIND,
|
|
27
32
|
authorizedStatusAuthors,
|
|
@@ -45,7 +50,7 @@ import {
|
|
|
45
50
|
} from "../chunk-X2CZPPDM.js";
|
|
46
51
|
|
|
47
52
|
// src/cli/rig.ts
|
|
48
|
-
import { createInterface } from "readline/promises";
|
|
53
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
49
54
|
|
|
50
55
|
// src/cli/dispatch.ts
|
|
51
56
|
import { createRequire } from "module";
|
|
@@ -900,7 +905,7 @@ function loadPaidSession(deps, relayUrl) {
|
|
|
900
905
|
});
|
|
901
906
|
}
|
|
902
907
|
var defaultLoadStandalone = async (options) => {
|
|
903
|
-
const mod = await import("../standalone-mode-
|
|
908
|
+
const mod = await import("../standalone-mode-VH2XPOPK.js");
|
|
904
909
|
return mod.createStandaloneContext(options);
|
|
905
910
|
};
|
|
906
911
|
function identityReport(ctx) {
|
|
@@ -3434,7 +3439,9 @@ pays). The identity comes from RIG_MNEMONIC (env or a project .env) or the
|
|
|
3434
3439
|
~/.toon-client keystore/config; the faucet from TOON_CLIENT_FAUCET_URL, the
|
|
3435
3440
|
faucetUrl config field, or the deployed devnet faucet when the network is
|
|
3436
3441
|
devnet \u2014 including when a configured *.devnet.toonprotocol.dev origin infers
|
|
3437
|
-
it
|
|
3442
|
+
it (a devnet relay/proxy/btp endpoint, or the git origin remote from
|
|
3443
|
+
\`rig remote add origin <devnet relay>\`). The faucet drips a FIXED amount per
|
|
3444
|
+
chain (there is no --amount). On a
|
|
3438
3445
|
network without a faucet, prints the wallet address(es) to fund externally
|
|
3439
3446
|
instead.
|
|
3440
3447
|
|
|
@@ -3445,24 +3452,50 @@ Options:
|
|
|
3445
3452
|
--json machine-readable envelope
|
|
3446
3453
|
-h, --help show this help`;
|
|
3447
3454
|
var SHARED_DEVNET_SUFFIX = ".devnet.toonprotocol.dev";
|
|
3448
|
-
function
|
|
3455
|
+
function devnetHost(url) {
|
|
3456
|
+
if (!url) return void 0;
|
|
3457
|
+
try {
|
|
3458
|
+
const { hostname } = new URL(url);
|
|
3459
|
+
if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1)) {
|
|
3460
|
+
return url;
|
|
3461
|
+
}
|
|
3462
|
+
} catch {
|
|
3463
|
+
}
|
|
3464
|
+
return void 0;
|
|
3465
|
+
}
|
|
3466
|
+
function sharedDevnetOrigin(env, file, originRelays = []) {
|
|
3449
3467
|
const candidates = [
|
|
3450
3468
|
env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl,
|
|
3451
3469
|
env["TOON_CLIENT_PROXY_URL"] ?? file.proxyUrl,
|
|
3452
|
-
env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl
|
|
3470
|
+
env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl,
|
|
3471
|
+
...originRelays
|
|
3453
3472
|
];
|
|
3454
3473
|
for (const url of candidates) {
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
const { hostname } = new URL(url);
|
|
3458
|
-
if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1)) {
|
|
3459
|
-
return url;
|
|
3460
|
-
}
|
|
3461
|
-
} catch {
|
|
3462
|
-
}
|
|
3474
|
+
const hit = devnetHost(url);
|
|
3475
|
+
if (hit) return hit;
|
|
3463
3476
|
}
|
|
3464
3477
|
return void 0;
|
|
3465
3478
|
}
|
|
3479
|
+
async function resolveOriginRelays(cwd) {
|
|
3480
|
+
let repoRoot;
|
|
3481
|
+
try {
|
|
3482
|
+
repoRoot = await resolveRepoRoot(cwd);
|
|
3483
|
+
} catch {
|
|
3484
|
+
return [];
|
|
3485
|
+
}
|
|
3486
|
+
try {
|
|
3487
|
+
const toonConfig = await readToonConfig(repoRoot);
|
|
3488
|
+
const resolved = await resolveRelays({
|
|
3489
|
+
relayFlags: [],
|
|
3490
|
+
remoteName: void 0,
|
|
3491
|
+
repoRoot,
|
|
3492
|
+
toonRelays: toonConfig.relays
|
|
3493
|
+
});
|
|
3494
|
+
return resolved.relays;
|
|
3495
|
+
} catch {
|
|
3496
|
+
return [];
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3466
3499
|
function noFaucetGuidance(network, devnetOrigin) {
|
|
3467
3500
|
const head = `no faucet is configured for network ${JSON.stringify(network ?? "custom")}`;
|
|
3468
3501
|
const external = "To fund the wallet externally instead, send the settlement token plus native gas to the address below for the chain your channels settle on.";
|
|
@@ -3529,7 +3562,9 @@ async function runFund(args, deps) {
|
|
|
3529
3562
|
);
|
|
3530
3563
|
}
|
|
3531
3564
|
const network = env["TOON_CLIENT_NETWORK"] ?? file.network;
|
|
3532
|
-
const
|
|
3565
|
+
const canInfer = network === void 0 || network === "custom";
|
|
3566
|
+
const originRelays = canInfer ? await resolveOriginRelays(deps.cwd) : [];
|
|
3567
|
+
const devnetOrigin = sharedDevnetOrigin(env, file, originRelays);
|
|
3533
3568
|
const inferredDevnet = devnetOrigin !== void 0 && (network === void 0 || network === "custom");
|
|
3534
3569
|
const effectiveNetwork = inferredDevnet ? "devnet" : network;
|
|
3535
3570
|
const faucetUrl = env["TOON_CLIENT_FAUCET_URL"] ?? file.faucetUrl ?? (effectiveNetwork === "devnet" ? DEVNET_FAUCET_URL : void 0);
|
|
@@ -3668,9 +3703,396 @@ var runGitPassthrough = (argv2, options = {}) => {
|
|
|
3668
3703
|
});
|
|
3669
3704
|
};
|
|
3670
3705
|
|
|
3706
|
+
// src/cli/identity-cmd.ts
|
|
3707
|
+
import { existsSync, mkdirSync } from "fs";
|
|
3708
|
+
import { dirname as dirname2 } from "path";
|
|
3709
|
+
import { createInterface } from "readline/promises";
|
|
3710
|
+
import { parseArgs as parseArgs10 } from "util";
|
|
3711
|
+
async function loadKeyOps() {
|
|
3712
|
+
return await import("@toon-protocol/client");
|
|
3713
|
+
}
|
|
3714
|
+
var IdentityExistsError = class extends Error {
|
|
3715
|
+
constructor(pubkey, sourceLabel) {
|
|
3716
|
+
super(
|
|
3717
|
+
`an identity already resolves (${pubkey}, from ${sourceLabel}) \u2014 rig will not overwrite it. Inspect it with \`rig identity show\`, or pass \`--force\` to replace the keystore with a brand-new identity (the old phrase becomes unrecoverable unless you backed it up).`
|
|
3718
|
+
);
|
|
3719
|
+
this.pubkey = pubkey;
|
|
3720
|
+
this.sourceLabel = sourceLabel;
|
|
3721
|
+
this.name = "IdentityExistsError";
|
|
3722
|
+
}
|
|
3723
|
+
pubkey;
|
|
3724
|
+
sourceLabel;
|
|
3725
|
+
};
|
|
3726
|
+
var KeystoreFileExistsError = class extends Error {
|
|
3727
|
+
constructor(keystorePath) {
|
|
3728
|
+
super(
|
|
3729
|
+
`a keystore file already exists at ${keystorePath} \u2014 rig will not overwrite it. Link it via the client config, or pass \`--force\` to replace it with a new identity.`
|
|
3730
|
+
);
|
|
3731
|
+
this.keystorePath = keystorePath;
|
|
3732
|
+
this.name = "KeystoreFileExistsError";
|
|
3733
|
+
}
|
|
3734
|
+
keystorePath;
|
|
3735
|
+
};
|
|
3736
|
+
async function prepareKeystoreTarget(input) {
|
|
3737
|
+
const keyOps = await (input.loadKeyOps ?? loadKeyOps)();
|
|
3738
|
+
const resolveImpl = input.resolveIdentityImpl ?? resolveIdentity;
|
|
3739
|
+
let existing;
|
|
3740
|
+
try {
|
|
3741
|
+
existing = await resolveImpl({
|
|
3742
|
+
env: input.env,
|
|
3743
|
+
cwd: input.cwd,
|
|
3744
|
+
warn: input.warn
|
|
3745
|
+
});
|
|
3746
|
+
} catch (err) {
|
|
3747
|
+
if (!(err instanceof MissingIdentityError)) throw err;
|
|
3748
|
+
}
|
|
3749
|
+
if (existing && !input.force) {
|
|
3750
|
+
throw new IdentityExistsError(existing.pubkey, existing.sourceLabel);
|
|
3751
|
+
}
|
|
3752
|
+
const keystorePath = clientKeystorePath(input.env);
|
|
3753
|
+
if (existsSync(keystorePath) && !input.force) {
|
|
3754
|
+
throw new KeystoreFileExistsError(keystorePath);
|
|
3755
|
+
}
|
|
3756
|
+
const envPassword = input.env["TOON_CLIENT_KEYSTORE_PASSWORD"]?.trim() || void 0;
|
|
3757
|
+
const autoPassword = envPassword === void 0;
|
|
3758
|
+
const password = envPassword ?? DEFAULT_KEYSTORE_PASSWORD;
|
|
3759
|
+
mkdirSync(dirname2(keystorePath), { recursive: true });
|
|
3760
|
+
return { keystorePath, password, autoPassword, existing, keyOps };
|
|
3761
|
+
}
|
|
3762
|
+
function readAccountIndex(env) {
|
|
3763
|
+
const file = readClientConfigFile(clientConfigPath(env));
|
|
3764
|
+
return typeof file.mnemonicAccountIndex === "number" ? file.mnemonicAccountIndex : 0;
|
|
3765
|
+
}
|
|
3766
|
+
function finishMint(input, target, mnemonic) {
|
|
3767
|
+
const configPath = linkKeystoreInClientConfig(
|
|
3768
|
+
input.env,
|
|
3769
|
+
target.keystorePath,
|
|
3770
|
+
target.autoPassword
|
|
3771
|
+
);
|
|
3772
|
+
const accountIndex = readAccountIndex(input.env);
|
|
3773
|
+
const { pubkey } = target.keyOps.deriveNostrKeyFromMnemonic(
|
|
3774
|
+
mnemonic,
|
|
3775
|
+
accountIndex
|
|
3776
|
+
);
|
|
3777
|
+
const result = {
|
|
3778
|
+
mnemonic,
|
|
3779
|
+
pubkey,
|
|
3780
|
+
keystorePath: target.keystorePath,
|
|
3781
|
+
configPath,
|
|
3782
|
+
autoPassword: target.autoPassword,
|
|
3783
|
+
accountIndex
|
|
3784
|
+
};
|
|
3785
|
+
if (target.existing && target.existing.pubkey !== pubkey) {
|
|
3786
|
+
result.shadowedBy = {
|
|
3787
|
+
sourceLabel: target.existing.sourceLabel,
|
|
3788
|
+
pubkey: target.existing.pubkey
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
return result;
|
|
3792
|
+
}
|
|
3793
|
+
async function createIdentity(input) {
|
|
3794
|
+
const target = await prepareKeystoreTarget(input);
|
|
3795
|
+
const { mnemonic } = target.keyOps.generateKeystore(
|
|
3796
|
+
target.keystorePath,
|
|
3797
|
+
target.password
|
|
3798
|
+
);
|
|
3799
|
+
return finishMint(input, target, mnemonic);
|
|
3800
|
+
}
|
|
3801
|
+
async function importIdentity(input) {
|
|
3802
|
+
const target = await prepareKeystoreTarget(input);
|
|
3803
|
+
target.keyOps.importKeystore(
|
|
3804
|
+
target.keystorePath,
|
|
3805
|
+
input.mnemonic,
|
|
3806
|
+
target.password
|
|
3807
|
+
);
|
|
3808
|
+
return finishMint(input, target, input.mnemonic);
|
|
3809
|
+
}
|
|
3810
|
+
function createdIdentityBanner(result) {
|
|
3811
|
+
return [
|
|
3812
|
+
"",
|
|
3813
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
3814
|
+
" rig: generated a new identity",
|
|
3815
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
3816
|
+
` Nostr pubkey : ${result.pubkey}`,
|
|
3817
|
+
"",
|
|
3818
|
+
" Seed phrase \u2014 this phrase CONTROLS YOUR FUNDS. Write it down and",
|
|
3819
|
+
" store it safely. It is shown ONCE and CANNOT be recovered:",
|
|
3820
|
+
"",
|
|
3821
|
+
` ${result.mnemonic}`,
|
|
3822
|
+
"",
|
|
3823
|
+
` Encrypted keystore: ${result.keystorePath}`,
|
|
3824
|
+
result.autoPassword ? " Encrypted with the default password, so the identity reloads with no\n env var. To use your own password: set TOON_CLIENT_KEYSTORE_PASSWORD\n and re-import with `rig identity import`." : " Encrypted with your TOON_CLIENT_KEYSTORE_PASSWORD.",
|
|
3825
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
3826
|
+
""
|
|
3827
|
+
];
|
|
3828
|
+
}
|
|
3829
|
+
function shadowNote(shadowedBy) {
|
|
3830
|
+
return `note: a higher-precedence identity (${shadowedBy.pubkey}, from ${shadowedBy.sourceLabel}) still shadows the new keystore \u2014 rig keeps using it until you remove that source (unset the env var / edit .env).`;
|
|
3831
|
+
}
|
|
3832
|
+
var IDENTITY_USAGE = `Usage: rig identity <command> [options]
|
|
3833
|
+
|
|
3834
|
+
Manage the BIP-39 signing identity rig uses to sign and pay:
|
|
3835
|
+
|
|
3836
|
+
create generate a fresh identity into the encrypted keystore under
|
|
3837
|
+
TOON_CLIENT_HOME. The seed phrase is shown ONCE \u2014 back it up.
|
|
3838
|
+
Refuses to overwrite an existing identity without --force.
|
|
3839
|
+
show report the ACTIVE identity's source + derived pubkey (never the
|
|
3840
|
+
phrase); errors with the resolution remediation when none exists.
|
|
3841
|
+
import read an EXISTING phrase from stdin (never a CLI argument) and write
|
|
3842
|
+
it to the encrypted keystore.
|
|
3843
|
+
|
|
3844
|
+
Options:
|
|
3845
|
+
--force (create/import) replace an existing identity/keystore
|
|
3846
|
+
--json machine-readable output. NOTE: \`identity create --json\`
|
|
3847
|
+
DELIBERATELY emits the seed phrase in a \`mnemonic\`
|
|
3848
|
+
field \u2014 treat that output as SECRET. \`identity show\`
|
|
3849
|
+
and \`identity import\` never emit the phrase.
|
|
3850
|
+
-h, --help show this help
|
|
3851
|
+
|
|
3852
|
+
The keystore is encrypted with TOON_CLIENT_KEYSTORE_PASSWORD when that env var
|
|
3853
|
+
is set, else a default password so the identity reloads with no configuration.
|
|
3854
|
+
rig deliberately does NOT accept the keystore password as a CLI flag \u2014 that
|
|
3855
|
+
would leak the value that decrypts your keys to shell history and \`ps\`.`;
|
|
3856
|
+
async function runIdentity(args, deps) {
|
|
3857
|
+
const [sub, ...rest] = args;
|
|
3858
|
+
switch (sub) {
|
|
3859
|
+
case "create":
|
|
3860
|
+
return runIdentityCreate(rest, deps);
|
|
3861
|
+
case "show":
|
|
3862
|
+
return runIdentityShow(rest, deps);
|
|
3863
|
+
case "import":
|
|
3864
|
+
return runIdentityImport(rest, deps);
|
|
3865
|
+
case "help":
|
|
3866
|
+
case "--help":
|
|
3867
|
+
case "-h":
|
|
3868
|
+
deps.io.out(IDENTITY_USAGE);
|
|
3869
|
+
return 0;
|
|
3870
|
+
case void 0:
|
|
3871
|
+
deps.io.err("rig identity needs a subcommand: create | show | import");
|
|
3872
|
+
deps.io.err(IDENTITY_USAGE);
|
|
3873
|
+
return 2;
|
|
3874
|
+
default:
|
|
3875
|
+
deps.io.err(`unknown rig identity subcommand: ${sub}`);
|
|
3876
|
+
deps.io.err(IDENTITY_USAGE);
|
|
3877
|
+
return 2;
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
function parseMintArgs(args, verb) {
|
|
3881
|
+
const { values, positionals } = parseArgs10({
|
|
3882
|
+
args,
|
|
3883
|
+
options: {
|
|
3884
|
+
force: { type: "boolean", default: false },
|
|
3885
|
+
json: { type: "boolean", default: false },
|
|
3886
|
+
help: { type: "boolean", short: "h", default: false }
|
|
3887
|
+
},
|
|
3888
|
+
allowPositionals: false
|
|
3889
|
+
});
|
|
3890
|
+
if (positionals.length > 0) {
|
|
3891
|
+
throw new Error(`rig identity ${verb} takes no positional arguments`);
|
|
3892
|
+
}
|
|
3893
|
+
return {
|
|
3894
|
+
force: values.force ?? false,
|
|
3895
|
+
json: values.json ?? false,
|
|
3896
|
+
help: values.help ?? false
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
async function runIdentityCreate(args, deps) {
|
|
3900
|
+
const { io: io2 } = deps;
|
|
3901
|
+
let flags;
|
|
3902
|
+
try {
|
|
3903
|
+
flags = parseMintArgs(args, "create");
|
|
3904
|
+
} catch (err) {
|
|
3905
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3906
|
+
io2.err(IDENTITY_USAGE);
|
|
3907
|
+
return 2;
|
|
3908
|
+
}
|
|
3909
|
+
if (flags.help) {
|
|
3910
|
+
io2.out(IDENTITY_USAGE);
|
|
3911
|
+
return 0;
|
|
3912
|
+
}
|
|
3913
|
+
try {
|
|
3914
|
+
const result = await createIdentity({
|
|
3915
|
+
env: deps.env,
|
|
3916
|
+
cwd: deps.cwd,
|
|
3917
|
+
force: flags.force,
|
|
3918
|
+
warn: (line) => io2.err(line),
|
|
3919
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {},
|
|
3920
|
+
...deps.loadKeyOps ? { loadKeyOps: deps.loadKeyOps } : {}
|
|
3921
|
+
});
|
|
3922
|
+
emitCreated(io2, flags.json, "identity create", result);
|
|
3923
|
+
return 0;
|
|
3924
|
+
} catch (err) {
|
|
3925
|
+
return emitMintError(io2, flags.json, "identity create", err);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
async function runIdentityImport(args, deps) {
|
|
3929
|
+
const { io: io2 } = deps;
|
|
3930
|
+
let flags;
|
|
3931
|
+
try {
|
|
3932
|
+
flags = parseMintArgs(args, "import");
|
|
3933
|
+
} catch (err) {
|
|
3934
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3935
|
+
io2.err(IDENTITY_USAGE);
|
|
3936
|
+
return 2;
|
|
3937
|
+
}
|
|
3938
|
+
if (flags.help) {
|
|
3939
|
+
io2.out(IDENTITY_USAGE);
|
|
3940
|
+
return 0;
|
|
3941
|
+
}
|
|
3942
|
+
try {
|
|
3943
|
+
const read = deps.readSecretLine ?? defaultReadSecretLine;
|
|
3944
|
+
const phrase = (await read(
|
|
3945
|
+
"Paste your BIP-39 seed phrase (read from stdin, not the terminal history): ",
|
|
3946
|
+
io2.isInteractive
|
|
3947
|
+
)).trim();
|
|
3948
|
+
if (phrase === "") {
|
|
3949
|
+
throw new Error(
|
|
3950
|
+
"no seed phrase provided on stdin \u2014 pipe it in or type it at the prompt (never pass a phrase as a CLI argument; it leaks to shell history and `ps`)."
|
|
3951
|
+
);
|
|
3952
|
+
}
|
|
3953
|
+
const result = await importIdentity({
|
|
3954
|
+
env: deps.env,
|
|
3955
|
+
cwd: deps.cwd,
|
|
3956
|
+
force: flags.force,
|
|
3957
|
+
mnemonic: phrase,
|
|
3958
|
+
warn: (line) => io2.err(line),
|
|
3959
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {},
|
|
3960
|
+
...deps.loadKeyOps ? { loadKeyOps: deps.loadKeyOps } : {}
|
|
3961
|
+
});
|
|
3962
|
+
if (flags.json) {
|
|
3963
|
+
io2.emitJson({
|
|
3964
|
+
command: "identity import",
|
|
3965
|
+
imported: true,
|
|
3966
|
+
pubkey: result.pubkey,
|
|
3967
|
+
keystorePath: result.keystorePath,
|
|
3968
|
+
autoPassword: result.autoPassword,
|
|
3969
|
+
...result.shadowedBy ? { shadowedBy: result.shadowedBy } : {}
|
|
3970
|
+
});
|
|
3971
|
+
} else {
|
|
3972
|
+
io2.out(`Imported identity ${result.pubkey}`);
|
|
3973
|
+
io2.out(` Encrypted keystore: ${result.keystorePath}`);
|
|
3974
|
+
}
|
|
3975
|
+
if (result.shadowedBy) io2.err(shadowNote(result.shadowedBy));
|
|
3976
|
+
return 0;
|
|
3977
|
+
} catch (err) {
|
|
3978
|
+
return emitMintError(io2, flags.json, "identity import", err);
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
async function runIdentityShow(args, deps) {
|
|
3982
|
+
const { io: io2 } = deps;
|
|
3983
|
+
let json = false;
|
|
3984
|
+
let help = false;
|
|
3985
|
+
try {
|
|
3986
|
+
const parsed = parseArgs10({
|
|
3987
|
+
args,
|
|
3988
|
+
options: {
|
|
3989
|
+
json: { type: "boolean", default: false },
|
|
3990
|
+
help: { type: "boolean", short: "h", default: false }
|
|
3991
|
+
},
|
|
3992
|
+
allowPositionals: false
|
|
3993
|
+
});
|
|
3994
|
+
json = parsed.values.json ?? false;
|
|
3995
|
+
help = parsed.values.help ?? false;
|
|
3996
|
+
} catch (err) {
|
|
3997
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3998
|
+
io2.err(IDENTITY_USAGE);
|
|
3999
|
+
return 2;
|
|
4000
|
+
}
|
|
4001
|
+
if (help) {
|
|
4002
|
+
io2.out(IDENTITY_USAGE);
|
|
4003
|
+
return 0;
|
|
4004
|
+
}
|
|
4005
|
+
try {
|
|
4006
|
+
const identity = await (deps.resolveIdentityImpl ?? resolveIdentity)({
|
|
4007
|
+
env: deps.env,
|
|
4008
|
+
cwd: deps.cwd,
|
|
4009
|
+
warn: (line) => io2.err(line)
|
|
4010
|
+
});
|
|
4011
|
+
if (json) {
|
|
4012
|
+
io2.emitJson({
|
|
4013
|
+
command: "identity show",
|
|
4014
|
+
source: identity.source,
|
|
4015
|
+
sourceLabel: identity.sourceLabel,
|
|
4016
|
+
pubkey: identity.pubkey,
|
|
4017
|
+
accountIndex: identity.accountIndex
|
|
4018
|
+
});
|
|
4019
|
+
} else {
|
|
4020
|
+
io2.out(renderIdentityLine(identity));
|
|
4021
|
+
io2.out(` source : ${identity.source} (${identity.sourceLabel})`);
|
|
4022
|
+
io2.out(` pubkey : ${identity.pubkey}`);
|
|
4023
|
+
}
|
|
4024
|
+
return 0;
|
|
4025
|
+
} catch (err) {
|
|
4026
|
+
return emitCliError(io2, json, "identity show", err);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
function emitCreated(io2, json, command, result) {
|
|
4030
|
+
if (json) {
|
|
4031
|
+
io2.emitJson({
|
|
4032
|
+
command,
|
|
4033
|
+
created: true,
|
|
4034
|
+
pubkey: result.pubkey,
|
|
4035
|
+
keystorePath: result.keystorePath,
|
|
4036
|
+
autoPassword: result.autoPassword,
|
|
4037
|
+
// DELIBERATE, documented exception to "never print the phrase": the
|
|
4038
|
+
// scripting/agent path needs the generated phrase to back it up.
|
|
4039
|
+
mnemonic: result.mnemonic,
|
|
4040
|
+
...result.shadowedBy ? { shadowedBy: result.shadowedBy } : {}
|
|
4041
|
+
});
|
|
4042
|
+
io2.err(
|
|
4043
|
+
"rig: `--json` emitted your seed phrase in the `mnemonic` field \u2014 this output is SECRET; store it safely and do not log or share it."
|
|
4044
|
+
);
|
|
4045
|
+
} else {
|
|
4046
|
+
for (const line of createdIdentityBanner(result)) io2.out(line);
|
|
4047
|
+
}
|
|
4048
|
+
if (result.shadowedBy) io2.err(shadowNote(result.shadowedBy));
|
|
4049
|
+
}
|
|
4050
|
+
function emitMintError(io2, json, command, err) {
|
|
4051
|
+
if (err instanceof IdentityExistsError) {
|
|
4052
|
+
if (json) {
|
|
4053
|
+
io2.emitJson({
|
|
4054
|
+
command,
|
|
4055
|
+
error: "identity_exists",
|
|
4056
|
+
pubkey: err.pubkey,
|
|
4057
|
+
detail: err.message
|
|
4058
|
+
});
|
|
4059
|
+
}
|
|
4060
|
+
for (const line of err.message.split("\n")) io2.err(line);
|
|
4061
|
+
return 1;
|
|
4062
|
+
}
|
|
4063
|
+
if (err instanceof KeystoreFileExistsError) {
|
|
4064
|
+
if (json) {
|
|
4065
|
+
io2.emitJson({
|
|
4066
|
+
command,
|
|
4067
|
+
error: "keystore_exists",
|
|
4068
|
+
keystorePath: err.keystorePath,
|
|
4069
|
+
detail: err.message
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
for (const line of err.message.split("\n")) io2.err(line);
|
|
4073
|
+
return 1;
|
|
4074
|
+
}
|
|
4075
|
+
return emitCliError(io2, json, command, err);
|
|
4076
|
+
}
|
|
4077
|
+
async function defaultReadSecretLine(prompt, isInteractive) {
|
|
4078
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
4079
|
+
if (isInteractive) {
|
|
4080
|
+
process.stderr.write(prompt);
|
|
4081
|
+
rl._writeToOutput = () => {
|
|
4082
|
+
};
|
|
4083
|
+
}
|
|
4084
|
+
try {
|
|
4085
|
+
const answer = await rl.question("");
|
|
4086
|
+
if (isInteractive) process.stderr.write("\n");
|
|
4087
|
+
return answer;
|
|
4088
|
+
} finally {
|
|
4089
|
+
rl.close();
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
|
|
3671
4093
|
// src/cli/init.ts
|
|
3672
4094
|
import { basename as basename2 } from "path";
|
|
3673
|
-
import { parseArgs as
|
|
4095
|
+
import { parseArgs as parseArgs11 } from "util";
|
|
3674
4096
|
var INIT_USAGE = `Usage: rig init [options]
|
|
3675
4097
|
|
|
3676
4098
|
Set up the current git repository for rig (one-shot, idempotent, free):
|
|
@@ -3684,16 +4106,23 @@ The follow-up step is adding a relay: \`rig remote add origin <relay-url>\`
|
|
|
3684
4106
|
\`git config toon.relay\` is migrated to the origin remote automatically
|
|
3685
4107
|
when no origin exists (the old key stays readable and is removed in v0.3).
|
|
3686
4108
|
|
|
4109
|
+
When NO identity resolves, init does not dead-end: in a terminal it offers to
|
|
4110
|
+
generate one (\`Create a new identity now? [y/N]\`), and \`--generate-identity\`
|
|
4111
|
+
does it non-interactively (equivalent to \`rig identity create\` \u2014 the phrase
|
|
4112
|
+
is shown once to back up). Nothing is ever auto-generated without your yes.
|
|
4113
|
+
|
|
3687
4114
|
Options:
|
|
3688
|
-
--repo-id <id>
|
|
3689
|
-
|
|
3690
|
-
--
|
|
3691
|
-
-
|
|
4115
|
+
--repo-id <id> repository id / NIP-34 d-tag (default: the existing
|
|
4116
|
+
toon.repoid, then the repo directory basename)
|
|
4117
|
+
--generate-identity when no identity resolves, mint a fresh one (no prompt)
|
|
4118
|
+
--json machine-readable report
|
|
4119
|
+
-h, --help show this help`;
|
|
3692
4120
|
function parseInitArgs(args) {
|
|
3693
|
-
const { values, positionals } =
|
|
4121
|
+
const { values, positionals } = parseArgs11({
|
|
3694
4122
|
args,
|
|
3695
4123
|
options: {
|
|
3696
4124
|
"repo-id": { type: "string" },
|
|
4125
|
+
"generate-identity": { type: "boolean", default: false },
|
|
3697
4126
|
json: { type: "boolean", default: false },
|
|
3698
4127
|
help: { type: "boolean", short: "h", default: false }
|
|
3699
4128
|
},
|
|
@@ -3703,6 +4132,7 @@ function parseInitArgs(args) {
|
|
|
3703
4132
|
throw new Error(`rig init takes no positional arguments`);
|
|
3704
4133
|
}
|
|
3705
4134
|
const flags = {
|
|
4135
|
+
generateIdentity: values["generate-identity"] ?? false,
|
|
3706
4136
|
json: values.json ?? false,
|
|
3707
4137
|
help: values.help ?? false
|
|
3708
4138
|
};
|
|
@@ -3713,8 +4143,38 @@ function parseInitArgs(args) {
|
|
|
3713
4143
|
}
|
|
3714
4144
|
return flags;
|
|
3715
4145
|
}
|
|
3716
|
-
async function
|
|
4146
|
+
async function resolveOrGenerateIdentity(deps, flags) {
|
|
3717
4147
|
const { io: io2, env } = deps;
|
|
4148
|
+
const resolve2 = deps.resolveIdentityImpl ?? resolveIdentity;
|
|
4149
|
+
try {
|
|
4150
|
+
return { identity: await resolve2({ env, cwd: deps.cwd, warn: io2.err }) };
|
|
4151
|
+
} catch (err) {
|
|
4152
|
+
if (!(err instanceof MissingIdentityError)) throw err;
|
|
4153
|
+
let generate = flags.generateIdentity;
|
|
4154
|
+
if (!generate && io2.isInteractive && !flags.json) {
|
|
4155
|
+
io2.err("No identity found \u2014 rig needs one to sign and pay.");
|
|
4156
|
+
generate = await io2.confirm("Create a new identity now? [y/N] ");
|
|
4157
|
+
}
|
|
4158
|
+
if (!generate) throw err;
|
|
4159
|
+
const created = await (deps.createIdentityImpl ?? createIdentity)({
|
|
4160
|
+
env,
|
|
4161
|
+
cwd: deps.cwd,
|
|
4162
|
+
force: false,
|
|
4163
|
+
warn: io2.err,
|
|
4164
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {}
|
|
4165
|
+
});
|
|
4166
|
+
const identity = {
|
|
4167
|
+
mnemonic: created.mnemonic,
|
|
4168
|
+
accountIndex: created.accountIndex,
|
|
4169
|
+
source: "keystore",
|
|
4170
|
+
sourceLabel: created.keystorePath,
|
|
4171
|
+
pubkey: created.pubkey
|
|
4172
|
+
};
|
|
4173
|
+
return { identity, generated: created };
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
async function runInit(args, deps) {
|
|
4177
|
+
const { io: io2 } = deps;
|
|
3718
4178
|
let flags;
|
|
3719
4179
|
try {
|
|
3720
4180
|
flags = parseInitArgs(args);
|
|
@@ -3734,11 +4194,7 @@ async function runInit(args, deps) {
|
|
|
3734
4194
|
} catch {
|
|
3735
4195
|
throw new NotAGitRepositoryError(deps.cwd);
|
|
3736
4196
|
}
|
|
3737
|
-
const identity = await (deps
|
|
3738
|
-
env,
|
|
3739
|
-
cwd: deps.cwd,
|
|
3740
|
-
warn: (line) => io2.err(line)
|
|
3741
|
-
});
|
|
4197
|
+
const { identity, generated } = await resolveOrGenerateIdentity(deps, flags);
|
|
3742
4198
|
const existing = await readToonConfig(repoRoot);
|
|
3743
4199
|
const repoId = flags.repoId ?? existing.repoId ?? basename2(repoRoot);
|
|
3744
4200
|
const changed = {
|
|
@@ -3772,11 +4228,29 @@ async function runInit(args, deps) {
|
|
|
3772
4228
|
remotes,
|
|
3773
4229
|
origin: originRelay ?? null,
|
|
3774
4230
|
migratedToonRelay,
|
|
3775
|
-
changed
|
|
4231
|
+
changed,
|
|
4232
|
+
...generated ? {
|
|
4233
|
+
generatedIdentity: {
|
|
4234
|
+
mnemonic: generated.mnemonic,
|
|
4235
|
+
pubkey: generated.pubkey,
|
|
4236
|
+
keystorePath: generated.keystorePath,
|
|
4237
|
+
autoPassword: generated.autoPassword
|
|
4238
|
+
}
|
|
4239
|
+
} : {}
|
|
3776
4240
|
};
|
|
3777
4241
|
io2.emitJson(output);
|
|
4242
|
+
if (generated) {
|
|
4243
|
+
io2.err(
|
|
4244
|
+
"rig: `--json` emitted your new seed phrase in `generatedIdentity.mnemonic` \u2014 this output is SECRET; store it safely and do not log or share it."
|
|
4245
|
+
);
|
|
4246
|
+
if (generated.shadowedBy) io2.err(shadowNote(generated.shadowedBy));
|
|
4247
|
+
}
|
|
3778
4248
|
return 0;
|
|
3779
4249
|
}
|
|
4250
|
+
if (generated) {
|
|
4251
|
+
for (const line of createdIdentityBanner(generated)) io2.out(line);
|
|
4252
|
+
if (generated.shadowedBy) io2.err(shadowNote(generated.shadowedBy));
|
|
4253
|
+
}
|
|
3780
4254
|
io2.out(`Initialized rig for ${repoRoot}`);
|
|
3781
4255
|
io2.out(renderIdentityLine(identity));
|
|
3782
4256
|
io2.out(
|
|
@@ -3816,7 +4290,7 @@ async function runInit(args, deps) {
|
|
|
3816
4290
|
}
|
|
3817
4291
|
|
|
3818
4292
|
// src/cli/maintainers.ts
|
|
3819
|
-
import { parseArgs as
|
|
4293
|
+
import { parseArgs as parseArgs12 } from "util";
|
|
3820
4294
|
var HEX64_RE3 = /^[0-9a-f]{64}$/;
|
|
3821
4295
|
var WS_URL_RE4 = /^wss?:\/\//i;
|
|
3822
4296
|
var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
|
|
@@ -3921,7 +4395,7 @@ async function runList2(args, deps) {
|
|
|
3921
4395
|
const { io: io2 } = deps;
|
|
3922
4396
|
let flags;
|
|
3923
4397
|
try {
|
|
3924
|
-
const { values, positionals } =
|
|
4398
|
+
const { values, positionals } = parseArgs12({
|
|
3925
4399
|
args,
|
|
3926
4400
|
options: MAINT_OPTIONS,
|
|
3927
4401
|
allowPositionals: true
|
|
@@ -3988,7 +4462,7 @@ async function runMutate(op, args, deps) {
|
|
|
3988
4462
|
let flags;
|
|
3989
4463
|
let pubkey;
|
|
3990
4464
|
try {
|
|
3991
|
-
const { values, positionals } =
|
|
4465
|
+
const { values, positionals } = parseArgs12({
|
|
3992
4466
|
args,
|
|
3993
4467
|
options: MAINT_OPTIONS,
|
|
3994
4468
|
allowPositionals: true
|
|
@@ -4141,8 +4615,15 @@ var USAGE = `rig \u2014 git with a TOON remote (pay-to-write Nostr + Arweave)
|
|
|
4141
4615
|
Usage: rig <command> [options]
|
|
4142
4616
|
|
|
4143
4617
|
Commands rig owns:
|
|
4144
|
-
|
|
4145
|
-
|
|
4618
|
+
identity create generate a fresh identity (BIP-39) into the
|
|
4619
|
+
encrypted keystore \u2014 the phrase is shown ONCE;
|
|
4620
|
+
back it up. This is your first step on a new box
|
|
4621
|
+
identity show the active identity's source + derived pubkey
|
|
4622
|
+
(never the phrase)
|
|
4623
|
+
identity import write an EXISTING phrase (read from stdin) to the
|
|
4624
|
+
encrypted keystore
|
|
4625
|
+
init set up this repo: resolve your identity (or offer
|
|
4626
|
+
to generate one) and write the toon.* git config
|
|
4146
4627
|
remote add <name> <url> add a relay as a REAL git remote ("origin" is
|
|
4147
4628
|
remote remove <name> the default publish target); remove/list manage
|
|
4148
4629
|
remote list them \u2014 \`git remote -v\` shows the same data
|
|
@@ -4219,6 +4700,8 @@ async function dispatch(argv2, deps) {
|
|
|
4219
4700
|
switch (command) {
|
|
4220
4701
|
case "init":
|
|
4221
4702
|
return runInit(rest, deps);
|
|
4703
|
+
case "identity":
|
|
4704
|
+
return runIdentity(rest, deps);
|
|
4222
4705
|
case "remote":
|
|
4223
4706
|
return runRemote(rest, deps);
|
|
4224
4707
|
// The #278 read path — FREE (relay + Arweave gateway reads, no payment).
|
|
@@ -4306,6 +4789,7 @@ function makeCliIo(options) {
|
|
|
4306
4789
|
}
|
|
4307
4790
|
var RIG_OWNED_VERBS = /* @__PURE__ */ new Set([
|
|
4308
4791
|
"init",
|
|
4792
|
+
"identity",
|
|
4309
4793
|
"remote",
|
|
4310
4794
|
"clone",
|
|
4311
4795
|
"fetch",
|
|
@@ -4383,7 +4867,7 @@ function makeIo(jsonMode) {
|
|
|
4383
4867
|
},
|
|
4384
4868
|
isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
4385
4869
|
confirm: async (question) => {
|
|
4386
|
-
const rl =
|
|
4870
|
+
const rl = createInterface2({
|
|
4387
4871
|
input: process.stdin,
|
|
4388
4872
|
output: process.stderr
|
|
4389
4873
|
});
|