@route_33/opencode 0.4.7-rc.10 → 0.4.7-rc.11

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.
@@ -899,6 +899,10 @@ var leaseForAttribution = async (options) => {
899
899
  }
900
900
  };
901
901
 
902
+ // src/attribution/read-legacy-key/read-legacy-key.ts
903
+ import * as path from "node:path";
904
+ var LEGACY_KEY_RELATIVE_PATH = path.join(".opencode", "route33.key");
905
+
902
906
  // src/attribution/parse-attribution/parse-attribution.ts
903
907
  var parseAttribution = (value) => {
904
908
  if (!isRecord(value)) {
@@ -913,6 +917,10 @@ var parseAttribution = (value) => {
913
917
  throw new ContractParseError("invalid attribution");
914
918
  };
915
919
 
920
+ // src/attribution/remove-legacy-key/remove-legacy-key.ts
921
+ import * as path2 from "node:path";
922
+ var LEGACY_KEY_RELATIVE_PATH2 = path2.join(".opencode", "route33.key");
923
+
916
924
  // src/key-lease/lease-state/lease-state.ts
917
925
  var LeaseState = class _LeaseState {
918
926
  /** Current committed snapshot; mutation is limited to compare-and-swap transitions. */
package/dist/index.js CHANGED
@@ -2687,6 +2687,36 @@ var clearAttribution = (options) => {
2687
2687
  return true;
2688
2688
  };
2689
2689
 
2690
+ // src/attribution/read-legacy-key/read-legacy-key.ts
2691
+ import * as fs10 from "node:fs";
2692
+ import * as path10 from "node:path";
2693
+ var LEGACY_KEY_RELATIVE_PATH = path10.join(".opencode", "route33.key");
2694
+ var readLegacyKey = (worktree) => {
2695
+ if (worktree.trim() === "") return void 0;
2696
+ let raw;
2697
+ try {
2698
+ raw = fs10.readFileSync(
2699
+ path10.join(worktree, LEGACY_KEY_RELATIVE_PATH),
2700
+ "utf8"
2701
+ );
2702
+ } catch {
2703
+ return void 0;
2704
+ }
2705
+ try {
2706
+ const parsed = JSON.parse(raw);
2707
+ if (typeof parsed !== "object" || parsed === null || parsed.type !== "api") {
2708
+ return void 0;
2709
+ }
2710
+ const key = parsed.key;
2711
+ if (typeof key !== "string" || key.trim() === "") {
2712
+ return void 0;
2713
+ }
2714
+ return { value: key };
2715
+ } catch {
2716
+ return void 0;
2717
+ }
2718
+ };
2719
+
2690
2720
  // src/attribution/parse-attribution/parse-attribution.ts
2691
2721
  var parseAttribution = (value) => {
2692
2722
  if (!isRecord(value)) {
@@ -2702,37 +2732,50 @@ var parseAttribution = (value) => {
2702
2732
  };
2703
2733
 
2704
2734
  // src/attribution/read-attribution/read-attribution.ts
2705
- import * as fs10 from "node:fs";
2735
+ import * as fs11 from "node:fs";
2706
2736
  var readAttribution = (options) => {
2707
2737
  const file = resolveAttributionFile(options);
2708
2738
  if (file === void 0) {
2709
2739
  return void 0;
2710
2740
  }
2711
2741
  try {
2712
- const parsed = JSON.parse(fs10.readFileSync(file, "utf8"));
2742
+ const parsed = JSON.parse(fs11.readFileSync(file, "utf8"));
2713
2743
  return parseAttribution(parsed);
2714
2744
  } catch {
2715
2745
  return void 0;
2716
2746
  }
2717
2747
  };
2718
2748
 
2749
+ // src/attribution/remove-legacy-key/remove-legacy-key.ts
2750
+ import * as fs12 from "node:fs";
2751
+ import * as path11 from "node:path";
2752
+ var LEGACY_KEY_RELATIVE_PATH2 = path11.join(".opencode", "route33.key");
2753
+ var removeLegacyKey = (worktree) => {
2754
+ try {
2755
+ fs12.rmSync(path11.join(worktree, LEGACY_KEY_RELATIVE_PATH2), { force: true });
2756
+ return true;
2757
+ } catch {
2758
+ return false;
2759
+ }
2760
+ };
2761
+
2719
2762
  // src/attribution/write-attribution/write-attribution.ts
2720
2763
  import { randomUUID as randomUUID2 } from "node:crypto";
2721
- import * as fs11 from "node:fs";
2722
- import * as path10 from "node:path";
2764
+ import * as fs13 from "node:fs";
2765
+ import * as path12 from "node:path";
2723
2766
  var writeAttribution = (options, attribution) => {
2724
2767
  const file = resolveAttributionFile(options);
2725
2768
  if (file === void 0) {
2726
2769
  return false;
2727
2770
  }
2728
- fs11.mkdirSync(path10.dirname(file), { recursive: true });
2771
+ fs13.mkdirSync(path12.dirname(file), { recursive: true });
2729
2772
  const temporaryFile = `${file}.${String(process.pid)}.${randomUUID2()}.tmp`;
2730
2773
  try {
2731
- fs11.writeFileSync(temporaryFile, `${JSON.stringify(attribution)}
2774
+ fs13.writeFileSync(temporaryFile, `${JSON.stringify(attribution)}
2732
2775
  `, "utf8");
2733
- fs11.renameSync(temporaryFile, file);
2776
+ fs13.renameSync(temporaryFile, file);
2734
2777
  } catch (error) {
2735
- fs11.rmSync(temporaryFile, { force: true });
2778
+ fs13.rmSync(temporaryFile, { force: true });
2736
2779
  throw error;
2737
2780
  }
2738
2781
  return true;
@@ -3970,7 +4013,8 @@ var toHostModels = (input) => {
3970
4013
  options: {},
3971
4014
  providerID: "route_33",
3972
4015
  release_date: "",
3973
- status: "active"
4016
+ status: "active",
4017
+ ...meta.variants === void 0 ? {} : { variants: structuredClone(meta.variants) }
3974
4018
  };
3975
4019
  }
3976
4020
  return models;
@@ -4154,8 +4198,10 @@ export {
4154
4198
  parseTokenSet,
4155
4199
  pkceChallenge,
4156
4200
  readAttribution,
4201
+ readLegacyKey,
4157
4202
  recoverLeaseState,
4158
4203
  refreshSession,
4204
+ removeLegacyKey,
4159
4205
  renewKey,
4160
4206
  resetAttribution,
4161
4207
  resolveAttribution,
@@ -5381,6 +5381,50 @@ var init_clear_attribution2 = __esm({
5381
5381
  }
5382
5382
  });
5383
5383
 
5384
+ // src/attribution/read-legacy-key/read-legacy-key.ts
5385
+ import * as fs10 from "node:fs";
5386
+ import * as path10 from "node:path";
5387
+ var LEGACY_KEY_RELATIVE_PATH, readLegacyKey;
5388
+ var init_read_legacy_key = __esm({
5389
+ "src/attribution/read-legacy-key/read-legacy-key.ts"() {
5390
+ "use strict";
5391
+ LEGACY_KEY_RELATIVE_PATH = path10.join(".opencode", "route33.key");
5392
+ readLegacyKey = (worktree) => {
5393
+ if (worktree.trim() === "") return void 0;
5394
+ let raw;
5395
+ try {
5396
+ raw = fs10.readFileSync(
5397
+ path10.join(worktree, LEGACY_KEY_RELATIVE_PATH),
5398
+ "utf8"
5399
+ );
5400
+ } catch {
5401
+ return void 0;
5402
+ }
5403
+ try {
5404
+ const parsed = JSON.parse(raw);
5405
+ if (typeof parsed !== "object" || parsed === null || parsed.type !== "api") {
5406
+ return void 0;
5407
+ }
5408
+ const key = parsed.key;
5409
+ if (typeof key !== "string" || key.trim() === "") {
5410
+ return void 0;
5411
+ }
5412
+ return { value: key };
5413
+ } catch {
5414
+ return void 0;
5415
+ }
5416
+ };
5417
+ }
5418
+ });
5419
+
5420
+ // src/attribution/read-legacy-key/index.ts
5421
+ var init_read_legacy_key2 = __esm({
5422
+ "src/attribution/read-legacy-key/index.ts"() {
5423
+ "use strict";
5424
+ init_read_legacy_key();
5425
+ }
5426
+ });
5427
+
5384
5428
  // src/attribution/parse-attribution/parse-attribution.ts
5385
5429
  var parseAttribution;
5386
5430
  var init_parse_attribution = __esm({
@@ -5412,7 +5456,7 @@ var init_parse_attribution2 = __esm({
5412
5456
  });
5413
5457
 
5414
5458
  // src/attribution/read-attribution/read-attribution.ts
5415
- import * as fs10 from "node:fs";
5459
+ import * as fs11 from "node:fs";
5416
5460
  var readAttribution;
5417
5461
  var init_read_attribution = __esm({
5418
5462
  "src/attribution/read-attribution/read-attribution.ts"() {
@@ -5425,7 +5469,7 @@ var init_read_attribution = __esm({
5425
5469
  return void 0;
5426
5470
  }
5427
5471
  try {
5428
- const parsed = JSON.parse(fs10.readFileSync(file, "utf8"));
5472
+ const parsed = JSON.parse(fs11.readFileSync(file, "utf8"));
5429
5473
  return parseAttribution(parsed);
5430
5474
  } catch {
5431
5475
  return void 0;
@@ -5442,10 +5486,37 @@ var init_read_attribution2 = __esm({
5442
5486
  }
5443
5487
  });
5444
5488
 
5489
+ // src/attribution/remove-legacy-key/remove-legacy-key.ts
5490
+ import * as fs12 from "node:fs";
5491
+ import * as path11 from "node:path";
5492
+ var LEGACY_KEY_RELATIVE_PATH2, removeLegacyKey;
5493
+ var init_remove_legacy_key = __esm({
5494
+ "src/attribution/remove-legacy-key/remove-legacy-key.ts"() {
5495
+ "use strict";
5496
+ LEGACY_KEY_RELATIVE_PATH2 = path11.join(".opencode", "route33.key");
5497
+ removeLegacyKey = (worktree) => {
5498
+ try {
5499
+ fs12.rmSync(path11.join(worktree, LEGACY_KEY_RELATIVE_PATH2), { force: true });
5500
+ return true;
5501
+ } catch {
5502
+ return false;
5503
+ }
5504
+ };
5505
+ }
5506
+ });
5507
+
5508
+ // src/attribution/remove-legacy-key/index.ts
5509
+ var init_remove_legacy_key2 = __esm({
5510
+ "src/attribution/remove-legacy-key/index.ts"() {
5511
+ "use strict";
5512
+ init_remove_legacy_key();
5513
+ }
5514
+ });
5515
+
5445
5516
  // src/attribution/write-attribution/write-attribution.ts
5446
5517
  import { randomUUID as randomUUID2 } from "node:crypto";
5447
- import * as fs11 from "node:fs";
5448
- import * as path10 from "node:path";
5518
+ import * as fs13 from "node:fs";
5519
+ import * as path12 from "node:path";
5449
5520
  var writeAttribution;
5450
5521
  var init_write_attribution = __esm({
5451
5522
  "src/attribution/write-attribution/write-attribution.ts"() {
@@ -5456,14 +5527,14 @@ var init_write_attribution = __esm({
5456
5527
  if (file === void 0) {
5457
5528
  return false;
5458
5529
  }
5459
- fs11.mkdirSync(path10.dirname(file), { recursive: true });
5530
+ fs13.mkdirSync(path12.dirname(file), { recursive: true });
5460
5531
  const temporaryFile = `${file}.${String(process.pid)}.${randomUUID2()}.tmp`;
5461
5532
  try {
5462
- fs11.writeFileSync(temporaryFile, `${JSON.stringify(attribution)}
5533
+ fs13.writeFileSync(temporaryFile, `${JSON.stringify(attribution)}
5463
5534
  `, "utf8");
5464
- fs11.renameSync(temporaryFile, file);
5535
+ fs13.renameSync(temporaryFile, file);
5465
5536
  } catch (error) {
5466
- fs11.rmSync(temporaryFile, { force: true });
5537
+ fs13.rmSync(temporaryFile, { force: true });
5467
5538
  throw error;
5468
5539
  }
5469
5540
  return true;
@@ -5485,8 +5556,10 @@ var init_attribution = __esm({
5485
5556
  "use strict";
5486
5557
  init_clear_attribution2();
5487
5558
  init_detect_worktree_root2();
5559
+ init_read_legacy_key2();
5488
5560
  init_parse_attribution2();
5489
5561
  init_read_attribution2();
5562
+ init_remove_legacy_key2();
5490
5563
  init_resolve_attribution_file2();
5491
5564
  init_write_attribution2();
5492
5565
  }
@@ -7209,6 +7282,26 @@ init_attribution();
7209
7282
  // src/tui-entry/v2-autoconnect/v2-autoconnect.ts
7210
7283
  init_attribution();
7211
7284
 
7285
+ // src/tui-entry/v2-connect-copy/v2-connect-copy.ts
7286
+ var V2_CONNECT_COPY = {
7287
+ dismissed: "Route 33 is not set up for this folder yet. Press Enter to pick the budget, or run /connect and choose Route 33.",
7288
+ expired: "The Route 33 sign-in link expired. Press Enter to retry, or run /connect and choose Route 33.",
7289
+ manualFallback: "Route 33 is not connected in this folder. Run /connect, choose Route 33, and follow the steps.",
7290
+ manualPath: "Press Enter to retry, or run /connect, choose Route 33, and follow the steps.",
7291
+ pickerTitle: "Which budget covers Route 33 usage in this folder?",
7292
+ setupIncomplete: "Route 33 setup did not complete. Press Enter to retry, or run /connect and choose Route 33.",
7293
+ signInFailed: "Route 33 sign-in did not complete. Press Enter to retry, or run /connect and choose Route 33.",
7294
+ legacyKeyConverted: "Route 33 key converted \u2713 \u2014 signing in to mint a fresh key for this folder.",
7295
+ legacyKeyMessage: "A Route 33 key from a previous version was found in this folder. Convert it to your personal budget?",
7296
+ legacyKeyTitle: "Route 33 \u2014 key from a previous version",
7297
+ signInActions: "Finish the sign-in in your browser \u2014 press o to open it, c to copy the link.",
7298
+ signInTitle: "Route 33 \u2014 sign in",
7299
+ statusUnavailable: "Route 33 sign-in did not settle. Press Enter to retry, or run /connect and choose Route 33.",
7300
+ stale: "That Route 33 option is no longer available. Press Enter to retry, or run /connect and choose Route 33.",
7301
+ success: "Route 33 connected \u2713 \u2014 pick a route_33 model and start.",
7302
+ timedOut: "Route 33 sign-in timed out. Press Enter to retry, or run /connect and choose Route 33."
7303
+ };
7304
+
7212
7305
  // src/tui-entry/v2-attribution-field/v2-attribution-field.ts
7213
7306
  var v2AttributionField = (methods) => {
7214
7307
  const browserMethods = methods?.filter(
@@ -7236,23 +7329,6 @@ var v2AttributionForm = async (context) => {
7236
7329
  return v2AttributionField(integration?.data.methods);
7237
7330
  };
7238
7331
 
7239
- // src/tui-entry/v2-connect-copy/v2-connect-copy.ts
7240
- var V2_CONNECT_COPY = {
7241
- dismissed: "Route 33 is not set up for this folder yet. Press Enter to pick the budget, or run /connect and choose Route 33.",
7242
- expired: "The Route 33 sign-in link expired. Press Enter to retry, or run /connect and choose Route 33.",
7243
- manualFallback: "Route 33 is not connected in this folder. Run /connect, choose Route 33, and follow the steps.",
7244
- manualPath: "Press Enter to retry, or run /connect, choose Route 33, and follow the steps.",
7245
- pickerTitle: "Which budget covers Route 33 usage in this folder?",
7246
- setupIncomplete: "Route 33 setup did not complete. Press Enter to retry, or run /connect and choose Route 33.",
7247
- signInFailed: "Route 33 sign-in did not complete. Press Enter to retry, or run /connect and choose Route 33.",
7248
- signInOption: "Sign in with your browser \u2014 press o to open it, c to copy the link.",
7249
- signInTitle: "Route 33 \u2014 sign in",
7250
- statusUnavailable: "Route 33 sign-in did not settle. Press Enter to retry, or run /connect and choose Route 33.",
7251
- stale: "That Route 33 option is no longer available. Press Enter to retry, or run /connect and choose Route 33.",
7252
- success: "Route 33 connected \u2713 \u2014 pick a route_33 model and start.",
7253
- timedOut: "Route 33 sign-in timed out. Press Enter to retry, or run /connect and choose Route 33."
7254
- };
7255
-
7256
7332
  // src/tui-entry/v2-outcome-copy/v2-outcome-copy.ts
7257
7333
  var v2OutcomeCopy = (outcome, failedFallback) => {
7258
7334
  if (outcome.kind === "failed") {
@@ -7400,50 +7476,12 @@ var v2ConnectRetry = async ({
7400
7476
  };
7401
7477
 
7402
7478
  // src/tui-entry/v2-sign-in-dialog/v2-sign-in-dialog.ts
7403
- init_open_system_browser2();
7404
-
7405
- // src/tui-entry/copy-to-clipboard/copy-to-clipboard.ts
7406
- import { spawn as spawn2 } from "node:child_process";
7407
- var copyToClipboard = async (text, options = {}) => new Promise((resolve2) => {
7408
- let command = "xclip";
7409
- if (process.platform === "darwin") {
7410
- command = "pbcopy";
7411
- } else if (process.platform === "win32") {
7412
- command = "clip";
7413
- }
7414
- const args = process.platform === "linux" ? ["-selection", "clipboard"] : [];
7415
- const spawnUrl = options.spawnUrl ?? ((cmd, opts) => spawn2(cmd, args, opts));
7416
- try {
7417
- const child = spawnUrl(command, { stdio: "pipe" });
7418
- child.stdin.write(text);
7419
- child.stdin.end();
7420
- resolve2(true);
7421
- } catch {
7422
- resolve2(false);
7423
- }
7424
- });
7479
+ var v2SignInDialog = ({ attempt, dialog }) => () => dialog.alert({
7480
+ message: `${attempt.instructions}
7425
7481
 
7426
- // src/tui-entry/v2-sign-in-dialog/v2-sign-in-dialog.ts
7427
- var v2SignInDialog = ({ attempt, dialog }) => () => dialog.select({
7428
- actions: [
7429
- {
7430
- bind: "o",
7431
- onTrigger: () => {
7432
- void openSystemBrowser(new URL(attempt.url));
7433
- },
7434
- title: "Open in browser"
7435
- },
7436
- {
7437
- bind: "c",
7438
- onTrigger: () => {
7439
- void copyToClipboard(attempt.url);
7440
- },
7441
- title: "Copy sign-in link"
7442
- }
7443
- ],
7444
- options: [{ title: V2_CONNECT_COPY.signInOption, value: "sign-in" }],
7482
+ ${V2_CONNECT_COPY.signInActions}`,
7445
7483
  title: V2_CONNECT_COPY.signInTitle
7446
- });
7484
+ }).then(() => void 0);
7447
7485
 
7448
7486
  // src/tui-entry/v2-sign-in-outcome/v2-sign-in-outcome.ts
7449
7487
  var v2SignInOutcome = async ({
@@ -7498,6 +7536,7 @@ var v2SignInPass = async ({
7498
7536
  formReader,
7499
7537
  locationInput,
7500
7538
  maxAttempts,
7539
+ onAttempt,
7501
7540
  pollDelayMs = 1e3,
7502
7541
  pollTerminal,
7503
7542
  retryDelayMs,
@@ -7527,26 +7566,31 @@ var v2SignInPass = async ({
7527
7566
  toast({ message: V2_CONNECT_COPY.manualFallback, variant: "error" });
7528
7567
  return { aborted: true };
7529
7568
  }
7530
- const waited = await v2SignInWait({
7531
- attemptID: attempt.data.attemptID,
7532
- pollTerminal,
7533
- showDelayMs: pollDelayMs + 100,
7534
- showDialog: v2SignInDialog({ attempt: attempt.data, dialog })
7535
- });
7536
- if (waited.dismissed) {
7537
- if (waited.outcome.kind === "complete") {
7538
- return { connected: true };
7569
+ onAttempt?.(attempt.data);
7570
+ try {
7571
+ const waited = await v2SignInWait({
7572
+ attemptID: attempt.data.attemptID,
7573
+ pollTerminal,
7574
+ showDelayMs: pollDelayMs + 100,
7575
+ showDialog: v2SignInDialog({ attempt: attempt.data, dialog })
7576
+ });
7577
+ if (waited.dismissed) {
7578
+ if (waited.outcome.kind === "complete") {
7579
+ return { connected: true };
7580
+ }
7581
+ toast({ message: V2_CONNECT_COPY.dismissed });
7582
+ return { aborted: true };
7539
7583
  }
7540
- toast({ message: V2_CONNECT_COPY.dismissed });
7541
- return { aborted: true };
7584
+ dialog.clear();
7585
+ return await v2SignInOutcome({
7586
+ formReader,
7587
+ outcome: waited.outcome,
7588
+ toast,
7589
+ unattributed
7590
+ });
7591
+ } finally {
7592
+ onAttempt?.(void 0);
7542
7593
  }
7543
- dialog.clear();
7544
- return v2SignInOutcome({
7545
- formReader,
7546
- outcome: waited.outcome,
7547
- toast,
7548
- unattributed
7549
- });
7550
7594
  };
7551
7595
 
7552
7596
  // src/tui-entry/v2-status-surface/v2-status-surface.ts
@@ -7599,6 +7643,7 @@ var v2GuidedConnect = async (deps) => {
7599
7643
  }),
7600
7644
  locationInput,
7601
7645
  maxAttempts: deps.maxAttempts,
7646
+ onAttempt: deps.onAttempt,
7602
7647
  pollDelayMs: deps.pollDelayMs,
7603
7648
  pollTerminal,
7604
7649
  retryDelayMs: deps.retryDelayMs,
@@ -7621,15 +7666,65 @@ var v2GuidedConnect = async (deps) => {
7621
7666
  });
7622
7667
  };
7623
7668
 
7669
+ // src/tui-entry/v2-legacy-key-gate/v2-legacy-key-gate.ts
7670
+ init_read_legacy_key2();
7671
+ init_remove_legacy_key2();
7672
+ init_write_attribution2();
7673
+ var v2LegacyKeyGate = async ({
7674
+ directory,
7675
+ dialog,
7676
+ toast
7677
+ }) => {
7678
+ const legacy = readLegacyKey(directory);
7679
+ if (legacy === void 0) {
7680
+ return "continue";
7681
+ }
7682
+ const convert = await dialog.confirm({
7683
+ message: V2_CONNECT_COPY.legacyKeyMessage,
7684
+ title: V2_CONNECT_COPY.legacyKeyTitle
7685
+ });
7686
+ if (convert !== true) {
7687
+ return "continue";
7688
+ }
7689
+ writeAttribution(
7690
+ { directory, outsideWorktree: "return-undefined" },
7691
+ { kind: "personal" }
7692
+ );
7693
+ removeLegacyKey(directory);
7694
+ toast({ message: V2_CONNECT_COPY.legacyKeyConverted, variant: "info" });
7695
+ return "converted";
7696
+ };
7697
+
7624
7698
  // src/tui-entry/v2-autoconnect/v2-autoconnect.ts
7625
- var v2Autoconnect = async (context) => {
7699
+ var v2Autoconnect = async (context, options = {}) => {
7626
7700
  const directory = v2SetupDirectory(context);
7627
7701
  if (directory === void 0) return;
7628
7702
  const unattributed = () => readAttribution({
7629
7703
  directory,
7630
7704
  outsideWorktree: "return-undefined"
7631
7705
  }) === void 0;
7632
- await v2GuidedConnect({ context, unattributed });
7706
+ const dialog = context.ui.dialog;
7707
+ if (dialog !== void 0) {
7708
+ const legacy = await v2LegacyKeyGate({
7709
+ directory,
7710
+ dialog,
7711
+ toast: (input) => {
7712
+ context.ui.toast.show(input);
7713
+ }
7714
+ });
7715
+ if (legacy === "converted" && !unattributed()) {
7716
+ context.ui.toast.show({
7717
+ message: V2_CONNECT_COPY.success,
7718
+ variant: "success"
7719
+ });
7720
+ return;
7721
+ }
7722
+ }
7723
+ await v2GuidedConnect({
7724
+ context,
7725
+ onAttempt: options.onAttempt,
7726
+ unattributed
7727
+ });
7633
7728
  };
7634
7729
 
7635
7730
  // src/tui-entry/show-r33-menu/show-r33-menu.ts
@@ -7678,15 +7773,53 @@ var showR33Menu = async (context) => {
7678
7773
  });
7679
7774
  };
7680
7775
 
7776
+ // src/tui-entry/copy-to-clipboard/copy-to-clipboard.ts
7777
+ import { spawn as spawn2 } from "node:child_process";
7778
+ var copyToClipboard = async (text, options = {}) => new Promise((resolve2) => {
7779
+ let command = "xclip";
7780
+ if (process.platform === "darwin") {
7781
+ command = "pbcopy";
7782
+ } else if (process.platform === "win32") {
7783
+ command = "clip";
7784
+ }
7785
+ const args = process.platform === "linux" ? ["-selection", "clipboard"] : [];
7786
+ const spawnUrl = options.spawnUrl ?? ((cmd, opts) => spawn2(cmd, args, opts));
7787
+ try {
7788
+ const child = spawnUrl(command, { stdio: "pipe" });
7789
+ child.stdin.write(text);
7790
+ child.stdin.end();
7791
+ resolve2(true);
7792
+ } catch {
7793
+ resolve2(false);
7794
+ }
7795
+ });
7796
+
7797
+ // src/tui-entry/v2-sign-in-action/v2-sign-in-copy/v2-sign-in-copy.ts
7798
+ var v2SignInCopy = ({ currentAttempt }) => () => {
7799
+ const attempt = currentAttempt();
7800
+ if (attempt !== void 0) {
7801
+ void copyToClipboard(attempt.url);
7802
+ }
7803
+ };
7804
+
7805
+ // src/tui-entry/v2-sign-in-action/v2-sign-in-open/v2-sign-in-open.ts
7806
+ init_open_system_browser2();
7807
+ var v2SignInOpen = ({ currentAttempt }) => () => {
7808
+ const attempt = currentAttempt();
7809
+ if (attempt !== void 0) {
7810
+ void openSystemBrowser(new URL(attempt.url));
7811
+ }
7812
+ };
7813
+
7681
7814
  // src/tui-entry/v2-setup-lock/v2-setup-lock.ts
7682
7815
  var SETUP_MODE2 = "route33-setup";
7683
7816
  var RESUME_BIND2 = "enter";
7684
- var v2SetupLock = (context) => {
7685
- const required = v2SetupDirectory(context) !== void 0;
7686
- const release = required ? context.keymap?.mode.push(SETUP_MODE2) : void 0;
7687
- if (required) {
7688
- context.keymap?.layer(() => ({
7689
- bindings: ["route33.setup"],
7817
+ var v2SetupLock = (deps) => {
7818
+ const setupPending = () => v2SetupDirectory({ location: { directory: deps.directory() } }) !== void 0;
7819
+ const pop = setupPending() ? deps.mode?.push(SETUP_MODE2) : void 0;
7820
+ if (pop !== void 0) {
7821
+ deps.keymap?.registerLayer({
7822
+ bindings: ["route33.setup", "route33.open", "route33.copy"],
7690
7823
  commands: [
7691
7824
  {
7692
7825
  bind: RESUME_BIND2,
@@ -7696,24 +7829,48 @@ var v2SetupLock = (context) => {
7696
7829
  },
7697
7830
  suggested: true,
7698
7831
  title: "Route 33: pick the budget for this folder"
7832
+ },
7833
+ {
7834
+ bind: "o",
7835
+ id: "route33.open",
7836
+ run: () => {
7837
+ openLink();
7838
+ },
7839
+ title: "Route 33: open the sign-in link in your browser"
7840
+ },
7841
+ {
7842
+ bind: "c",
7843
+ id: "route33.copy",
7844
+ run: () => {
7845
+ copyLink();
7846
+ },
7847
+ title: "Route 33: copy the sign-in link"
7699
7848
  }
7700
7849
  ],
7701
7850
  mode: SETUP_MODE2,
7702
7851
  priority: 10
7703
- }));
7852
+ });
7704
7853
  }
7705
7854
  let running = false;
7706
7855
  const settle = async () => {
7707
7856
  if (running) return;
7708
7857
  running = true;
7709
7858
  try {
7710
- await v2Autoconnect(context);
7859
+ if (setupPending()) {
7860
+ await deps.run();
7861
+ }
7711
7862
  } finally {
7712
7863
  running = false;
7713
7864
  }
7714
- if (v2SetupDirectory(context) !== void 0) return;
7715
- release?.();
7865
+ if (setupPending()) return;
7866
+ pop?.();
7716
7867
  };
7868
+ const openLink = v2SignInOpen({
7869
+ currentAttempt: () => deps.currentAttempt?.()
7870
+ });
7871
+ const copyLink = v2SignInCopy({
7872
+ currentAttempt: () => deps.currentAttempt?.()
7873
+ });
7717
7874
  return { start: settle };
7718
7875
  };
7719
7876
 
@@ -7739,7 +7896,27 @@ var createV2Tui = () => ({
7739
7896
  bindings: ["route33.menu"]
7740
7897
  }));
7741
7898
  const location = context.location ?? context.data?.location.default();
7742
- void v2SetupLock({ ...context, location }).start().catch(() => void 0);
7899
+ let liveAttempt;
7900
+ void v2SetupLock({
7901
+ ...context.keymap === void 0 ? {} : {
7902
+ keymap: {
7903
+ registerLayer: (layer) => {
7904
+ const host = context.keymap;
7905
+ if (host !== void 0) {
7906
+ host.layer(() => layer);
7907
+ }
7908
+ }
7909
+ }
7910
+ },
7911
+ ...context.keymap?.mode === void 0 ? {} : { mode: context.keymap.mode },
7912
+ currentAttempt: () => liveAttempt,
7913
+ directory: () => location?.directory ?? "",
7914
+ run: () => v2Autoconnect(context, {
7915
+ onAttempt: (attempt) => {
7916
+ liveAttempt = attempt;
7917
+ }
7918
+ })
7919
+ }).start().catch(() => void 0);
7743
7920
  }
7744
7921
  });
7745
7922
 
@@ -2343,6 +2343,10 @@ var clearAttribution = (options) => {
2343
2343
  return true;
2344
2344
  };
2345
2345
 
2346
+ // src/attribution/read-legacy-key/read-legacy-key.ts
2347
+ import * as path10 from "node:path";
2348
+ var LEGACY_KEY_RELATIVE_PATH = path10.join(".opencode", "route33.key");
2349
+
2346
2350
  // src/attribution/parse-attribution/parse-attribution.ts
2347
2351
  var parseAttribution = (value) => {
2348
2352
  if (!isRecord(value)) {
@@ -2372,16 +2376,20 @@ var readAttribution = (options) => {
2372
2376
  }
2373
2377
  };
2374
2378
 
2379
+ // src/attribution/remove-legacy-key/remove-legacy-key.ts
2380
+ import * as path11 from "node:path";
2381
+ var LEGACY_KEY_RELATIVE_PATH2 = path11.join(".opencode", "route33.key");
2382
+
2375
2383
  // src/attribution/write-attribution/write-attribution.ts
2376
2384
  import { randomUUID as randomUUID2 } from "node:crypto";
2377
2385
  import * as fs11 from "node:fs";
2378
- import * as path10 from "node:path";
2386
+ import * as path12 from "node:path";
2379
2387
  var writeAttribution = (options, attribution) => {
2380
2388
  const file = resolveAttributionFile(options);
2381
2389
  if (file === void 0) {
2382
2390
  return false;
2383
2391
  }
2384
- fs11.mkdirSync(path10.dirname(file), { recursive: true });
2392
+ fs11.mkdirSync(path12.dirname(file), { recursive: true });
2385
2393
  const temporaryFile = `${file}.${String(process.pid)}.${randomUUID2()}.tmp`;
2386
2394
  try {
2387
2395
  fs11.writeFileSync(temporaryFile, `${JSON.stringify(attribution)}
@@ -3917,7 +3925,42 @@ var buildConnectForm = (plan) => ({
3917
3925
  });
3918
3926
 
3919
3927
  // src/v2-entry/map-route33-v2-provider/map-route33-v2-provider.ts
3920
- import { Integration, Model, Provider } from "@opencode/plugin";
3928
+ import { Integration, Provider } from "@opencode/plugin";
3929
+
3930
+ // src/v2-entry/map-route33-model/map-route33-model.ts
3931
+ import { Model as Model2 } from "@opencode/plugin";
3932
+
3933
+ // src/v2-entry/map-route33-variant/map-route33-variant.ts
3934
+ import { Model } from "@opencode/plugin";
3935
+ var mapRoute33Variant = (variants) => {
3936
+ if (variants === void 0) {
3937
+ return [];
3938
+ }
3939
+ const mapped = [];
3940
+ for (const [variantID, overlay] of Object.entries(variants)) {
3941
+ if (typeof overlay !== "object") {
3942
+ continue;
3943
+ }
3944
+ mapped.push({
3945
+ id: Model.VariantID.make(variantID),
3946
+ ...structuredClone(overlay)
3947
+ });
3948
+ }
3949
+ return mapped;
3950
+ };
3951
+
3952
+ // src/v2-entry/map-route33-model/map-route33-model.ts
3953
+ var mapRoute33Model = (providerID, modelID, model) => ({
3954
+ ...Model2.Info.default(providerID, Model2.ID.make(modelID)),
3955
+ name: typeof model.name === "string" ? model.name : modelID,
3956
+ ...model.variants === void 0 ? {} : {
3957
+ variants: mapRoute33Variant(
3958
+ model.variants
3959
+ )
3960
+ }
3961
+ });
3962
+
3963
+ // src/v2-entry/map-route33-v2-provider/map-route33-v2-provider.ts
3921
3964
  var mapRoute33V2Provider = (provider) => {
3922
3965
  const providerID = Provider.ID.make("route_33");
3923
3966
  const info = Object.assign(Provider.Info.empty(providerID), {
@@ -3942,12 +3985,7 @@ var mapRoute33V2Provider = (provider) => {
3942
3985
  }
3943
3986
  }),
3944
3987
  models: Object.entries(provider.models ?? {}).flatMap(
3945
- ([modelID, model]) => model === void 0 ? [] : [
3946
- {
3947
- ...Model.Info.default(providerID, Model.ID.make(modelID)),
3948
- name: typeof model["name"] === "string" ? model["name"] : modelID
3949
- }
3950
- ]
3988
+ ([modelID, model]) => model === void 0 ? [] : [mapRoute33Model(providerID, modelID, model)]
3951
3989
  )
3952
3990
  };
3953
3991
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@route_33/opencode",
3
- "version": "0.4.7-rc.10",
3
+ "version": "0.4.7-rc.11",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,8 +42,8 @@
42
42
  "eslint": "10.13.0",
43
43
  "typescript": "6.0.3",
44
44
  "vitest": "5.0.5",
45
- "@cronos-ai-lab/eslint-config": "0.1.2",
46
- "@cronos-ai-lab/typescript-config": "0.1.2"
45
+ "@cronos-ai-lab/eslint-config": "0.2.0",
46
+ "@cronos-ai-lab/typescript-config": "0.2.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "esbuild src/index.ts src/v2-entry/index.ts src/tui-entry/index.ts src/cli/production-smoke/production-smoke.ts --bundle --format=esm --platform=node --packages=external --outbase=src --outdir=dist --alias:@opencode-ai/plugin-v2=@opencode/plugin --define:__ROUTE33_DEFAULT_CLIENT_ID__=\\\"${ROUTE33_DEFAULT_CLIENT_ID:-}\\\"",