@toon-protocol/rig 2.3.0 → 2.4.0
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 +476 -22
- 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) {
|
|
@@ -3668,9 +3673,396 @@ var runGitPassthrough = (argv2, options = {}) => {
|
|
|
3668
3673
|
});
|
|
3669
3674
|
};
|
|
3670
3675
|
|
|
3676
|
+
// src/cli/identity-cmd.ts
|
|
3677
|
+
import { existsSync, mkdirSync } from "fs";
|
|
3678
|
+
import { dirname as dirname2 } from "path";
|
|
3679
|
+
import { createInterface } from "readline/promises";
|
|
3680
|
+
import { parseArgs as parseArgs10 } from "util";
|
|
3681
|
+
async function loadKeyOps() {
|
|
3682
|
+
return await import("@toon-protocol/client");
|
|
3683
|
+
}
|
|
3684
|
+
var IdentityExistsError = class extends Error {
|
|
3685
|
+
constructor(pubkey, sourceLabel) {
|
|
3686
|
+
super(
|
|
3687
|
+
`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).`
|
|
3688
|
+
);
|
|
3689
|
+
this.pubkey = pubkey;
|
|
3690
|
+
this.sourceLabel = sourceLabel;
|
|
3691
|
+
this.name = "IdentityExistsError";
|
|
3692
|
+
}
|
|
3693
|
+
pubkey;
|
|
3694
|
+
sourceLabel;
|
|
3695
|
+
};
|
|
3696
|
+
var KeystoreFileExistsError = class extends Error {
|
|
3697
|
+
constructor(keystorePath) {
|
|
3698
|
+
super(
|
|
3699
|
+
`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.`
|
|
3700
|
+
);
|
|
3701
|
+
this.keystorePath = keystorePath;
|
|
3702
|
+
this.name = "KeystoreFileExistsError";
|
|
3703
|
+
}
|
|
3704
|
+
keystorePath;
|
|
3705
|
+
};
|
|
3706
|
+
async function prepareKeystoreTarget(input) {
|
|
3707
|
+
const keyOps = await (input.loadKeyOps ?? loadKeyOps)();
|
|
3708
|
+
const resolveImpl = input.resolveIdentityImpl ?? resolveIdentity;
|
|
3709
|
+
let existing;
|
|
3710
|
+
try {
|
|
3711
|
+
existing = await resolveImpl({
|
|
3712
|
+
env: input.env,
|
|
3713
|
+
cwd: input.cwd,
|
|
3714
|
+
warn: input.warn
|
|
3715
|
+
});
|
|
3716
|
+
} catch (err) {
|
|
3717
|
+
if (!(err instanceof MissingIdentityError)) throw err;
|
|
3718
|
+
}
|
|
3719
|
+
if (existing && !input.force) {
|
|
3720
|
+
throw new IdentityExistsError(existing.pubkey, existing.sourceLabel);
|
|
3721
|
+
}
|
|
3722
|
+
const keystorePath = clientKeystorePath(input.env);
|
|
3723
|
+
if (existsSync(keystorePath) && !input.force) {
|
|
3724
|
+
throw new KeystoreFileExistsError(keystorePath);
|
|
3725
|
+
}
|
|
3726
|
+
const envPassword = input.env["TOON_CLIENT_KEYSTORE_PASSWORD"]?.trim() || void 0;
|
|
3727
|
+
const autoPassword = envPassword === void 0;
|
|
3728
|
+
const password = envPassword ?? DEFAULT_KEYSTORE_PASSWORD;
|
|
3729
|
+
mkdirSync(dirname2(keystorePath), { recursive: true });
|
|
3730
|
+
return { keystorePath, password, autoPassword, existing, keyOps };
|
|
3731
|
+
}
|
|
3732
|
+
function readAccountIndex(env) {
|
|
3733
|
+
const file = readClientConfigFile(clientConfigPath(env));
|
|
3734
|
+
return typeof file.mnemonicAccountIndex === "number" ? file.mnemonicAccountIndex : 0;
|
|
3735
|
+
}
|
|
3736
|
+
function finishMint(input, target, mnemonic) {
|
|
3737
|
+
const configPath = linkKeystoreInClientConfig(
|
|
3738
|
+
input.env,
|
|
3739
|
+
target.keystorePath,
|
|
3740
|
+
target.autoPassword
|
|
3741
|
+
);
|
|
3742
|
+
const accountIndex = readAccountIndex(input.env);
|
|
3743
|
+
const { pubkey } = target.keyOps.deriveNostrKeyFromMnemonic(
|
|
3744
|
+
mnemonic,
|
|
3745
|
+
accountIndex
|
|
3746
|
+
);
|
|
3747
|
+
const result = {
|
|
3748
|
+
mnemonic,
|
|
3749
|
+
pubkey,
|
|
3750
|
+
keystorePath: target.keystorePath,
|
|
3751
|
+
configPath,
|
|
3752
|
+
autoPassword: target.autoPassword,
|
|
3753
|
+
accountIndex
|
|
3754
|
+
};
|
|
3755
|
+
if (target.existing && target.existing.pubkey !== pubkey) {
|
|
3756
|
+
result.shadowedBy = {
|
|
3757
|
+
sourceLabel: target.existing.sourceLabel,
|
|
3758
|
+
pubkey: target.existing.pubkey
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
return result;
|
|
3762
|
+
}
|
|
3763
|
+
async function createIdentity(input) {
|
|
3764
|
+
const target = await prepareKeystoreTarget(input);
|
|
3765
|
+
const { mnemonic } = target.keyOps.generateKeystore(
|
|
3766
|
+
target.keystorePath,
|
|
3767
|
+
target.password
|
|
3768
|
+
);
|
|
3769
|
+
return finishMint(input, target, mnemonic);
|
|
3770
|
+
}
|
|
3771
|
+
async function importIdentity(input) {
|
|
3772
|
+
const target = await prepareKeystoreTarget(input);
|
|
3773
|
+
target.keyOps.importKeystore(
|
|
3774
|
+
target.keystorePath,
|
|
3775
|
+
input.mnemonic,
|
|
3776
|
+
target.password
|
|
3777
|
+
);
|
|
3778
|
+
return finishMint(input, target, input.mnemonic);
|
|
3779
|
+
}
|
|
3780
|
+
function createdIdentityBanner(result) {
|
|
3781
|
+
return [
|
|
3782
|
+
"",
|
|
3783
|
+
"\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",
|
|
3784
|
+
" rig: generated a new identity",
|
|
3785
|
+
"\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",
|
|
3786
|
+
` Nostr pubkey : ${result.pubkey}`,
|
|
3787
|
+
"",
|
|
3788
|
+
" Seed phrase \u2014 this phrase CONTROLS YOUR FUNDS. Write it down and",
|
|
3789
|
+
" store it safely. It is shown ONCE and CANNOT be recovered:",
|
|
3790
|
+
"",
|
|
3791
|
+
` ${result.mnemonic}`,
|
|
3792
|
+
"",
|
|
3793
|
+
` Encrypted keystore: ${result.keystorePath}`,
|
|
3794
|
+
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.",
|
|
3795
|
+
"\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",
|
|
3796
|
+
""
|
|
3797
|
+
];
|
|
3798
|
+
}
|
|
3799
|
+
function shadowNote(shadowedBy) {
|
|
3800
|
+
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).`;
|
|
3801
|
+
}
|
|
3802
|
+
var IDENTITY_USAGE = `Usage: rig identity <command> [options]
|
|
3803
|
+
|
|
3804
|
+
Manage the BIP-39 signing identity rig uses to sign and pay:
|
|
3805
|
+
|
|
3806
|
+
create generate a fresh identity into the encrypted keystore under
|
|
3807
|
+
TOON_CLIENT_HOME. The seed phrase is shown ONCE \u2014 back it up.
|
|
3808
|
+
Refuses to overwrite an existing identity without --force.
|
|
3809
|
+
show report the ACTIVE identity's source + derived pubkey (never the
|
|
3810
|
+
phrase); errors with the resolution remediation when none exists.
|
|
3811
|
+
import read an EXISTING phrase from stdin (never a CLI argument) and write
|
|
3812
|
+
it to the encrypted keystore.
|
|
3813
|
+
|
|
3814
|
+
Options:
|
|
3815
|
+
--force (create/import) replace an existing identity/keystore
|
|
3816
|
+
--json machine-readable output. NOTE: \`identity create --json\`
|
|
3817
|
+
DELIBERATELY emits the seed phrase in a \`mnemonic\`
|
|
3818
|
+
field \u2014 treat that output as SECRET. \`identity show\`
|
|
3819
|
+
and \`identity import\` never emit the phrase.
|
|
3820
|
+
-h, --help show this help
|
|
3821
|
+
|
|
3822
|
+
The keystore is encrypted with TOON_CLIENT_KEYSTORE_PASSWORD when that env var
|
|
3823
|
+
is set, else a default password so the identity reloads with no configuration.
|
|
3824
|
+
rig deliberately does NOT accept the keystore password as a CLI flag \u2014 that
|
|
3825
|
+
would leak the value that decrypts your keys to shell history and \`ps\`.`;
|
|
3826
|
+
async function runIdentity(args, deps) {
|
|
3827
|
+
const [sub, ...rest] = args;
|
|
3828
|
+
switch (sub) {
|
|
3829
|
+
case "create":
|
|
3830
|
+
return runIdentityCreate(rest, deps);
|
|
3831
|
+
case "show":
|
|
3832
|
+
return runIdentityShow(rest, deps);
|
|
3833
|
+
case "import":
|
|
3834
|
+
return runIdentityImport(rest, deps);
|
|
3835
|
+
case "help":
|
|
3836
|
+
case "--help":
|
|
3837
|
+
case "-h":
|
|
3838
|
+
deps.io.out(IDENTITY_USAGE);
|
|
3839
|
+
return 0;
|
|
3840
|
+
case void 0:
|
|
3841
|
+
deps.io.err("rig identity needs a subcommand: create | show | import");
|
|
3842
|
+
deps.io.err(IDENTITY_USAGE);
|
|
3843
|
+
return 2;
|
|
3844
|
+
default:
|
|
3845
|
+
deps.io.err(`unknown rig identity subcommand: ${sub}`);
|
|
3846
|
+
deps.io.err(IDENTITY_USAGE);
|
|
3847
|
+
return 2;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
function parseMintArgs(args, verb) {
|
|
3851
|
+
const { values, positionals } = parseArgs10({
|
|
3852
|
+
args,
|
|
3853
|
+
options: {
|
|
3854
|
+
force: { type: "boolean", default: false },
|
|
3855
|
+
json: { type: "boolean", default: false },
|
|
3856
|
+
help: { type: "boolean", short: "h", default: false }
|
|
3857
|
+
},
|
|
3858
|
+
allowPositionals: false
|
|
3859
|
+
});
|
|
3860
|
+
if (positionals.length > 0) {
|
|
3861
|
+
throw new Error(`rig identity ${verb} takes no positional arguments`);
|
|
3862
|
+
}
|
|
3863
|
+
return {
|
|
3864
|
+
force: values.force ?? false,
|
|
3865
|
+
json: values.json ?? false,
|
|
3866
|
+
help: values.help ?? false
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3869
|
+
async function runIdentityCreate(args, deps) {
|
|
3870
|
+
const { io: io2 } = deps;
|
|
3871
|
+
let flags;
|
|
3872
|
+
try {
|
|
3873
|
+
flags = parseMintArgs(args, "create");
|
|
3874
|
+
} catch (err) {
|
|
3875
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3876
|
+
io2.err(IDENTITY_USAGE);
|
|
3877
|
+
return 2;
|
|
3878
|
+
}
|
|
3879
|
+
if (flags.help) {
|
|
3880
|
+
io2.out(IDENTITY_USAGE);
|
|
3881
|
+
return 0;
|
|
3882
|
+
}
|
|
3883
|
+
try {
|
|
3884
|
+
const result = await createIdentity({
|
|
3885
|
+
env: deps.env,
|
|
3886
|
+
cwd: deps.cwd,
|
|
3887
|
+
force: flags.force,
|
|
3888
|
+
warn: (line) => io2.err(line),
|
|
3889
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {},
|
|
3890
|
+
...deps.loadKeyOps ? { loadKeyOps: deps.loadKeyOps } : {}
|
|
3891
|
+
});
|
|
3892
|
+
emitCreated(io2, flags.json, "identity create", result);
|
|
3893
|
+
return 0;
|
|
3894
|
+
} catch (err) {
|
|
3895
|
+
return emitMintError(io2, flags.json, "identity create", err);
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
async function runIdentityImport(args, deps) {
|
|
3899
|
+
const { io: io2 } = deps;
|
|
3900
|
+
let flags;
|
|
3901
|
+
try {
|
|
3902
|
+
flags = parseMintArgs(args, "import");
|
|
3903
|
+
} catch (err) {
|
|
3904
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3905
|
+
io2.err(IDENTITY_USAGE);
|
|
3906
|
+
return 2;
|
|
3907
|
+
}
|
|
3908
|
+
if (flags.help) {
|
|
3909
|
+
io2.out(IDENTITY_USAGE);
|
|
3910
|
+
return 0;
|
|
3911
|
+
}
|
|
3912
|
+
try {
|
|
3913
|
+
const read = deps.readSecretLine ?? defaultReadSecretLine;
|
|
3914
|
+
const phrase = (await read(
|
|
3915
|
+
"Paste your BIP-39 seed phrase (read from stdin, not the terminal history): ",
|
|
3916
|
+
io2.isInteractive
|
|
3917
|
+
)).trim();
|
|
3918
|
+
if (phrase === "") {
|
|
3919
|
+
throw new Error(
|
|
3920
|
+
"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`)."
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3923
|
+
const result = await importIdentity({
|
|
3924
|
+
env: deps.env,
|
|
3925
|
+
cwd: deps.cwd,
|
|
3926
|
+
force: flags.force,
|
|
3927
|
+
mnemonic: phrase,
|
|
3928
|
+
warn: (line) => io2.err(line),
|
|
3929
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {},
|
|
3930
|
+
...deps.loadKeyOps ? { loadKeyOps: deps.loadKeyOps } : {}
|
|
3931
|
+
});
|
|
3932
|
+
if (flags.json) {
|
|
3933
|
+
io2.emitJson({
|
|
3934
|
+
command: "identity import",
|
|
3935
|
+
imported: true,
|
|
3936
|
+
pubkey: result.pubkey,
|
|
3937
|
+
keystorePath: result.keystorePath,
|
|
3938
|
+
autoPassword: result.autoPassword,
|
|
3939
|
+
...result.shadowedBy ? { shadowedBy: result.shadowedBy } : {}
|
|
3940
|
+
});
|
|
3941
|
+
} else {
|
|
3942
|
+
io2.out(`Imported identity ${result.pubkey}`);
|
|
3943
|
+
io2.out(` Encrypted keystore: ${result.keystorePath}`);
|
|
3944
|
+
}
|
|
3945
|
+
if (result.shadowedBy) io2.err(shadowNote(result.shadowedBy));
|
|
3946
|
+
return 0;
|
|
3947
|
+
} catch (err) {
|
|
3948
|
+
return emitMintError(io2, flags.json, "identity import", err);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
async function runIdentityShow(args, deps) {
|
|
3952
|
+
const { io: io2 } = deps;
|
|
3953
|
+
let json = false;
|
|
3954
|
+
let help = false;
|
|
3955
|
+
try {
|
|
3956
|
+
const parsed = parseArgs10({
|
|
3957
|
+
args,
|
|
3958
|
+
options: {
|
|
3959
|
+
json: { type: "boolean", default: false },
|
|
3960
|
+
help: { type: "boolean", short: "h", default: false }
|
|
3961
|
+
},
|
|
3962
|
+
allowPositionals: false
|
|
3963
|
+
});
|
|
3964
|
+
json = parsed.values.json ?? false;
|
|
3965
|
+
help = parsed.values.help ?? false;
|
|
3966
|
+
} catch (err) {
|
|
3967
|
+
io2.err(err instanceof Error ? err.message : String(err));
|
|
3968
|
+
io2.err(IDENTITY_USAGE);
|
|
3969
|
+
return 2;
|
|
3970
|
+
}
|
|
3971
|
+
if (help) {
|
|
3972
|
+
io2.out(IDENTITY_USAGE);
|
|
3973
|
+
return 0;
|
|
3974
|
+
}
|
|
3975
|
+
try {
|
|
3976
|
+
const identity = await (deps.resolveIdentityImpl ?? resolveIdentity)({
|
|
3977
|
+
env: deps.env,
|
|
3978
|
+
cwd: deps.cwd,
|
|
3979
|
+
warn: (line) => io2.err(line)
|
|
3980
|
+
});
|
|
3981
|
+
if (json) {
|
|
3982
|
+
io2.emitJson({
|
|
3983
|
+
command: "identity show",
|
|
3984
|
+
source: identity.source,
|
|
3985
|
+
sourceLabel: identity.sourceLabel,
|
|
3986
|
+
pubkey: identity.pubkey,
|
|
3987
|
+
accountIndex: identity.accountIndex
|
|
3988
|
+
});
|
|
3989
|
+
} else {
|
|
3990
|
+
io2.out(renderIdentityLine(identity));
|
|
3991
|
+
io2.out(` source : ${identity.source} (${identity.sourceLabel})`);
|
|
3992
|
+
io2.out(` pubkey : ${identity.pubkey}`);
|
|
3993
|
+
}
|
|
3994
|
+
return 0;
|
|
3995
|
+
} catch (err) {
|
|
3996
|
+
return emitCliError(io2, json, "identity show", err);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
function emitCreated(io2, json, command, result) {
|
|
4000
|
+
if (json) {
|
|
4001
|
+
io2.emitJson({
|
|
4002
|
+
command,
|
|
4003
|
+
created: true,
|
|
4004
|
+
pubkey: result.pubkey,
|
|
4005
|
+
keystorePath: result.keystorePath,
|
|
4006
|
+
autoPassword: result.autoPassword,
|
|
4007
|
+
// DELIBERATE, documented exception to "never print the phrase": the
|
|
4008
|
+
// scripting/agent path needs the generated phrase to back it up.
|
|
4009
|
+
mnemonic: result.mnemonic,
|
|
4010
|
+
...result.shadowedBy ? { shadowedBy: result.shadowedBy } : {}
|
|
4011
|
+
});
|
|
4012
|
+
io2.err(
|
|
4013
|
+
"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."
|
|
4014
|
+
);
|
|
4015
|
+
} else {
|
|
4016
|
+
for (const line of createdIdentityBanner(result)) io2.out(line);
|
|
4017
|
+
}
|
|
4018
|
+
if (result.shadowedBy) io2.err(shadowNote(result.shadowedBy));
|
|
4019
|
+
}
|
|
4020
|
+
function emitMintError(io2, json, command, err) {
|
|
4021
|
+
if (err instanceof IdentityExistsError) {
|
|
4022
|
+
if (json) {
|
|
4023
|
+
io2.emitJson({
|
|
4024
|
+
command,
|
|
4025
|
+
error: "identity_exists",
|
|
4026
|
+
pubkey: err.pubkey,
|
|
4027
|
+
detail: err.message
|
|
4028
|
+
});
|
|
4029
|
+
}
|
|
4030
|
+
for (const line of err.message.split("\n")) io2.err(line);
|
|
4031
|
+
return 1;
|
|
4032
|
+
}
|
|
4033
|
+
if (err instanceof KeystoreFileExistsError) {
|
|
4034
|
+
if (json) {
|
|
4035
|
+
io2.emitJson({
|
|
4036
|
+
command,
|
|
4037
|
+
error: "keystore_exists",
|
|
4038
|
+
keystorePath: err.keystorePath,
|
|
4039
|
+
detail: err.message
|
|
4040
|
+
});
|
|
4041
|
+
}
|
|
4042
|
+
for (const line of err.message.split("\n")) io2.err(line);
|
|
4043
|
+
return 1;
|
|
4044
|
+
}
|
|
4045
|
+
return emitCliError(io2, json, command, err);
|
|
4046
|
+
}
|
|
4047
|
+
async function defaultReadSecretLine(prompt, isInteractive) {
|
|
4048
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
4049
|
+
if (isInteractive) {
|
|
4050
|
+
process.stderr.write(prompt);
|
|
4051
|
+
rl._writeToOutput = () => {
|
|
4052
|
+
};
|
|
4053
|
+
}
|
|
4054
|
+
try {
|
|
4055
|
+
const answer = await rl.question("");
|
|
4056
|
+
if (isInteractive) process.stderr.write("\n");
|
|
4057
|
+
return answer;
|
|
4058
|
+
} finally {
|
|
4059
|
+
rl.close();
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
|
|
3671
4063
|
// src/cli/init.ts
|
|
3672
4064
|
import { basename as basename2 } from "path";
|
|
3673
|
-
import { parseArgs as
|
|
4065
|
+
import { parseArgs as parseArgs11 } from "util";
|
|
3674
4066
|
var INIT_USAGE = `Usage: rig init [options]
|
|
3675
4067
|
|
|
3676
4068
|
Set up the current git repository for rig (one-shot, idempotent, free):
|
|
@@ -3684,16 +4076,23 @@ The follow-up step is adding a relay: \`rig remote add origin <relay-url>\`
|
|
|
3684
4076
|
\`git config toon.relay\` is migrated to the origin remote automatically
|
|
3685
4077
|
when no origin exists (the old key stays readable and is removed in v0.3).
|
|
3686
4078
|
|
|
4079
|
+
When NO identity resolves, init does not dead-end: in a terminal it offers to
|
|
4080
|
+
generate one (\`Create a new identity now? [y/N]\`), and \`--generate-identity\`
|
|
4081
|
+
does it non-interactively (equivalent to \`rig identity create\` \u2014 the phrase
|
|
4082
|
+
is shown once to back up). Nothing is ever auto-generated without your yes.
|
|
4083
|
+
|
|
3687
4084
|
Options:
|
|
3688
|
-
--repo-id <id>
|
|
3689
|
-
|
|
3690
|
-
--
|
|
3691
|
-
-
|
|
4085
|
+
--repo-id <id> repository id / NIP-34 d-tag (default: the existing
|
|
4086
|
+
toon.repoid, then the repo directory basename)
|
|
4087
|
+
--generate-identity when no identity resolves, mint a fresh one (no prompt)
|
|
4088
|
+
--json machine-readable report
|
|
4089
|
+
-h, --help show this help`;
|
|
3692
4090
|
function parseInitArgs(args) {
|
|
3693
|
-
const { values, positionals } =
|
|
4091
|
+
const { values, positionals } = parseArgs11({
|
|
3694
4092
|
args,
|
|
3695
4093
|
options: {
|
|
3696
4094
|
"repo-id": { type: "string" },
|
|
4095
|
+
"generate-identity": { type: "boolean", default: false },
|
|
3697
4096
|
json: { type: "boolean", default: false },
|
|
3698
4097
|
help: { type: "boolean", short: "h", default: false }
|
|
3699
4098
|
},
|
|
@@ -3703,6 +4102,7 @@ function parseInitArgs(args) {
|
|
|
3703
4102
|
throw new Error(`rig init takes no positional arguments`);
|
|
3704
4103
|
}
|
|
3705
4104
|
const flags = {
|
|
4105
|
+
generateIdentity: values["generate-identity"] ?? false,
|
|
3706
4106
|
json: values.json ?? false,
|
|
3707
4107
|
help: values.help ?? false
|
|
3708
4108
|
};
|
|
@@ -3713,8 +4113,38 @@ function parseInitArgs(args) {
|
|
|
3713
4113
|
}
|
|
3714
4114
|
return flags;
|
|
3715
4115
|
}
|
|
3716
|
-
async function
|
|
4116
|
+
async function resolveOrGenerateIdentity(deps, flags) {
|
|
3717
4117
|
const { io: io2, env } = deps;
|
|
4118
|
+
const resolve2 = deps.resolveIdentityImpl ?? resolveIdentity;
|
|
4119
|
+
try {
|
|
4120
|
+
return { identity: await resolve2({ env, cwd: deps.cwd, warn: io2.err }) };
|
|
4121
|
+
} catch (err) {
|
|
4122
|
+
if (!(err instanceof MissingIdentityError)) throw err;
|
|
4123
|
+
let generate = flags.generateIdentity;
|
|
4124
|
+
if (!generate && io2.isInteractive && !flags.json) {
|
|
4125
|
+
io2.err("No identity found \u2014 rig needs one to sign and pay.");
|
|
4126
|
+
generate = await io2.confirm("Create a new identity now? [y/N] ");
|
|
4127
|
+
}
|
|
4128
|
+
if (!generate) throw err;
|
|
4129
|
+
const created = await (deps.createIdentityImpl ?? createIdentity)({
|
|
4130
|
+
env,
|
|
4131
|
+
cwd: deps.cwd,
|
|
4132
|
+
force: false,
|
|
4133
|
+
warn: io2.err,
|
|
4134
|
+
...deps.resolveIdentityImpl ? { resolveIdentityImpl: deps.resolveIdentityImpl } : {}
|
|
4135
|
+
});
|
|
4136
|
+
const identity = {
|
|
4137
|
+
mnemonic: created.mnemonic,
|
|
4138
|
+
accountIndex: created.accountIndex,
|
|
4139
|
+
source: "keystore",
|
|
4140
|
+
sourceLabel: created.keystorePath,
|
|
4141
|
+
pubkey: created.pubkey
|
|
4142
|
+
};
|
|
4143
|
+
return { identity, generated: created };
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
async function runInit(args, deps) {
|
|
4147
|
+
const { io: io2 } = deps;
|
|
3718
4148
|
let flags;
|
|
3719
4149
|
try {
|
|
3720
4150
|
flags = parseInitArgs(args);
|
|
@@ -3734,11 +4164,7 @@ async function runInit(args, deps) {
|
|
|
3734
4164
|
} catch {
|
|
3735
4165
|
throw new NotAGitRepositoryError(deps.cwd);
|
|
3736
4166
|
}
|
|
3737
|
-
const identity = await (deps
|
|
3738
|
-
env,
|
|
3739
|
-
cwd: deps.cwd,
|
|
3740
|
-
warn: (line) => io2.err(line)
|
|
3741
|
-
});
|
|
4167
|
+
const { identity, generated } = await resolveOrGenerateIdentity(deps, flags);
|
|
3742
4168
|
const existing = await readToonConfig(repoRoot);
|
|
3743
4169
|
const repoId = flags.repoId ?? existing.repoId ?? basename2(repoRoot);
|
|
3744
4170
|
const changed = {
|
|
@@ -3772,11 +4198,29 @@ async function runInit(args, deps) {
|
|
|
3772
4198
|
remotes,
|
|
3773
4199
|
origin: originRelay ?? null,
|
|
3774
4200
|
migratedToonRelay,
|
|
3775
|
-
changed
|
|
4201
|
+
changed,
|
|
4202
|
+
...generated ? {
|
|
4203
|
+
generatedIdentity: {
|
|
4204
|
+
mnemonic: generated.mnemonic,
|
|
4205
|
+
pubkey: generated.pubkey,
|
|
4206
|
+
keystorePath: generated.keystorePath,
|
|
4207
|
+
autoPassword: generated.autoPassword
|
|
4208
|
+
}
|
|
4209
|
+
} : {}
|
|
3776
4210
|
};
|
|
3777
4211
|
io2.emitJson(output);
|
|
4212
|
+
if (generated) {
|
|
4213
|
+
io2.err(
|
|
4214
|
+
"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."
|
|
4215
|
+
);
|
|
4216
|
+
if (generated.shadowedBy) io2.err(shadowNote(generated.shadowedBy));
|
|
4217
|
+
}
|
|
3778
4218
|
return 0;
|
|
3779
4219
|
}
|
|
4220
|
+
if (generated) {
|
|
4221
|
+
for (const line of createdIdentityBanner(generated)) io2.out(line);
|
|
4222
|
+
if (generated.shadowedBy) io2.err(shadowNote(generated.shadowedBy));
|
|
4223
|
+
}
|
|
3780
4224
|
io2.out(`Initialized rig for ${repoRoot}`);
|
|
3781
4225
|
io2.out(renderIdentityLine(identity));
|
|
3782
4226
|
io2.out(
|
|
@@ -3816,7 +4260,7 @@ async function runInit(args, deps) {
|
|
|
3816
4260
|
}
|
|
3817
4261
|
|
|
3818
4262
|
// src/cli/maintainers.ts
|
|
3819
|
-
import { parseArgs as
|
|
4263
|
+
import { parseArgs as parseArgs12 } from "util";
|
|
3820
4264
|
var HEX64_RE3 = /^[0-9a-f]{64}$/;
|
|
3821
4265
|
var WS_URL_RE4 = /^wss?:\/\//i;
|
|
3822
4266
|
var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
|
|
@@ -3921,7 +4365,7 @@ async function runList2(args, deps) {
|
|
|
3921
4365
|
const { io: io2 } = deps;
|
|
3922
4366
|
let flags;
|
|
3923
4367
|
try {
|
|
3924
|
-
const { values, positionals } =
|
|
4368
|
+
const { values, positionals } = parseArgs12({
|
|
3925
4369
|
args,
|
|
3926
4370
|
options: MAINT_OPTIONS,
|
|
3927
4371
|
allowPositionals: true
|
|
@@ -3988,7 +4432,7 @@ async function runMutate(op, args, deps) {
|
|
|
3988
4432
|
let flags;
|
|
3989
4433
|
let pubkey;
|
|
3990
4434
|
try {
|
|
3991
|
-
const { values, positionals } =
|
|
4435
|
+
const { values, positionals } = parseArgs12({
|
|
3992
4436
|
args,
|
|
3993
4437
|
options: MAINT_OPTIONS,
|
|
3994
4438
|
allowPositionals: true
|
|
@@ -4141,8 +4585,15 @@ var USAGE = `rig \u2014 git with a TOON remote (pay-to-write Nostr + Arweave)
|
|
|
4141
4585
|
Usage: rig <command> [options]
|
|
4142
4586
|
|
|
4143
4587
|
Commands rig owns:
|
|
4144
|
-
|
|
4145
|
-
|
|
4588
|
+
identity create generate a fresh identity (BIP-39) into the
|
|
4589
|
+
encrypted keystore \u2014 the phrase is shown ONCE;
|
|
4590
|
+
back it up. This is your first step on a new box
|
|
4591
|
+
identity show the active identity's source + derived pubkey
|
|
4592
|
+
(never the phrase)
|
|
4593
|
+
identity import write an EXISTING phrase (read from stdin) to the
|
|
4594
|
+
encrypted keystore
|
|
4595
|
+
init set up this repo: resolve your identity (or offer
|
|
4596
|
+
to generate one) and write the toon.* git config
|
|
4146
4597
|
remote add <name> <url> add a relay as a REAL git remote ("origin" is
|
|
4147
4598
|
remote remove <name> the default publish target); remove/list manage
|
|
4148
4599
|
remote list them \u2014 \`git remote -v\` shows the same data
|
|
@@ -4219,6 +4670,8 @@ async function dispatch(argv2, deps) {
|
|
|
4219
4670
|
switch (command) {
|
|
4220
4671
|
case "init":
|
|
4221
4672
|
return runInit(rest, deps);
|
|
4673
|
+
case "identity":
|
|
4674
|
+
return runIdentity(rest, deps);
|
|
4222
4675
|
case "remote":
|
|
4223
4676
|
return runRemote(rest, deps);
|
|
4224
4677
|
// The #278 read path — FREE (relay + Arweave gateway reads, no payment).
|
|
@@ -4306,6 +4759,7 @@ function makeCliIo(options) {
|
|
|
4306
4759
|
}
|
|
4307
4760
|
var RIG_OWNED_VERBS = /* @__PURE__ */ new Set([
|
|
4308
4761
|
"init",
|
|
4762
|
+
"identity",
|
|
4309
4763
|
"remote",
|
|
4310
4764
|
"clone",
|
|
4311
4765
|
"fetch",
|
|
@@ -4383,7 +4837,7 @@ function makeIo(jsonMode) {
|
|
|
4383
4837
|
},
|
|
4384
4838
|
isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
4385
4839
|
confirm: async (question) => {
|
|
4386
|
-
const rl =
|
|
4840
|
+
const rl = createInterface2({
|
|
4387
4841
|
input: process.stdin,
|
|
4388
4842
|
output: process.stderr
|
|
4389
4843
|
});
|