@toon-protocol/rig 2.4.0 → 2.5.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/dist/cli/rig.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  OversizeObjectsError,
7
7
  collectRepoObjects,
8
8
  executePush,
9
+ hexToNpub,
9
10
  isSafeRefname,
10
11
  missingObjectsMessage,
11
12
  ownerToHex,
@@ -17,7 +18,7 @@ import {
17
18
  setHeadSymref,
18
19
  updateRef,
19
20
  writeGitObjects
20
- } from "../chunk-W2SDL2PE.js";
21
+ } from "../chunk-PTXKCR5R.js";
21
22
  import {
22
23
  DEFAULT_KEYSTORE_PASSWORD,
23
24
  MissingIdentityError,
@@ -27,6 +28,15 @@ import {
27
28
  readClientConfigFile,
28
29
  resolveIdentity
29
30
  } from "../chunk-XGFBDUQX.js";
31
+ import {
32
+ ChannelMapStore,
33
+ channelStatus,
34
+ defaultDaemonPort,
35
+ resolveChannelPaths
36
+ } from "../chunk-SW7ZHMGS.js";
37
+ import {
38
+ MAX_OBJECT_SIZE
39
+ } from "../chunk-X2CZPPDM.js";
30
40
  import {
31
41
  COMMENT_KIND,
32
42
  authorizedStatusAuthors,
@@ -39,15 +49,6 @@ import {
39
49
  parseMaintainers,
40
50
  queryRelay
41
51
  } from "../chunk-3HRFDH7H.js";
42
- import {
43
- ChannelMapStore,
44
- channelStatus,
45
- defaultDaemonPort,
46
- resolveChannelPaths
47
- } from "../chunk-SW7ZHMGS.js";
48
- import {
49
- MAX_OBJECT_SIZE
50
- } from "../chunk-X2CZPPDM.js";
51
52
 
52
53
  // src/cli/rig.ts
53
54
  import { createInterface as createInterface2 } from "readline/promises";
@@ -263,7 +264,7 @@ var NotAGitRepositoryError = class extends Error {
263
264
  constructor(cwd) {
264
265
  super(
265
266
  `not a git repository: ${cwd}
266
- rig works inside an existing repo \u2014 create one first with \`git init\` (rig never runs it for you), then re-run.`
267
+ rig can create one for you: re-run \`rig init --git-init\` (in a terminal, plain \`rig init\` offers the same via a prompt). Or run \`git init\` yourself first, then re-run.`
267
268
  );
268
269
  this.name = "NotAGitRepositoryError";
269
270
  }
@@ -607,6 +608,9 @@ async function resolveRepoRoot(cwd) {
607
608
  );
608
609
  }
609
610
  }
611
+ async function initGitRepository(dir) {
612
+ await git(dir, ["init"]);
613
+ }
610
614
  async function readToonConfig(repoPath) {
611
615
  const [repoId, owner, relays] = await Promise.all([
612
616
  git(repoPath, ["config", "--get", "toon.repoid"], [1]),
@@ -639,6 +643,10 @@ async function listGitRemotes(repoPath) {
639
643
  }
640
644
  return remotes;
641
645
  }
646
+ async function setGitAuthor(repoPath, author) {
647
+ await git(repoPath, ["config", "--local", "user.name", author.name]);
648
+ await git(repoPath, ["config", "--local", "user.email", author.email]);
649
+ }
642
650
  async function addGitRemote(repoPath, name, url) {
643
651
  await git(repoPath, ["remote", "add", name, url]);
644
652
  }
@@ -905,7 +913,7 @@ function loadPaidSession(deps, relayUrl) {
905
913
  });
906
914
  }
907
915
  var defaultLoadStandalone = async (options) => {
908
- const mod = await import("../standalone-mode-VH2XPOPK.js");
916
+ const mod = await import("../standalone-mode-BIFFNDH3.js");
909
917
  return mod.createStandaloneContext(options);
910
918
  };
911
919
  function identityReport(ctx) {
@@ -1215,14 +1223,26 @@ async function runPush(args, deps) {
1215
1223
  // src/cli/balance.ts
1216
1224
  var BALANCE_USAGE = `Usage: rig balance [--json]
1217
1225
 
1218
- Show the active identity's money: on-chain wallet balances (per configured
1219
- chain, read from the settlement chain the payment channels actually use) and
1220
- recorded payment-channel holdings (deposited / claimed / available). Free \u2014
1221
- reads chain RPCs and local state only; nothing is signed or paid.
1226
+ Show the active identity's money: the full on-chain wallet view \u2014 native coin
1227
+ plus USDC on every configured chain (EVM / Solana / Mina) \u2014 and recorded
1228
+ payment-channel holdings (deposited / claimed / available). Free \u2014 reads chain
1229
+ RPCs and local state only; nothing is signed or paid. An unreachable chain RPC
1230
+ degrades to a per-chain notice without failing the others.
1222
1231
 
1223
1232
  Options:
1224
1233
  --json machine-readable envelope (base units as strings)
1225
1234
  -h, --help show this help`;
1235
+ function formatUnits(amount, decimals) {
1236
+ if (decimals === void 0 || decimals <= 0) return amount;
1237
+ const neg = amount.startsWith("-");
1238
+ const digits = (neg ? amount.slice(1) : amount).padStart(decimals + 1, "0");
1239
+ const whole = digits.slice(0, digits.length - decimals);
1240
+ const frac = digits.slice(digits.length - decimals).replace(/0+$/, "");
1241
+ return (neg ? "-" : "") + (frac ? `${whole}.${frac}` : whole);
1242
+ }
1243
+ function renderAmount(a) {
1244
+ return `${a.symbol ?? "token"} ${formatUnits(a.amount, a.decimals)}`;
1245
+ }
1226
1246
  async function runBalance(args, deps) {
1227
1247
  const { io: io2, env } = deps;
1228
1248
  let json = false;
@@ -1254,7 +1274,7 @@ async function runBalance(args, deps) {
1254
1274
  requireUplink: false
1255
1275
  });
1256
1276
  const identity = identityReport(ctx);
1257
- const wallet = ctx.money ? await ctx.money.walletBalances() : [];
1277
+ const wallet = ctx.money ? await ctx.money.walletChainBalances() : [];
1258
1278
  const store = new ChannelMapStore(resolveChannelPaths(env));
1259
1279
  const channels = store.list().filter((record) => record.identity === identity.pubkey).map((record) => {
1260
1280
  const watermark = store.readWatermark(record.channelId);
@@ -1291,13 +1311,23 @@ async function runBalance(args, deps) {
1291
1311
  io2.out("Wallet (on-chain):");
1292
1312
  if (wallet.length === 0) {
1293
1313
  io2.out(
1294
- " (no balance readable \u2014 no chain configured, the RPC is unreachable, or the chain's keys derive only during a client start)"
1314
+ " (no chain configured for this identity \u2014 nothing to read)"
1295
1315
  );
1296
1316
  }
1297
- for (const balance of wallet) {
1298
- io2.out(
1299
- ` ${balance.chain.padEnd(7)} ${balance.address} ${balance.amount}` + (balance.asset ? ` ${balance.asset}` : " base units") + (balance.assetScale !== void 0 ? ` (scale ${balance.assetScale})` : "")
1300
- );
1317
+ for (const chain of wallet) {
1318
+ io2.out(` ${chain.chainKey.padEnd(11)} ${chain.address}`);
1319
+ if (chain.unreadable) {
1320
+ io2.out(
1321
+ ` unreadable (RPC unreachable)` + (chain.error ? ` \u2014 ${chain.error}` : "")
1322
+ );
1323
+ continue;
1324
+ }
1325
+ const assets = [...chain.native ? [chain.native] : [], ...chain.tokens];
1326
+ if (assets.length === 0) {
1327
+ io2.out(" (no balance readable)");
1328
+ continue;
1329
+ }
1330
+ io2.out(` ${assets.map(renderAmount).join(" ")}`);
1301
1331
  }
1302
1332
  io2.out("");
1303
1333
  io2.out("Channels (recorded):");
@@ -3439,7 +3469,9 @@ pays). The identity comes from RIG_MNEMONIC (env or a project .env) or the
3439
3469
  ~/.toon-client keystore/config; the faucet from TOON_CLIENT_FAUCET_URL, the
3440
3470
  faucetUrl config field, or the deployed devnet faucet when the network is
3441
3471
  devnet \u2014 including when a configured *.devnet.toonprotocol.dev origin infers
3442
- it. The faucet drips a FIXED amount per chain (there is no --amount). On a
3472
+ it (a devnet relay/proxy/btp endpoint, or the git origin remote from
3473
+ \`rig remote add origin <devnet relay>\`). The faucet drips a FIXED amount per
3474
+ chain (there is no --amount). On a
3443
3475
  network without a faucet, prints the wallet address(es) to fund externally
3444
3476
  instead.
3445
3477
 
@@ -3450,24 +3482,50 @@ Options:
3450
3482
  --json machine-readable envelope
3451
3483
  -h, --help show this help`;
3452
3484
  var SHARED_DEVNET_SUFFIX = ".devnet.toonprotocol.dev";
3453
- function sharedDevnetOrigin(env, file) {
3485
+ function devnetHost(url) {
3486
+ if (!url) return void 0;
3487
+ try {
3488
+ const { hostname } = new URL(url);
3489
+ if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1)) {
3490
+ return url;
3491
+ }
3492
+ } catch {
3493
+ }
3494
+ return void 0;
3495
+ }
3496
+ function sharedDevnetOrigin(env, file, originRelays = []) {
3454
3497
  const candidates = [
3455
3498
  env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl,
3456
3499
  env["TOON_CLIENT_PROXY_URL"] ?? file.proxyUrl,
3457
- env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl
3500
+ env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl,
3501
+ ...originRelays
3458
3502
  ];
3459
3503
  for (const url of candidates) {
3460
- if (!url) continue;
3461
- try {
3462
- const { hostname } = new URL(url);
3463
- if (hostname.endsWith(SHARED_DEVNET_SUFFIX) || hostname === SHARED_DEVNET_SUFFIX.slice(1)) {
3464
- return url;
3465
- }
3466
- } catch {
3467
- }
3504
+ const hit = devnetHost(url);
3505
+ if (hit) return hit;
3468
3506
  }
3469
3507
  return void 0;
3470
3508
  }
3509
+ async function resolveOriginRelays(cwd) {
3510
+ let repoRoot;
3511
+ try {
3512
+ repoRoot = await resolveRepoRoot(cwd);
3513
+ } catch {
3514
+ return [];
3515
+ }
3516
+ try {
3517
+ const toonConfig = await readToonConfig(repoRoot);
3518
+ const resolved = await resolveRelays({
3519
+ relayFlags: [],
3520
+ remoteName: void 0,
3521
+ repoRoot,
3522
+ toonRelays: toonConfig.relays
3523
+ });
3524
+ return resolved.relays;
3525
+ } catch {
3526
+ return [];
3527
+ }
3528
+ }
3471
3529
  function noFaucetGuidance(network, devnetOrigin) {
3472
3530
  const head = `no faucet is configured for network ${JSON.stringify(network ?? "custom")}`;
3473
3531
  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.";
@@ -3534,7 +3592,9 @@ async function runFund(args, deps) {
3534
3592
  );
3535
3593
  }
3536
3594
  const network = env["TOON_CLIENT_NETWORK"] ?? file.network;
3537
- const devnetOrigin = sharedDevnetOrigin(env, file);
3595
+ const canInfer = network === void 0 || network === "custom";
3596
+ const originRelays = canInfer ? await resolveOriginRelays(deps.cwd) : [];
3597
+ const devnetOrigin = sharedDevnetOrigin(env, file, originRelays);
3538
3598
  const inferredDevnet = devnetOrigin !== void 0 && (network === void 0 || network === "custom");
3539
3599
  const effectiveNetwork = inferredDevnet ? "devnet" : network;
3540
3600
  const faucetUrl = env["TOON_CLIENT_FAUCET_URL"] ?? file.faucetUrl ?? (effectiveNetwork === "devnet" ? DEVNET_FAUCET_URL : void 0);
@@ -4063,6 +4123,82 @@ async function defaultReadSecretLine(prompt, isInteractive) {
4063
4123
  // src/cli/init.ts
4064
4124
  import { basename as basename2 } from "path";
4065
4125
  import { parseArgs as parseArgs11 } from "util";
4126
+
4127
+ // src/cli/git-author.ts
4128
+ var PROFILE_KIND = 0;
4129
+ var DEFAULT_PROFILE_TIMEOUT_MS = 3e3;
4130
+ var PROFILE_TIMEOUT_ENV = "RIG_PROFILE_TIMEOUT_MS";
4131
+ function isWebSocketRelay(url) {
4132
+ return /^wss?:\/\//i.test(url);
4133
+ }
4134
+ function displayNameFromKind0(content) {
4135
+ let parsed;
4136
+ try {
4137
+ parsed = JSON.parse(content);
4138
+ } catch {
4139
+ return void 0;
4140
+ }
4141
+ if (typeof parsed !== "object" || parsed === null) return void 0;
4142
+ const fields = parsed;
4143
+ for (const key of ["display_name", "name"]) {
4144
+ const value = fields[key];
4145
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
4146
+ }
4147
+ return void 0;
4148
+ }
4149
+ async function resolveGitAuthor(options) {
4150
+ const npub = hexToNpub(options.pubkey);
4151
+ const email = `${npub}@nostr`;
4152
+ const fallback = { name: npub, email, npub, source: "npub" };
4153
+ const { relayUrl } = options;
4154
+ if (relayUrl === void 0 || !isWebSocketRelay(relayUrl)) {
4155
+ return fallback;
4156
+ }
4157
+ const query = options.queryRelayImpl ?? queryRelay;
4158
+ const timeoutMs = options.timeoutMs ?? DEFAULT_PROFILE_TIMEOUT_MS;
4159
+ const factory = options.webSocketFactory ?? defaultWebSocketFactory2;
4160
+ try {
4161
+ const events = await query(
4162
+ relayUrl,
4163
+ { kinds: [PROFILE_KIND], authors: [options.pubkey], limit: 20 },
4164
+ timeoutMs,
4165
+ factory
4166
+ );
4167
+ let latest;
4168
+ for (const event of events) {
4169
+ if (event.kind !== PROFILE_KIND) continue;
4170
+ if (event.pubkey !== options.pubkey) continue;
4171
+ if (!latest || event.created_at > latest.created_at) {
4172
+ latest = { created_at: event.created_at, content: event.content };
4173
+ }
4174
+ }
4175
+ if (latest) {
4176
+ const name = displayNameFromKind0(latest.content);
4177
+ if (name !== void 0) {
4178
+ return { name, email, npub, source: "profile" };
4179
+ }
4180
+ }
4181
+ } catch {
4182
+ }
4183
+ return fallback;
4184
+ }
4185
+ function profileTimeoutFromEnv(env) {
4186
+ const raw = env[PROFILE_TIMEOUT_ENV]?.trim();
4187
+ if (!raw) return void 0;
4188
+ const ms = Number.parseInt(raw, 10);
4189
+ return Number.isFinite(ms) && ms > 0 ? ms : void 0;
4190
+ }
4191
+ function defaultWebSocketFactory2(url) {
4192
+ const ctor = globalThis.WebSocket;
4193
+ if (!ctor) {
4194
+ throw new Error(
4195
+ "No global WebSocket constructor (Node >= 22 required) for the kind:0 read"
4196
+ );
4197
+ }
4198
+ return new ctor(url);
4199
+ }
4200
+
4201
+ // src/cli/init.ts
4066
4202
  var INIT_USAGE = `Usage: rig init [options]
4067
4203
 
4068
4204
  Set up the current git repository for rig (one-shot, idempotent, free):
@@ -4071,19 +4207,35 @@ keystore/config), then writes toon.repoid and toon.owner to the repo's
4071
4207
  LOCAL git config. Re-running updates and reports; it never errors on an
4072
4208
  already-initialized repo. The seed phrase is never written anywhere.
4073
4209
 
4210
+ It also sets the repo's LOCAL git commit-author from your nostr identity
4211
+ (never --global) so \`rig commit\`/\`git commit\` work out of the box and every
4212
+ commit is attributed to the signer: user.email = <npub>@nostr, user.name =
4213
+ your kind:0 profile display name when published (read best-effort from a
4214
+ relay, latest-wins) else the npub. Re-running refreshes the name from a
4215
+ now-readable profile.
4216
+
4074
4217
  The follow-up step is adding a relay: \`rig remote add origin <relay-url>\`
4075
4218
  (a real git remote \u2014 \`git remote -v\` shows it). A deprecated v0.1
4076
4219
  \`git config toon.relay\` is migrated to the origin remote automatically
4077
4220
  when no origin exists (the old key stays readable and is removed in v0.3).
4078
4221
 
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.
4222
+ When the directory is NOT inside a git repository, init does not dead-end: in
4223
+ a terminal it offers to create one (\`Initialize a git repository here?
4224
+ [y/N]\`), and \`--git-init\` does it non-interactively. Likewise, when NO
4225
+ identity resolves it offers to generate one (\`Create a new identity now?
4226
+ [y/N]\`), and \`--generate-identity\` does it non-interactively (equivalent to
4227
+ \`rig identity create\` \u2014 the phrase is shown once to back up). Nothing \u2014 repo
4228
+ or identity \u2014 is ever created without your yes. \`--git-init
4229
+ --generate-identity\` is a fully non-interactive fresh setup.
4083
4230
 
4084
4231
  Options:
4085
4232
  --repo-id <id> repository id / NIP-34 d-tag (default: the existing
4086
4233
  toon.repoid, then the repo directory basename)
4234
+ --git-init when the cwd is not a git repo, run \`git init\` here
4235
+ first, then proceed (no prompt)
4236
+ --relay <url> relay to read your kind:0 profile from for the git
4237
+ author name (default: origin/toon.relay, then the
4238
+ genesis seed; skipped if none is resolvable)
4087
4239
  --generate-identity when no identity resolves, mint a fresh one (no prompt)
4088
4240
  --json machine-readable report
4089
4241
  -h, --help show this help`;
@@ -4092,6 +4244,8 @@ function parseInitArgs(args) {
4092
4244
  args,
4093
4245
  options: {
4094
4246
  "repo-id": { type: "string" },
4247
+ "git-init": { type: "boolean", default: false },
4248
+ relay: { type: "string" },
4095
4249
  "generate-identity": { type: "boolean", default: false },
4096
4250
  json: { type: "boolean", default: false },
4097
4251
  help: { type: "boolean", short: "h", default: false }
@@ -4102,6 +4256,7 @@ function parseInitArgs(args) {
4102
4256
  throw new Error(`rig init takes no positional arguments`);
4103
4257
  }
4104
4258
  const flags = {
4259
+ gitInit: values["git-init"] ?? false,
4105
4260
  generateIdentity: values["generate-identity"] ?? false,
4106
4261
  json: values.json ?? false,
4107
4262
  help: values.help ?? false
@@ -4111,8 +4266,46 @@ function parseInitArgs(args) {
4111
4266
  if (repoId.trim() === "") throw new Error("--repo-id must not be empty");
4112
4267
  flags.repoId = repoId;
4113
4268
  }
4269
+ const relay = values.relay;
4270
+ if (relay !== void 0) {
4271
+ if (relay.trim() === "") throw new Error("--relay must not be empty");
4272
+ flags.relay = relay.trim();
4273
+ }
4114
4274
  return flags;
4115
4275
  }
4276
+ function isWebSocketRelay2(url) {
4277
+ return /^wss?:\/\//i.test(url);
4278
+ }
4279
+ async function resolveProfileRelay(flagRelay, originRelay, toonRelays) {
4280
+ const candidates = [
4281
+ flagRelay,
4282
+ originRelay,
4283
+ ...toonRelays
4284
+ ].filter((u) => u !== void 0 && isWebSocketRelay2(u));
4285
+ if (candidates[0] !== void 0) return candidates[0];
4286
+ try {
4287
+ const { loadGenesisSeed } = await import("../network-bootstrap-WNSHRC5P.js");
4288
+ const seed = loadGenesisSeed();
4289
+ if (seed?.relayUrl && isWebSocketRelay2(seed.relayUrl)) return seed.relayUrl;
4290
+ } catch {
4291
+ }
4292
+ return void 0;
4293
+ }
4294
+ async function resolveOrInitGitRepo(deps, flags) {
4295
+ const { io: io2 } = deps;
4296
+ try {
4297
+ return { repoRoot: await resolveRepoRoot(deps.cwd), initialized: false };
4298
+ } catch {
4299
+ let doInit = flags.gitInit;
4300
+ if (!doInit && io2.isInteractive && !flags.json) {
4301
+ io2.err("Not a git repository \u2014 rig keeps its config and objects in one.");
4302
+ doInit = await io2.confirm("Initialize a git repository here? [y/N] ");
4303
+ }
4304
+ if (!doInit) throw new NotAGitRepositoryError(deps.cwd);
4305
+ await initGitRepository(deps.cwd);
4306
+ return { repoRoot: await resolveRepoRoot(deps.cwd), initialized: true };
4307
+ }
4308
+ }
4116
4309
  async function resolveOrGenerateIdentity(deps, flags) {
4117
4310
  const { io: io2, env } = deps;
4118
4311
  const resolve2 = deps.resolveIdentityImpl ?? resolveIdentity;
@@ -4158,12 +4351,7 @@ async function runInit(args, deps) {
4158
4351
  return 0;
4159
4352
  }
4160
4353
  try {
4161
- let repoRoot;
4162
- try {
4163
- repoRoot = await resolveRepoRoot(deps.cwd);
4164
- } catch {
4165
- throw new NotAGitRepositoryError(deps.cwd);
4166
- }
4354
+ const { repoRoot, initialized: initializedGitRepo } = await resolveOrInitGitRepo(deps, flags);
4167
4355
  const { identity, generated } = await resolveOrGenerateIdentity(deps, flags);
4168
4356
  const existing = await readToonConfig(repoRoot);
4169
4357
  const repoId = flags.repoId ?? existing.repoId ?? basename2(repoRoot);
@@ -4182,10 +4370,26 @@ async function runInit(args, deps) {
4182
4370
  }
4183
4371
  const origin = remotes.find((r) => r.name === "origin");
4184
4372
  const originRelay = origin !== void 0 && origin.urls.length === 1 && isRelayUrl(origin.urls[0]) ? origin.urls[0] : void 0;
4373
+ const profileRelay = await resolveProfileRelay(
4374
+ flags.relay,
4375
+ originRelay,
4376
+ existing.relays
4377
+ );
4378
+ const resolveAuthor = deps.resolveGitAuthorImpl ?? resolveGitAuthor;
4379
+ const gitAuthor = await resolveAuthor({
4380
+ pubkey: identity.pubkey,
4381
+ relayUrl: profileRelay,
4382
+ ...profileTimeoutFromEnv(deps.env) !== void 0 ? { timeoutMs: profileTimeoutFromEnv(deps.env) } : {}
4383
+ });
4384
+ await setGitAuthor(repoRoot, {
4385
+ name: gitAuthor.name,
4386
+ email: gitAuthor.email
4387
+ });
4185
4388
  if (flags.json) {
4186
4389
  const output = {
4187
4390
  command: "init",
4188
4391
  repoRoot,
4392
+ initializedGitRepo,
4189
4393
  repoId,
4190
4394
  owner: identity.pubkey,
4191
4395
  identity: {
@@ -4198,6 +4402,11 @@ async function runInit(args, deps) {
4198
4402
  remotes,
4199
4403
  origin: originRelay ?? null,
4200
4404
  migratedToonRelay,
4405
+ gitAuthor: {
4406
+ name: gitAuthor.name,
4407
+ email: gitAuthor.email,
4408
+ source: gitAuthor.source
4409
+ },
4201
4410
  changed,
4202
4411
  ...generated ? {
4203
4412
  generatedIdentity: {
@@ -4221,6 +4430,7 @@ async function runInit(args, deps) {
4221
4430
  for (const line of createdIdentityBanner(generated)) io2.out(line);
4222
4431
  if (generated.shadowedBy) io2.err(shadowNote(generated.shadowedBy));
4223
4432
  }
4433
+ if (initializedGitRepo) io2.out(`Created a git repository at ${repoRoot}`);
4224
4434
  io2.out(`Initialized rig for ${repoRoot}`);
4225
4435
  io2.out(renderIdentityLine(identity));
4226
4436
  io2.out(
@@ -4229,6 +4439,9 @@ async function runInit(args, deps) {
4229
4439
  io2.out(
4230
4440
  ` toon.owner = ${identity.pubkey}` + (changed.owner ? "" : " (unchanged)") + (existing.owner && changed.owner ? ` (was ${existing.owner})` : "")
4231
4441
  );
4442
+ io2.out(
4443
+ `Git author: ${gitAuthor.name} <${gitAuthor.email}> (from ${gitAuthor.source === "profile" ? "nostr profile" : "npub"})`
4444
+ );
4232
4445
  if (originRelay !== void 0) {
4233
4446
  io2.out(
4234
4447
  ` origin = ${originRelay}` + (migratedToonRelay ? " (migrated from git config toon.relay)" : "")