ledgit-cli 0.4.1 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +300 -156
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -48242,16 +48242,6 @@ async function getValidAccessToken(cwd = process.cwd()) {
48242
48242
  return null;
48243
48243
  }
48244
48244
  }
48245
- function deleteAuth0Token(cwd = process.cwd()) {
48246
- const localPath = getLocalTokenPath(cwd);
48247
- const globalPath = getGlobalTokenPath();
48248
- if (fs10.existsSync(localPath)) {
48249
- fs10.unlinkSync(localPath);
48250
- }
48251
- if (fs10.existsSync(globalPath)) {
48252
- fs10.unlinkSync(globalPath);
48253
- }
48254
- }
48255
48245
 
48256
48246
  // src/lib/env.ts
48257
48247
  async function loadConfig(cwd = process.cwd()) {
@@ -48287,7 +48277,10 @@ async function loadConfig(cwd = process.cwd()) {
48287
48277
  if (!companyId) {
48288
48278
  throw new Error("BOKIO_COMPANY_ID environment variable not set");
48289
48279
  }
48290
- const inboxProvider = process.env.INBOX_PROVIDER;
48280
+ let inboxProvider = process.env.INBOX_PROVIDER;
48281
+ if (!inboxProvider && process.env.LEDGIT_API_TOKEN) {
48282
+ inboxProvider = "ledgit";
48283
+ }
48291
48284
  const result = {
48292
48285
  provider: "bokio",
48293
48286
  bokio: { token, companyId }
@@ -48296,6 +48289,10 @@ async function loadConfig(cwd = process.cwd()) {
48296
48289
  result.inboxProvider = "ledgit";
48297
48290
  result.ledgit = {
48298
48291
  getAccessToken: async () => {
48292
+ const apiToken = process.env.LEDGIT_API_TOKEN;
48293
+ if (apiToken) {
48294
+ return apiToken;
48295
+ }
48299
48296
  const accessToken = await getValidAccessToken(cwd);
48300
48297
  if (!accessToken) {
48301
48298
  throw new Error("Ledgit authentication required for inbox. Run 'ledgit login' to authenticate.");
@@ -48310,6 +48307,10 @@ async function loadConfig(cwd = process.cwd()) {
48310
48307
  provider: "ledgit",
48311
48308
  ledgit: {
48312
48309
  getAccessToken: async () => {
48310
+ const apiToken = process.env.LEDGIT_API_TOKEN;
48311
+ if (apiToken) {
48312
+ return apiToken;
48313
+ }
48313
48314
  const token = await getValidAccessToken(cwd);
48314
48315
  if (!token) {
48315
48316
  throw new Error("Ledgit authentication required. Run 'ledgit login' to authenticate.");
@@ -56393,80 +56394,131 @@ async function loginCommand(options) {
56393
56394
  }
56394
56395
 
56395
56396
  // src/commands/token.ts
56396
- async function tokenCommand(action = "show", options = {}) {
56397
+ var API_BASE_URL = process.env.LEDGIT_API_URL || "https://api.ledgit.se";
56398
+ async function tokenCommand(action = "list", options = {}) {
56397
56399
  const cwd = process.cwd();
56400
+ const accessToken = await getValidAccessToken(cwd);
56401
+ if (!accessToken) {
56402
+ console.error(`
56403
+ Not logged in. Run 'ledgit login' first.
56404
+ `);
56405
+ process.exit(1);
56406
+ }
56398
56407
  switch (action) {
56399
- case "show": {
56400
- const token = loadAuth0Token(cwd);
56401
- if (!token) {
56402
- console.log(`
56403
- No Ledgit authentication token found.`);
56404
- console.log(` Run 'ledgit login' to authenticate.
56408
+ case "list": {
56409
+ const response = await fetch(`${API_BASE_URL}/api/tokens`, {
56410
+ headers: {
56411
+ Authorization: `Bearer ${accessToken}`
56412
+ }
56413
+ });
56414
+ if (!response.ok) {
56415
+ const error = await response.json().catch(() => ({}));
56416
+ console.error(`
56417
+ Failed to fetch tokens: ${error.error || response.statusText} (${response.status})
56405
56418
  `);
56406
56419
  process.exit(1);
56407
56420
  }
56408
- const expired = isTokenExpired2(token);
56409
- const expiresAt = new Date(token.expires_at);
56410
- console.log(`
56411
- Ledgit Token Info:`);
56412
- console.log(` Status: ${expired ? "Expired" : "Valid"}`);
56413
- console.log(` Expires: ${expiresAt.toISOString()}`);
56414
- console.log(` Has Refresh Token: ${token.refresh_token ? "Yes" : "No"}`);
56415
- console.log("");
56416
- if (expired) {
56417
- console.log(` Token is expired. Run 'ledgit token refresh' or 'ledgit login' to get a new token.
56421
+ const result = await response.json();
56422
+ const tokens = result.data;
56423
+ if (tokens.length === 0) {
56424
+ console.log(`
56425
+ No API tokens found.`);
56426
+ console.log(` Create one with: ledgit token create
56418
56427
  `);
56428
+ return;
56419
56429
  }
56420
- break;
56421
- }
56422
- case "export": {
56423
- const accessToken = await getValidAccessToken(cwd);
56424
- if (!accessToken) {
56425
- console.error("No valid token available. Run 'ledgit login' first.");
56426
- process.exit(1);
56430
+ console.log(`
56431
+ API Tokens:
56432
+ `);
56433
+ for (const token of tokens) {
56434
+ const lastUsed = token.lastUsedAt ? new Date(token.lastUsedAt).toLocaleDateString() : "Never";
56435
+ console.log(` ${token.name}`);
56436
+ console.log(` Token: ${token.tokenPrefix}...`);
56437
+ console.log(` Scopes: ${token.scopes.join(", ")}`);
56438
+ console.log(` Last used: ${lastUsed}`);
56439
+ console.log("");
56427
56440
  }
56428
- process.stdout.write(accessToken);
56429
56441
  break;
56430
56442
  }
56431
- case "refresh": {
56432
- const token = loadAuth0Token(cwd);
56433
- if (!token) {
56434
- console.log(`
56435
- No Ledgit authentication token found.`);
56436
- console.log(` Run 'ledgit login' to authenticate.
56443
+ case "create": {
56444
+ const name = await dist_default4({
56445
+ message: "Token name:",
56446
+ default: "GitHub Actions"
56447
+ });
56448
+ const scopes = ["read", "write"];
56449
+ console.log(`
56450
+ Creating API token...`);
56451
+ const response = await fetch(`${API_BASE_URL}/api/tokens`, {
56452
+ method: "POST",
56453
+ headers: {
56454
+ Authorization: `Bearer ${accessToken}`,
56455
+ "Content-Type": "application/json"
56456
+ },
56457
+ body: JSON.stringify({ name, scopes })
56458
+ });
56459
+ if (!response.ok) {
56460
+ const error = await response.json().catch(() => ({}));
56461
+ console.error(`
56462
+ Failed to create token: ${error.error || response.statusText}
56437
56463
  `);
56438
56464
  process.exit(1);
56439
56465
  }
56440
- if (!token.refresh_token) {
56441
- console.log(`
56442
- No refresh token available.`);
56443
- console.log(` Run 'ledgit login' to get a new token.
56466
+ const result = await response.json();
56467
+ console.log(`
56468
+ API token created successfully!
56444
56469
  `);
56445
- process.exit(1);
56446
- }
56447
- console.log(" Refreshing token...");
56448
- const accessToken = await getValidAccessToken(cwd);
56449
- if (accessToken) {
56450
- console.log(` Token refreshed successfully!
56470
+ console.log(" Token (copy now - it won't be shown again):");
56471
+ console.log(`
56472
+ ${result.data.token}
56451
56473
  `);
56452
- } else {
56453
- console.error(` Failed to refresh token. Run 'ledgit login' to re-authenticate.
56474
+ console.log(" To use in GitHub Actions, add as a secret:");
56475
+ console.log(` gh secret set LEDGIT_API_TOKEN
56454
56476
  `);
56455
- process.exit(1);
56456
- }
56457
56477
  break;
56458
56478
  }
56459
56479
  case "revoke": {
56460
- const token = loadAuth0Token(cwd);
56461
- if (!token) {
56480
+ const listResponse = await fetch(`${API_BASE_URL}/api/tokens`, {
56481
+ headers: {
56482
+ Authorization: `Bearer ${accessToken}`
56483
+ }
56484
+ });
56485
+ if (!listResponse.ok) {
56486
+ const error = await listResponse.json().catch(() => ({}));
56487
+ console.error(`
56488
+ Failed to fetch tokens: ${error.error || listResponse.statusText} (${listResponse.status})
56489
+ `);
56490
+ process.exit(1);
56491
+ }
56492
+ const listResult = await listResponse.json();
56493
+ const tokens = listResult.data;
56494
+ if (tokens.length === 0) {
56462
56495
  console.log(`
56463
- No Ledgit authentication token found.
56496
+ No API tokens to revoke.
56464
56497
  `);
56465
56498
  return;
56466
56499
  }
56467
- deleteAuth0Token(cwd);
56500
+ const tokenId = await dist_default8({
56501
+ message: "Select token to revoke:",
56502
+ choices: tokens.map((t) => ({
56503
+ value: t.id,
56504
+ name: `${t.name} (${t.tokenPrefix}...)`
56505
+ }))
56506
+ });
56507
+ const response = await fetch(`${API_BASE_URL}/api/tokens/${tokenId}`, {
56508
+ method: "DELETE",
56509
+ headers: {
56510
+ Authorization: `Bearer ${accessToken}`
56511
+ }
56512
+ });
56513
+ if (!response.ok) {
56514
+ const error = await response.json().catch(() => ({}));
56515
+ console.error(`
56516
+ Failed to revoke token: ${error.error || response.statusText} (${response.status})
56517
+ `);
56518
+ process.exit(1);
56519
+ }
56468
56520
  console.log(`
56469
- Ledgit token has been revoked/deleted.
56521
+ Token revoked.
56470
56522
  `);
56471
56523
  break;
56472
56524
  }
@@ -56474,10 +56526,9 @@ async function tokenCommand(action = "show", options = {}) {
56474
56526
  console.error(`Unknown action: ${action}`);
56475
56527
  console.log(`
56476
56528
  Usage:`);
56477
- console.log(" ledgit token show - Display current token info");
56478
- console.log(" ledgit token export - Export token for CI/CD");
56479
- console.log(" ledgit token refresh - Refresh expired token");
56480
- console.log(" ledgit token revoke - Revoke/delete stored token");
56529
+ console.log(" ledgit token list - List all API tokens");
56530
+ console.log(" ledgit token create - Create a new API token");
56531
+ console.log(" ledgit token revoke - Revoke an API token");
56481
56532
  process.exit(1);
56482
56533
  }
56483
56534
  }
@@ -56516,6 +56567,7 @@ jobs:
56516
56567
  - name: Sync inbox
56517
56568
  run: npx ledgit-cli sync-inbox
56518
56569
  env:
56570
+ LEDGIT_API_TOKEN: \${{ secrets.LEDGIT_API_TOKEN }}
56519
56571
  BOKIO_TOKEN: \${{ secrets.BOKIO_TOKEN }}
56520
56572
  BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
56521
56573
 
@@ -56591,40 +56643,6 @@ jobs:
56591
56643
  --verbose \\
56592
56644
  --output-format stream-json \\
56593
56645
  2>&1 | tee /tmp/claude-output.json
56594
-
56595
- - name: Write job summary
56596
- if: always() && steps.check.outputs.has_new == 'true'
56597
- run: |
56598
- echo "## \uD83E\uDD16 Claude Bookkeeping Report" >> $GITHUB_STEP_SUMMARY
56599
- echo "" >> $GITHUB_STEP_SUMMARY
56600
-
56601
- # Show model from init
56602
- jq -r 'select(.type == "system" and .subtype == "init") | "**Model:** \\(.model // "claude")"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
56603
- echo "" >> $GITHUB_STEP_SUMMARY
56604
-
56605
- # Count tool uses
56606
- TOOL_COUNT=$(jq -s '[.[] | select(.type == "tool_use")] | length' /tmp/claude-output.json 2>/dev/null || echo "0")
56607
- echo "**Tool calls:** $TOOL_COUNT" >> $GITHUB_STEP_SUMMARY
56608
- echo "" >> $GITHUB_STEP_SUMMARY
56609
-
56610
- # List unique tools used
56611
- echo "### Tools Used" >> $GITHUB_STEP_SUMMARY
56612
- jq -r 'select(.type == "tool_use") | .name' /tmp/claude-output.json 2>/dev/null | sort | uniq -c | while read count name; do
56613
- echo "- $name ($count calls)" >> $GITHUB_STEP_SUMMARY
56614
- done
56615
- echo "" >> $GITHUB_STEP_SUMMARY
56616
-
56617
- # Final result
56618
- echo "### Result" >> $GITHUB_STEP_SUMMARY
56619
- jq -r 'select(.type == "result") | "**Status:** \\(.subtype // "completed")\\n**Duration:** \\((.duration_ms // 0) / 1000 | floor)s\\n**Turns:** \\(.num_turns // "N/A")\\n**Cost:** $\\(.total_cost_usd // 0 | tostring | .[0:6])"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
56620
-
56621
- - name: Upload Claude execution log
56622
- if: always() && steps.check.outputs.has_new == 'true'
56623
- uses: actions/upload-artifact@v4
56624
- with:
56625
- name: claude-execution-log
56626
- path: /tmp/claude-output.json
56627
- retention-days: 7
56628
56646
  `;
56629
56647
  var CLAUDE_BOOKKEEPING_WORKFLOW = `name: Claude Bookkeeping
56630
56648
  on:
@@ -56719,40 +56737,6 @@ jobs:
56719
56737
  --verbose \\
56720
56738
  --output-format stream-json \\
56721
56739
  2>&1 | tee /tmp/claude-output.json
56722
-
56723
- - name: Write job summary
56724
- if: always()
56725
- run: |
56726
- echo "## \uD83E\uDD16 Claude Response Report" >> $GITHUB_STEP_SUMMARY
56727
- echo "" >> $GITHUB_STEP_SUMMARY
56728
-
56729
- # Show model from init
56730
- jq -r 'select(.type == "system" and .subtype == "init") | "**Model:** \\(.model // "claude")"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
56731
- echo "" >> $GITHUB_STEP_SUMMARY
56732
-
56733
- # Count tool uses
56734
- TOOL_COUNT=$(jq -s '[.[] | select(.type == "tool_use")] | length' /tmp/claude-output.json 2>/dev/null || echo "0")
56735
- echo "**Tool calls:** $TOOL_COUNT" >> $GITHUB_STEP_SUMMARY
56736
- echo "" >> $GITHUB_STEP_SUMMARY
56737
-
56738
- # List unique tools used
56739
- echo "### Tools Used" >> $GITHUB_STEP_SUMMARY
56740
- jq -r 'select(.type == "tool_use") | .name' /tmp/claude-output.json 2>/dev/null | sort | uniq -c | while read count name; do
56741
- echo "- $name ($count calls)" >> $GITHUB_STEP_SUMMARY
56742
- done
56743
- echo "" >> $GITHUB_STEP_SUMMARY
56744
-
56745
- # Final result
56746
- echo "### Result" >> $GITHUB_STEP_SUMMARY
56747
- jq -r 'select(.type == "result") | "**Status:** \\(.subtype // "completed")\\n**Duration:** \\((.duration_ms // 0) / 1000 | floor)s\\n**Turns:** \\(.num_turns // "N/A")\\n**Cost:** $\\(.total_cost_usd // 0 | tostring | .[0:6])"' /tmp/claude-output.json >> $GITHUB_STEP_SUMMARY 2>/dev/null || true
56748
-
56749
- - name: Upload Claude execution log
56750
- if: always()
56751
- uses: actions/upload-artifact@v4
56752
- with:
56753
- name: claude-execution-log-pr-\${{ github.event.issue.number }}
56754
- path: /tmp/claude-output.json
56755
- retention-days: 7
56756
56740
  `;
56757
56741
  var SYNC_JOURNAL_WORKFLOW = `name: Sync Journal
56758
56742
  on:
@@ -56778,6 +56762,7 @@ jobs:
56778
56762
  - name: Sync to Bokio
56779
56763
  run: npx ledgit-cli sync-journal
56780
56764
  env:
56765
+ LEDGIT_API_TOKEN: \${{ secrets.LEDGIT_API_TOKEN }}
56781
56766
  BOKIO_TOKEN: \${{ secrets.BOKIO_TOKEN }}
56782
56767
  BOKIO_COMPANY_ID: \${{ secrets.BOKIO_COMPANY_ID }}
56783
56768
  `;
@@ -57216,6 +57201,23 @@ async function authenticateClaude(options = {}) {
57216
57201
 
57217
57202
  // src/lib/github.ts
57218
57203
  var execAsync3 = promisify7(exec7);
57204
+ async function saveToEnv(cwd, key, value) {
57205
+ const envPath = path15.join(cwd, ".env");
57206
+ let content = "";
57207
+ try {
57208
+ content = await fs19.readFile(envPath, "utf-8");
57209
+ } catch {
57210
+ }
57211
+ const regex2 = new RegExp(`^${key}=.*$`, "m");
57212
+ if (regex2.test(content)) {
57213
+ content = content.replace(regex2, `${key}=${value}`);
57214
+ } else {
57215
+ content = content.trimEnd() + `
57216
+ ${key}=${value}
57217
+ `;
57218
+ }
57219
+ await fs19.writeFile(envPath, content);
57220
+ }
57219
57221
  async function checkGhInstalled() {
57220
57222
  try {
57221
57223
  await execAsync3("which gh");
@@ -57241,6 +57243,22 @@ async function checkGhWorkflowScope() {
57241
57243
  return false;
57242
57244
  }
57243
57245
  }
57246
+ async function checkRepoAccess() {
57247
+ try {
57248
+ await execAsync3("gh repo view --json name");
57249
+ return true;
57250
+ } catch {
57251
+ return false;
57252
+ }
57253
+ }
57254
+ async function checkRepoSecretsAccess() {
57255
+ try {
57256
+ await execAsync3("gh api repos/{owner}/{repo}/actions/secrets/public-key");
57257
+ return true;
57258
+ } catch {
57259
+ return false;
57260
+ }
57261
+ }
57244
57262
  async function hasGitRemote(cwd) {
57245
57263
  try {
57246
57264
  const cmd = cwd ? `git -C "${cwd}" remote -v` : "git remote -v";
@@ -57350,6 +57368,7 @@ needed to manage GitHub Actions and secrets.
57350
57368
  To fix this, run:
57351
57369
  gh auth refresh -h github.com -s repo,workflow`);
57352
57370
  }
57371
+ const hasSecretsAccess = await checkRepoSecretsAccess();
57353
57372
  let config3;
57354
57373
  try {
57355
57374
  config3 = await loadConfig(cwd);
@@ -57385,6 +57404,17 @@ Fortnox support requires OAuth token handling in CI, coming soon.`);
57385
57404
  }
57386
57405
  const secretName = authMethod === "api_key" ? "ANTHROPIC_API_KEY" : "CLAUDE_CODE_OAUTH_TOKEN";
57387
57406
  let claudeToken = options.claudeToken;
57407
+ if (!claudeToken && authMethod === "oauth") {
57408
+ claudeToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
57409
+ if (claudeToken) {
57410
+ console.log(" Using existing CLAUDE_CODE_OAUTH_TOKEN from .env");
57411
+ }
57412
+ } else if (!claudeToken && authMethod === "api_key") {
57413
+ claudeToken = process.env.ANTHROPIC_API_KEY;
57414
+ if (claudeToken) {
57415
+ console.log(" Using existing ANTHROPIC_API_KEY from .env");
57416
+ }
57417
+ }
57388
57418
  if (!claudeToken) {
57389
57419
  if (authMethod === "api_key") {
57390
57420
  claudeToken = await dist_default6({
@@ -57406,6 +57436,8 @@ Starting Claude OAuth authentication...`);
57406
57436
  });
57407
57437
  spinner.succeed("Claude authentication successful!");
57408
57438
  claudeToken = result.token;
57439
+ await saveToEnv(cwd, "CLAUDE_CODE_OAUTH_TOKEN", claudeToken);
57440
+ console.log(" Saved token to .env");
57409
57441
  } catch (error) {
57410
57442
  spinner.fail("Claude authentication failed");
57411
57443
  throw error;
@@ -57415,21 +57447,52 @@ Starting Claude OAuth authentication...`);
57415
57447
  if (!claudeToken) {
57416
57448
  throw new Error("Token is required.");
57417
57449
  }
57418
- console.log(`
57450
+ if (hasSecretsAccess) {
57451
+ console.log(`
57419
57452
  Setting up GitHub secrets...`);
57420
- await setGhSecret(secretName, claudeToken);
57421
- console.log(` Set ${secretName}`);
57422
- await setGhSecret("BOKIO_TOKEN", config3.bokio.token);
57423
- console.log(" Set BOKIO_TOKEN");
57424
- await setGhSecret("BOKIO_COMPANY_ID", config3.bokio.companyId);
57425
- console.log(" Set BOKIO_COMPANY_ID");
57426
- try {
57427
- await enableActionsPrPermission();
57428
- console.log(" Enabled Actions to create pull requests");
57429
- } catch {
57430
- console.error(`
57453
+ await setGhSecret(secretName, claudeToken);
57454
+ console.log(` Set ${secretName}`);
57455
+ await setGhSecret("BOKIO_TOKEN", config3.bokio.token);
57456
+ console.log(" Set BOKIO_TOKEN");
57457
+ await setGhSecret("BOKIO_COMPANY_ID", config3.bokio.companyId);
57458
+ console.log(" Set BOKIO_COMPANY_ID");
57459
+ if (options.ledgitApiToken) {
57460
+ await setGhSecret("LEDGIT_API_TOKEN", options.ledgitApiToken);
57461
+ console.log(" Set LEDGIT_API_TOKEN");
57462
+ }
57463
+ try {
57464
+ await enableActionsPrPermission();
57465
+ console.log(" Enabled Actions to create pull requests");
57466
+ } catch {
57467
+ console.error(`
57431
57468
  Warning: Could not enable PR creation for Actions.`);
57432
- console.error("You may need to enable this manually in Settings > Actions > General");
57469
+ console.error("You may need to enable this manually in Settings > Actions > General");
57470
+ }
57471
+ } else {
57472
+ console.log(`
57473
+ ⚠️ Unable to set secrets via GitHub CLI.`);
57474
+ console.log(` This can happen if you don't have admin access to this repository.
57475
+ `);
57476
+ console.log(" To fix gh access and retry, run:");
57477
+ console.log(` gh auth refresh -h github.com -s repo,workflow
57478
+ `);
57479
+ console.log(" Or add these secrets manually in GitHub:");
57480
+ console.log(` Go to: Settings > Secrets and variables > Actions > New repository secret
57481
+ `);
57482
+ console.log(` ${secretName}:`);
57483
+ console.log(` ${claudeToken}
57484
+ `);
57485
+ console.log(" BOKIO_TOKEN:");
57486
+ console.log(` ${config3.bokio.token}
57487
+ `);
57488
+ console.log(" BOKIO_COMPANY_ID:");
57489
+ console.log(` ${config3.bokio.companyId}
57490
+ `);
57491
+ if (options.ledgitApiToken) {
57492
+ console.log(" LEDGIT_API_TOKEN:");
57493
+ console.log(` ${options.ledgitApiToken}
57494
+ `);
57495
+ }
57433
57496
  }
57434
57497
  console.log(`
57435
57498
  Creating workflow files...`);
@@ -57468,6 +57531,7 @@ Workflows created:`);
57468
57531
 
57469
57532
  // src/commands/setup-github.ts
57470
57533
  var execAsync4 = promisify8(exec8);
57534
+ var API_BASE_URL2 = process.env.LEDGIT_API_URL || "https://api.ledgit.se";
57471
57535
  async function setupGithubCommand() {
57472
57536
  const cwd = process.cwd();
57473
57537
  console.log(`Setting up GitHub Actions for automated bookkeeping...
@@ -57482,7 +57546,73 @@ async function setupGithubCommand() {
57482
57546
  console.log(` Using existing remote: ${repoUrl}
57483
57547
  `);
57484
57548
  }
57485
- await setupClaudeWorkflow({ cwd, autoCommit: false });
57549
+ let ghUser = "unknown";
57550
+ try {
57551
+ const { stdout } = await execAsync4("gh api user -q .login");
57552
+ ghUser = stdout.trim();
57553
+ } catch {
57554
+ }
57555
+ const hasRepoAccess = await checkRepoAccess();
57556
+ if (!hasRepoAccess) {
57557
+ throw new UserError(`Cannot access repository.
57558
+
57559
+ ` + `You are logged in as '${ghUser}' but this account doesn't have access to the repository.
57560
+
57561
+ ` + `To fix this:
57562
+ ` + ` 1. Check your gh accounts: gh auth status
57563
+ ` + ` 2. Switch account: gh auth switch -u <username>`);
57564
+ }
57565
+ const hasSecretsAccess = await checkRepoSecretsAccess();
57566
+ if (!hasSecretsAccess) {
57567
+ throw new UserError(`Cannot access repository secrets.
57568
+
57569
+ ` + `You are logged in as '${ghUser}' but don't have admin/write access to manage secrets.
57570
+
57571
+ ` + `To fix this:
57572
+ ` + ` 1. Ensure you have admin access to this repository
57573
+ ` + ` 2. Or refresh auth: gh auth refresh -h github.com -s repo,workflow`);
57574
+ }
57575
+ console.log(` Repository access verified (${ghUser})
57576
+ `);
57577
+ let ledgitApiToken;
57578
+ let config3;
57579
+ try {
57580
+ config3 = await loadConfig(cwd);
57581
+ } catch {
57582
+ }
57583
+ const needsLedgitToken = config3?.provider === "ledgit" || config3?.inboxProvider === "ledgit";
57584
+ if (needsLedgitToken) {
57585
+ const accessToken = await getValidAccessToken(cwd);
57586
+ if (accessToken) {
57587
+ console.log(" Creating Ledgit API token...");
57588
+ const response = await fetch(`${API_BASE_URL2}/api/tokens`, {
57589
+ method: "POST",
57590
+ headers: {
57591
+ Authorization: `Bearer ${accessToken}`,
57592
+ "Content-Type": "application/json"
57593
+ },
57594
+ body: JSON.stringify({
57595
+ name: "GitHub Actions",
57596
+ scopes: ["read", "write"]
57597
+ })
57598
+ });
57599
+ if (response.ok) {
57600
+ const result = await response.json();
57601
+ console.log(` ✓ Ledgit API token created
57602
+ `);
57603
+ ledgitApiToken = result.data.token;
57604
+ } else {
57605
+ console.log(" Could not create API token. You can create one later with:");
57606
+ console.log(` ledgit token create
57607
+ `);
57608
+ }
57609
+ } else {
57610
+ console.log(" Not logged in - skipping Ledgit API token creation.");
57611
+ console.log(` Run 'ledgit login' then 'ledgit token create' to create one.
57612
+ `);
57613
+ }
57614
+ }
57615
+ await setupClaudeWorkflow({ cwd, autoCommit: false, ledgitApiToken });
57486
57616
  try {
57487
57617
  await execAsync4("git add .github/workflows");
57488
57618
  const { stdout: status } = await execAsync4("git status --porcelain .github/workflows");
@@ -58352,6 +58482,13 @@ initialize({
58352
58482
  appName: "ledgit"
58353
58483
  });
58354
58484
  setVersion(pkg.version);
58485
+
58486
+ class UserError extends Error {
58487
+ constructor(message) {
58488
+ super(message);
58489
+ this.name = "UserError";
58490
+ }
58491
+ }
58355
58492
  function withTelemetry(command, action) {
58356
58493
  return async (...args) => {
58357
58494
  const start = Date.now();
@@ -58375,6 +58512,13 @@ function withTelemetry(command, action) {
58375
58512
  if (error instanceof Error) {
58376
58513
  trackError("cli_command", error, { command });
58377
58514
  }
58515
+ if (error instanceof UserError) {
58516
+ console.error(`
58517
+ Error: ${error.message}
58518
+ `);
58519
+ await shutdown();
58520
+ process.exit(1);
58521
+ }
58378
58522
  throw error;
58379
58523
  } finally {
58380
58524
  await shutdown();
@@ -58393,19 +58537,16 @@ program.command("generate-lines").description("Append journal lines to an existi
58393
58537
  program.command("list-tax-codes").description("List all available Swedish tax codes").action(withTelemetry("list-tax-codes", listTaxCodesCommand));
58394
58538
  program.command("discard <inbox-directory>").description("Remove an inbox item and mark for deletion from provider").action(withTelemetry("discard", discardCommand));
58395
58539
  program.command("login").description("Authenticate with Ledgit").option("-f, --force", "Overwrite existing token without prompting").action(withTelemetry("login", loginCommand));
58396
- program.command("token [action]").description("Manage Ledgit authentication tokens for CI/CD").addHelpText("after", `
58540
+ program.command("token [action]").description("Manage API tokens (PATs) for CI/CD").addHelpText("after", `
58397
58541
  Actions:
58398
- show Display current token info (default)
58399
- export Export token for CI/CD (outputs to stdout)
58400
- refresh Refresh expired token
58401
- revoke Revoke/delete stored token
58542
+ list List all API tokens (default)
58543
+ create Create a new API token
58544
+ revoke Revoke an API token
58402
58545
 
58403
58546
  Examples:
58404
58547
  $ ledgit token
58405
- $ ledgit token show
58406
- $ ledgit token export
58407
- $ LEDGIT_TOKEN=$(ledgit token export)
58408
- $ ledgit token refresh
58548
+ $ ledgit token list
58549
+ $ ledgit token create
58409
58550
  $ ledgit token revoke
58410
58551
  `).action(withTelemetry("token", tokenCommand));
58411
58552
  program.command("update").description("Update AGENTS.md to the latest version").option("-f, --force", "Overwrite without prompting if modified").action(withTelemetry("update", updateCommand));
@@ -58442,3 +58583,6 @@ Examples:
58442
58583
  `).action(withTelemetry("traktamente:create", traktamenteCommand));
58443
58584
  traktamente.command("list").description("List available countries and traktamente rates").option("-y, --year <year>", "Year for rates (default: current year)").action(withTelemetry("traktamente:list", listCountriesCommand));
58444
58585
  program.parse();
58586
+ export {
58587
+ UserError
58588
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ledgit-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "CLI for ledgit bookkeeping repositories",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,10 +24,10 @@
24
24
  "@inquirer/prompts": "^8.1.0",
25
25
  "@moatless/api-client": "^0.1.2",
26
26
  "@moatless/bokio-client": "^0.1.2",
27
- "@moatless/bookkeeping": "^0.2.0",
27
+ "@moatless/bookkeeping": "^0.2.1",
28
28
  "@moatless/bookkeeping-types": "^0.2.0",
29
29
  "@moatless/fortnox-client": "^0.1.2",
30
- "@moatless/ledgit-client": "^0.2.0",
30
+ "@moatless/ledgit-client": "^0.2.1",
31
31
  "@moatless/riksbank-client": "^0.1.2",
32
32
  "@moatless/telemetry": "^0.1.2",
33
33
  "chalk": "^5.6.2",