@quonfig/cli 0.0.29 → 0.0.30

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 (37) hide show
  1. package/README.md +35 -35
  2. package/dist/codegen/fetch-workspace-snapshot.js +3 -3
  3. package/dist/commands/delete.js +2 -1
  4. package/dist/commands/login.js +0 -1
  5. package/dist/commands/migrate/doctor.js +3 -1
  6. package/dist/commands/migrate.js +10 -13
  7. package/dist/commands/override.js +1 -1
  8. package/dist/commands/pull.js +12 -12
  9. package/dist/commands/push.d.ts +1 -1
  10. package/dist/commands/push.js +5 -5
  11. package/dist/commands/sync.d.ts +1 -0
  12. package/dist/commands/sync.js +6 -13
  13. package/dist/commands/whoami.js +15 -3
  14. package/dist/commands/workspace/bootstrap.js +8 -12
  15. package/dist/commands/workspace/create.d.ts +5 -1
  16. package/dist/commands/workspace/create.js +18 -7
  17. package/dist/commands/workspace/switch.d.ts +7 -1
  18. package/dist/commands/workspace/switch.js +45 -27
  19. package/dist/commands/workspace.d.ts +5 -0
  20. package/dist/commands/workspace.js +28 -9
  21. package/dist/migrate/sources/launch/translate.js +6 -3
  22. package/dist/migrate/sources/launch/zod-to-json-schema.js +4 -0
  23. package/dist/push/config-push-client.d.ts +1 -1
  24. package/dist/push/config-push-client.js +3 -5
  25. package/dist/util/get-client.js +12 -12
  26. package/dist/util/get-valid-token.d.ts +7 -7
  27. package/dist/util/get-valid-token.js +19 -13
  28. package/dist/util/gitea-api.d.ts +2 -2
  29. package/dist/util/gitea-api.js +5 -7
  30. package/dist/util/resolve-workspace.d.ts +14 -4
  31. package/dist/util/resolve-workspace.js +32 -20
  32. package/dist/util/token-storage.d.ts +10 -5
  33. package/dist/util/token-storage.js +14 -7
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/oclif.manifest.json +1 -1
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@ $ npm install -g @quonfig/cli
14
14
  $ qfg COMMAND
15
15
  running command...
16
16
  $ qfg (--version)
17
- @quonfig/cli/0.0.29 linux-x64 node-v24.4.1
17
+ @quonfig/cli/0.0.30 linux-x64 node-v24.4.1
18
18
  $ qfg --help [COMMAND]
19
19
  USAGE
20
20
  $ qfg COMMAND
@@ -91,7 +91,7 @@ EXAMPLES
91
91
  $ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
92
92
  ```
93
93
 
94
- _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/config-schema.ts)_
94
+ _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/config-schema.ts)_
95
95
 
96
96
  ## `qfg create NAME`
97
97
 
@@ -176,7 +176,7 @@ EXAMPLES
176
176
  $ qfg set-rollout my.new.flag --environment production --true-percent 20
177
177
  ```
178
178
 
179
- _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/create.ts)_
179
+ _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/create.ts)_
180
180
 
181
181
  ## `qfg delete NAME`
182
182
 
@@ -222,7 +222,7 @@ EXAMPLES
222
222
  $ qfg delete my.config --yes
223
223
  ```
224
224
 
225
- _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/delete.ts)_
225
+ _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/delete.ts)_
226
226
 
227
227
  ## `qfg flag info [NAME]`
228
228
 
@@ -425,7 +425,7 @@ EXAMPLES
425
425
  $ qfg generate --targets node-ts -o ./dist # combine with targets
426
426
  ```
427
427
 
428
- _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/generate.ts)_
428
+ _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/generate.ts)_
429
429
 
430
430
  ## `qfg generate-new-hex-key`
431
431
 
@@ -448,7 +448,7 @@ EXAMPLES
448
448
  $ qfg generate-new-hex-key
449
449
  ```
450
450
 
451
- _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/generate-new-hex-key.ts)_
451
+ _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/generate-new-hex-key.ts)_
452
452
 
453
453
  ## `qfg get [NAME]`
454
454
 
@@ -480,7 +480,7 @@ EXAMPLES
480
480
  $ qfg get my.config.name --environment=production
481
481
  ```
482
482
 
483
- _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/get.ts)_
483
+ _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/get.ts)_
484
484
 
485
485
  ## `qfg info [NAME]`
486
486
 
@@ -528,7 +528,7 @@ EXAMPLES
528
528
  $ qfg info my.config.name --exclude-evaluations # skip 24h stats
529
529
  ```
530
530
 
531
- _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/info.ts)_
531
+ _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/info.ts)_
532
532
 
533
533
  ## `qfg init [DIRECTORY]`
534
534
 
@@ -568,7 +568,7 @@ EXAMPLES
568
568
  $ qfg init --dry-run
569
569
  ```
570
570
 
571
- _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/init.ts)_
571
+ _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/init.ts)_
572
572
 
573
573
  ## `qfg interactive`
574
574
 
@@ -598,7 +598,7 @@ EXAMPLES
598
598
  $ qfg
599
599
  ```
600
600
 
601
- _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/interactive.ts)_
601
+ _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/interactive.ts)_
602
602
 
603
603
  ## `qfg list`
604
604
 
@@ -638,7 +638,7 @@ EXAMPLES
638
638
  $ qfg list --feature-flags
639
639
  ```
640
640
 
641
- _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/list.ts)_
641
+ _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/list.ts)_
642
642
 
643
643
  ## `qfg log-level NAME`
644
644
 
@@ -703,7 +703,7 @@ EXAMPLES
703
703
  $ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
704
704
  ```
705
705
 
706
- _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/log-level.ts)_
706
+ _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/log-level.ts)_
707
707
 
708
708
  ## `qfg login`
709
709
 
@@ -728,7 +728,7 @@ EXAMPLES
728
728
  $ qfg login --profile myprofile
729
729
  ```
730
730
 
731
- _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/login.ts)_
731
+ _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/login.ts)_
732
732
 
733
733
  ## `qfg logout`
734
734
 
@@ -751,7 +751,7 @@ EXAMPLES
751
751
  $ qfg logout
752
752
  ```
753
753
 
754
- _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/logout.ts)_
754
+ _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/logout.ts)_
755
755
 
756
756
  ## `qfg migrate`
757
757
 
@@ -805,7 +805,7 @@ EXAMPLES
805
805
  $ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-config --staging
806
806
  ```
807
807
 
808
- _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/migrate.ts)_
808
+ _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/migrate.ts)_
809
809
 
810
810
  ## `qfg migrate doctor`
811
811
 
@@ -843,7 +843,7 @@ EXAMPLES
843
843
  $ qfg migrate doctor --json
844
844
  ```
845
845
 
846
- _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/migrate/doctor.ts)_
846
+ _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/migrate/doctor.ts)_
847
847
 
848
848
  ## `qfg migrate my-code`
849
849
 
@@ -875,7 +875,7 @@ EXAMPLES
875
875
  $ qfg migrate my-code --dry-run
876
876
  ```
877
877
 
878
- _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/migrate/my-code.ts)_
878
+ _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/migrate/my-code.ts)_
879
879
 
880
880
  ## `qfg migrate status`
881
881
 
@@ -906,7 +906,7 @@ EXAMPLES
906
906
  $ qfg migrate status --json
907
907
  ```
908
908
 
909
- _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/migrate/status.ts)_
909
+ _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/migrate/status.ts)_
910
910
 
911
911
  ## `qfg override [NAME] [VALUE]`
912
912
 
@@ -961,7 +961,7 @@ EXAMPLES
961
961
  $ qfg override my.flag true --env=staging
962
962
  ```
963
963
 
964
- _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/override.ts)_
964
+ _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/override.ts)_
965
965
 
966
966
  ## `qfg pull`
967
967
 
@@ -1005,7 +1005,7 @@ EXAMPLES
1005
1005
  $ qfg pull # uses QUONFIG_DIR env var
1006
1006
  ```
1007
1007
 
1008
- _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/pull.ts)_
1008
+ _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/pull.ts)_
1009
1009
 
1010
1010
  ## `qfg push`
1011
1011
 
@@ -1053,7 +1053,7 @@ EXAMPLES
1053
1053
  $ qfg push --dir ./our-config --yes
1054
1054
  ```
1055
1055
 
1056
- _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/push.ts)_
1056
+ _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/push.ts)_
1057
1057
 
1058
1058
  ## `qfg schema NAME`
1059
1059
 
@@ -1091,7 +1091,7 @@ EXAMPLES
1091
1091
  $ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
1092
1092
  ```
1093
1093
 
1094
- _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/schema.ts)_
1094
+ _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/schema.ts)_
1095
1095
 
1096
1096
  ## `qfg sdk-key`
1097
1097
 
@@ -1118,7 +1118,7 @@ EXAMPLES
1118
1118
  $ qfg sdk-key revoke <key-id>
1119
1119
  ```
1120
1120
 
1121
- _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/sdk-key.ts)_
1121
+ _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/sdk-key.ts)_
1122
1122
 
1123
1123
  ## `qfg sdk-key create`
1124
1124
 
@@ -1150,7 +1150,7 @@ EXAMPLES
1150
1150
  $ qfg sdk-key create --environment staging --type browser
1151
1151
  ```
1152
1152
 
1153
- _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/sdk-key/create.ts)_
1153
+ _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/sdk-key/create.ts)_
1154
1154
 
1155
1155
  ## `qfg sdk-key list`
1156
1156
 
@@ -1179,7 +1179,7 @@ EXAMPLES
1179
1179
  $ qfg sdk-key list --environment production
1180
1180
  ```
1181
1181
 
1182
- _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/sdk-key/list.ts)_
1182
+ _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/sdk-key/list.ts)_
1183
1183
 
1184
1184
  ## `qfg sdk-key revoke KEYID`
1185
1185
 
@@ -1206,7 +1206,7 @@ EXAMPLES
1206
1206
  $ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
1207
1207
  ```
1208
1208
 
1209
- _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/sdk-key/revoke.ts)_
1209
+ _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/sdk-key/revoke.ts)_
1210
1210
 
1211
1211
  ## `qfg set-default [NAME]`
1212
1212
 
@@ -1277,7 +1277,7 @@ EXAMPLES
1277
1277
  $ qfg pull && qfg config-schema
1278
1278
  ```
1279
1279
 
1280
- _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/set-default.ts)_
1280
+ _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/set-default.ts)_
1281
1281
 
1282
1282
  ## `qfg set-rollout [NAME]`
1283
1283
 
@@ -1354,7 +1354,7 @@ EXAMPLES
1354
1354
  $ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
1355
1355
  ```
1356
1356
 
1357
- _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/set-rollout.ts)_
1357
+ _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/set-rollout.ts)_
1358
1358
 
1359
1359
  ## `qfg sync`
1360
1360
 
@@ -1386,7 +1386,7 @@ EXAMPLES
1386
1386
  $ qfg sync --watch --dir ./our-config --interval 30
1387
1387
  ```
1388
1388
 
1389
- _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/sync.ts)_
1389
+ _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/sync.ts)_
1390
1390
 
1391
1391
  ## `qfg toggle [NAME]`
1392
1392
 
@@ -1488,7 +1488,7 @@ EXAMPLES
1488
1488
  $ qfg verify --json
1489
1489
  ```
1490
1490
 
1491
- _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/verify.ts)_
1491
+ _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/verify.ts)_
1492
1492
 
1493
1493
  ## `qfg whoami`
1494
1494
 
@@ -1511,7 +1511,7 @@ EXAMPLES
1511
1511
  $ qfg whoami
1512
1512
  ```
1513
1513
 
1514
- _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/whoami.ts)_
1514
+ _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/whoami.ts)_
1515
1515
 
1516
1516
  ## `qfg workspace`
1517
1517
 
@@ -1534,7 +1534,7 @@ EXAMPLES
1534
1534
  $ qfg workspace
1535
1535
  ```
1536
1536
 
1537
- _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/workspace.ts)_
1537
+ _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/workspace.ts)_
1538
1538
 
1539
1539
  ## `qfg workspace bootstrap`
1540
1540
 
@@ -1567,7 +1567,7 @@ EXAMPLES
1567
1567
  $ qfg workspace bootstrap --dir ./our-config --skip-validate
1568
1568
  ```
1569
1569
 
1570
- _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/workspace/bootstrap.ts)_
1570
+ _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/workspace/bootstrap.ts)_
1571
1571
 
1572
1572
  ## `qfg workspace create SLUG`
1573
1573
 
@@ -1601,7 +1601,7 @@ EXAMPLES
1601
1601
  $ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
1602
1602
  ```
1603
1603
 
1604
- _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/workspace/create.ts)_
1604
+ _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/workspace/create.ts)_
1605
1605
 
1606
1606
  ## `qfg workspace switch [SLUG]`
1607
1607
 
@@ -1629,7 +1629,7 @@ EXAMPLES
1629
1629
  $ qfg workspace switch acme/production
1630
1630
  ```
1631
1631
 
1632
- _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.29/src/commands/workspace/switch.ts)_
1632
+ _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.30/src/commands/workspace/switch.ts)_
1633
1633
  <!-- commandsstop -->
1634
1634
 
1635
1635
  ## Local Development
@@ -20,11 +20,11 @@ const looksLike401 = (err) => {
20
20
  * caller is responsible for calling `cleanup()` in a `finally` block.
21
21
  */
22
22
  export async function fetchWorkspaceSnapshot(command, options = {}) {
23
- const workspaceId = await resolveWorkspaceUuid(command, options.workspace);
23
+ const { workspaceId, orgSlug } = await resolveWorkspaceUuid(command, options.workspace);
24
24
  let entry = await loadGiteaToken(workspaceId);
25
25
  if (!entry || isGiteaTokenExpired(entry)) {
26
26
  command.verboseLog('fetchWorkspaceSnapshot', 'Minting new Gitea read token...');
27
- entry = await mintAndStoreGiteaReadToken(workspaceId);
27
+ entry = await mintAndStoreGiteaReadToken(workspaceId, orgSlug);
28
28
  }
29
29
  const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'qfg-codegen-'));
30
30
  const cleanup = () => fs.promises.rm(tmpDir, { force: true, recursive: true });
@@ -36,7 +36,7 @@ export async function fetchWorkspaceSnapshot(command, options = {}) {
36
36
  catch (error) {
37
37
  if (looksLike401(error)) {
38
38
  command.verboseLog('fetchWorkspaceSnapshot', 'Got 401 on clone, refreshing Gitea token...');
39
- const fresh = await mintAndStoreGiteaReadToken(workspaceId);
39
+ const fresh = await mintAndStoreGiteaReadToken(workspaceId, orgSlug);
40
40
  await gitClone(fresh.repoUrl, tmpDir);
41
41
  }
42
42
  else {
@@ -47,7 +47,8 @@ Examples
47
47
  };
48
48
  async run() {
49
49
  const { args, flags } = await this.parse(Delete);
50
- // TODO(qfg-kr7.5): pick the token set keyed by the resolved workosOrgId.
50
+ // Auth happens through APICommand's apiClient (used below). This is just
51
+ // a "logged in?" gate — any cached token answers that.
51
52
  const store = await loadTokens();
52
53
  const tokens = store ? Object.values(store.tokensByOrg)[0] : undefined;
53
54
  if (!tokens?.access_token) {
@@ -135,7 +135,6 @@ export default class Login extends BaseCommand {
135
135
  [profileName]: {
136
136
  organizationName: result.defaultOrg.name,
137
137
  organizationSlug: result.defaultOrg.slug,
138
- workosOrgId: result.defaultOrgId,
139
138
  workspace: defaultWorkspace?.workspaceId ?? result.defaultOrgId,
140
139
  workspaceName: defaultWorkspace?.workspaceSlug,
141
140
  workspaceSlug: defaultWorkspace?.workspaceSlug,
@@ -4,7 +4,9 @@ import { formatHumanReport, runDoctor } from '../../migrate/doctor.js';
4
4
  import { loadTokens } from '../../util/token-storage.js';
5
5
  const SUPPORTED_SOURCES = new Set(['launch']);
6
6
  const defaultLoadSession = async () => {
7
- // TODO(qfg-kr7.5): pick the token set keyed by the resolved workosOrgId.
7
+ // Doctor only checks token freshness, which is identical across the user's
8
+ // per-org tokens (they all expire on the same WorkOS clock). Reading the
9
+ // first one is sufficient.
8
10
  let store;
9
11
  try {
10
12
  store = await loadTokens();
@@ -11,7 +11,8 @@ import { NotYetImplementedError, } from '../migrate/source.js';
11
11
  import { applyLaunchBaseUrl } from '../migrate/sources/launch/api.js';
12
12
  import { mintGiteaToken } from '../util/gitea-api.js';
13
13
  import { displayUrl } from '../util/git-ops.js';
14
- import { getActiveProfile, loadAuthConfig, resolveWorkspaceId } from '../util/token-storage.js';
14
+ import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
15
+ import { loadAuthConfig } from '../util/token-storage.js';
15
16
  const DEFAULT_DIR = 'quonfig-config';
16
17
  export default class Migrate extends BaseCommand {
17
18
  static description = 'Migrate flags and configs from a legacy feature-flag system into a Quonfig workspace.\n\n' +
@@ -128,11 +129,11 @@ export default class Migrate extends BaseCommand {
128
129
  return payload;
129
130
  }
130
131
  if (flags.push && workspaceContext) {
131
- const { workspaceId } = workspaceContext;
132
+ const { workspaceId, orgSlug } = workspaceContext;
132
133
  this.log('Connecting to Gitea...');
133
134
  let repoUrl;
134
135
  try {
135
- const tokenData = await mintGiteaToken(workspaceId, 'write', 'bootstrap');
136
+ const tokenData = await mintGiteaToken(workspaceId, orgSlug, 'write', 'bootstrap');
136
137
  repoUrl = tokenData.repoUrl;
137
138
  }
138
139
  catch (error) {
@@ -219,16 +220,12 @@ export default class Migrate extends BaseCommand {
219
220
  const authConfig = await loadAuthConfig();
220
221
  if (!authConfig)
221
222
  return 'Not logged in. Run `qfg login` first, then re-run with --push.';
222
- if (slugOrId) {
223
- const resolved = resolveWorkspaceId(authConfig, slugOrId);
224
- return { authConfig, workspaceId: resolved ?? slugOrId };
225
- }
226
- const activeProfile = getActiveProfile();
227
- const profile = authConfig.profiles[activeProfile] || authConfig.profiles[authConfig.defaultProfile || 'default'];
228
- if (!profile) {
229
- return 'No active profile found. Pass --workspace <slug> or run `qfg login` first.';
230
- }
231
- return { authConfig, workspaceId: profile.workspace };
223
+ // For migrate --push we need both the workspaceId and the owning org's
224
+ // slug (so mintGiteaToken can pick the right per-org WorkOS token).
225
+ // resolveWorkspaceUuid handles both --workspace (org/ws form) and the
226
+ // saved-profile fallback uniformly.
227
+ const resolved = await resolveWorkspaceUuid(this, slugOrId);
228
+ return { authConfig, workspaceId: resolved.workspaceId, orgSlug: resolved.orgSlug };
232
229
  }
233
230
  }
234
231
  function latestChangedAtOf(changes, fallback) {
@@ -97,7 +97,7 @@ Examples
97
97
  };
98
98
  async run() {
99
99
  const { args, flags } = await this.parse(Override);
100
- // TODO(qfg-kr7.5): pick the token set keyed by the resolved workosOrgId.
100
+ // user_email is the same across all org-scoped tokens, so any one will do.
101
101
  const store = await loadTokens();
102
102
  const tokens = store ? Object.values(store.tokensByOrg)[0] : undefined;
103
103
  const userEmail = tokens?.user_email;
@@ -44,10 +44,10 @@ CLI shortcuts (no JSON editing needed for simple cases):
44
44
  // Resolve workspace ID — supports both OAuth and QUONFIG_API_KEY paths,
45
45
  // mirroring what APICommand does via get-client.ts so `qfg pull` behaves
46
46
  // the same in CI as `qfg create` or `qfg push`.
47
- const workspaceId = await resolveWorkspaceUuid(this, flags.workspace);
48
- this.verboseLog('Pull', { workspaceId, dir: resolvedDir });
47
+ const { workspaceId, orgSlug } = await resolveWorkspaceUuid(this, flags.workspace);
48
+ this.verboseLog('Pull', { workspaceId, orgSlug, dir: resolvedDir });
49
49
  // Get or mint Gitea read token
50
- const tokenEntry = await this.resolveTokenEntry(workspaceId);
50
+ const tokenEntry = await this.resolveTokenEntry(workspaceId, orgSlug);
51
51
  const repoUrl = tokenEntry.repoUrl;
52
52
  // Perform git operation
53
53
  const isRepo = await isGitRepo(resolvedDir);
@@ -74,7 +74,7 @@ CLI shortcuts (no JSON editing needed for simple cases):
74
74
  catch (error) {
75
75
  if (this.looksLike401(error)) {
76
76
  this.verboseLog('Pull', 'Got 401 on fetch, refreshing Gitea token...');
77
- const freshUrl = await this.refreshAndGetUrl(workspaceId);
77
+ const freshUrl = await this.refreshAndGetUrl(workspaceId, orgSlug);
78
78
  await gitSetRemote(resolvedDir, freshUrl);
79
79
  await gitFetch(resolvedDir);
80
80
  }
@@ -115,7 +115,7 @@ CLI shortcuts (no JSON editing needed for simple cases):
115
115
  // On 401, refresh token and retry once
116
116
  if (this.looksLike401(error)) {
117
117
  this.verboseLog('Pull', 'Got 401, refreshing Gitea token...');
118
- const freshUrl = await this.refreshAndGetUrl(workspaceId);
118
+ const freshUrl = await this.refreshAndGetUrl(workspaceId, orgSlug);
119
119
  await gitClone(freshUrl, resolvedDir);
120
120
  }
121
121
  else {
@@ -131,7 +131,7 @@ CLI shortcuts (no JSON editing needed for simple cases):
131
131
  // is a git repo, we also commit the new pin so it's included in the
132
132
  // user's next `qfg push` delta — push diffs HEAD vs origin, so a
133
133
  // working-tree-only write would never reach the server (qfg-0fn).
134
- await this.backfillWorkspacePin(resolvedDir, tokenEntry, workspaceId);
134
+ await this.backfillWorkspacePin(resolvedDir, tokenEntry, workspaceId, orgSlug);
135
135
  return { dir: resolvedDir, workspaceId };
136
136
  }
137
137
  /**
@@ -146,13 +146,13 @@ CLI shortcuts (no JSON editing needed for simple cases):
146
146
  *
147
147
  * This never throws — a broken `quonfig.json` shouldn't fail `qfg pull`.
148
148
  */
149
- async backfillWorkspacePin(dir, tokenEntry, workspaceId) {
149
+ async backfillWorkspacePin(dir, tokenEntry, workspaceId, orgSlug) {
150
150
  let backendSlug = tokenEntry.workspaceSlug;
151
151
  if (!backendSlug) {
152
152
  // Older cached entries lack the slug. Mint a fresh one solely to learn the
153
153
  // slug. Cheap (one API call), and the refresh updates the cache for next time.
154
154
  try {
155
- const fresh = await mintAndStoreGiteaReadToken(workspaceId);
155
+ const fresh = await mintAndStoreGiteaReadToken(workspaceId, orgSlug);
156
156
  backendSlug = fresh.workspaceSlug;
157
157
  }
158
158
  catch (error) {
@@ -248,8 +248,8 @@ CLI shortcuts (no JSON editing needed for simple cases):
248
248
  // Non-fatal
249
249
  }
250
250
  }
251
- async refreshAndGetUrl(workspaceId) {
252
- const data = await mintGiteaToken(workspaceId, 'read', 'pull');
251
+ async refreshAndGetUrl(workspaceId, orgSlug) {
252
+ const data = await mintGiteaToken(workspaceId, orgSlug, 'read', 'pull');
253
253
  const entry = {
254
254
  token: data.token,
255
255
  repoUrl: data.repoUrl,
@@ -259,11 +259,11 @@ CLI shortcuts (no JSON editing needed for simple cases):
259
259
  await saveGiteaToken(workspaceId, entry);
260
260
  return entry.repoUrl;
261
261
  }
262
- async resolveTokenEntry(workspaceId) {
262
+ async resolveTokenEntry(workspaceId, orgSlug) {
263
263
  let entry = await loadGiteaToken(workspaceId);
264
264
  if (!entry || isGiteaTokenExpired(entry)) {
265
265
  this.verboseLog('Pull', 'Minting new Gitea read token...');
266
- entry = await mintAndStoreGiteaReadToken(workspaceId);
266
+ entry = await mintAndStoreGiteaReadToken(workspaceId, orgSlug);
267
267
  }
268
268
  return entry;
269
269
  }
@@ -39,7 +39,7 @@ export declare function giteaResponseToMintResult(resp: GiteaTokenResponse): Git
39
39
  * oRPC procedure. We still mint a READ token to authenticate the bare-path
40
40
  * probe-clone and the clone-path `git fetch`.
41
41
  */
42
- export declare function buildRealDeps(cmd: Push): {
42
+ export declare function buildRealDeps(cmd: Push, orgSlug: string): {
43
43
  deps: Parameters<typeof runPush>[1];
44
44
  cleanup: () => Promise<void>;
45
45
  };
@@ -61,8 +61,8 @@ Enforces three guards before touching the remote:
61
61
  // Resolve requested target. --workspace > QUONFIG_WORKSPACE > active OAuth
62
62
  // profile. Supports API-key mode for headless runs so `qfg push` behaves
63
63
  // the same way `qfg create` does (both resolve via resolve-workspace.ts).
64
- const requestedTarget = await resolveWorkspaceUuid(this, flags.workspace);
65
- const { deps, cleanup } = buildRealDeps(this);
64
+ const { workspaceId: requestedTarget, orgSlug } = await resolveWorkspaceUuid(this, flags.workspace);
65
+ const { deps, cleanup } = buildRealDeps(this, orgSlug);
66
66
  try {
67
67
  const result = await runPush({
68
68
  dir: resolvedDir,
@@ -131,7 +131,7 @@ export function giteaResponseToMintResult(resp) {
131
131
  * oRPC procedure. We still mint a READ token to authenticate the bare-path
132
132
  * probe-clone and the clone-path `git fetch`.
133
133
  */
134
- export function buildRealDeps(cmd) {
134
+ export function buildRealDeps(cmd, orgSlug) {
135
135
  const log = (line) => cmd.log(line);
136
136
  const errLog = (line) => cmd.logToStderr(line);
137
137
  // State captured across calls so a bare-path diff can share its probe clone
@@ -208,7 +208,7 @@ export function buildRealDeps(cmd) {
208
208
  // fetch. The actual commit goes through the server via configs.push,
209
209
  // which is authorized off the user's WorkOS session — no write PAT
210
210
  // needed in the push code path (qfg-azk.13).
211
- const resp = await mintGiteaToken(requestedTarget, 'read', 'pull');
211
+ const resp = await mintGiteaToken(requestedTarget, orgSlug, 'read', 'pull');
212
212
  authenticatedRepoUrl = resp.repoUrl;
213
213
  return giteaResponseToMintResult(resp);
214
214
  },
@@ -219,7 +219,7 @@ export function buildRealDeps(cmd) {
219
219
  return { errors };
220
220
  },
221
221
  gitOps,
222
- pushToServer: callConfigsPush,
222
+ pushToServer: (input) => callConfigsPush(input, orgSlug),
223
223
  log,
224
224
  errLog,
225
225
  };
@@ -10,6 +10,7 @@ export default class Sync extends BaseCommand {
10
10
  };
11
11
  private resolvedDir;
12
12
  private workspaceId;
13
+ private orgSlug;
13
14
  run(): Promise<JsonObj | void>;
14
15
  private ensureRemoteUrl;
15
16
  private looksLike401;
@@ -1,9 +1,9 @@
1
1
  import * as path from 'node:path';
2
2
  import { Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../index.js';
4
- import { getActiveProfile, loadAuthConfig } from '../util/token-storage.js';
5
4
  import { loadGiteaToken, isGiteaTokenExpired, saveGiteaToken } from '../util/gitea-token-storage.js';
6
5
  import { mintGiteaToken } from '../util/gitea-api.js';
6
+ import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
7
7
  import { isGitRepo, isWorkingTreeClean, gitFetch, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitSetRemote, } from '../util/git-ops.js';
8
8
  const sleep = (ms) => new Promise((resolve) => {
9
9
  setTimeout(resolve, ms);
@@ -30,6 +30,7 @@ export default class Sync extends BaseCommand {
30
30
  };
31
31
  resolvedDir;
32
32
  workspaceId;
33
+ orgSlug;
33
34
  async run() {
34
35
  const { flags } = await this.parse(Sync);
35
36
  // Resolve directory
@@ -38,17 +39,9 @@ export default class Sync extends BaseCommand {
38
39
  return this.err('No directory specified. Use --dir <path> or set QUONFIG_DIR.');
39
40
  }
40
41
  this.resolvedDir = path.resolve(dir);
41
- // Resolve workspace ID
42
- const authConfig = await loadAuthConfig();
43
- if (!authConfig) {
44
- return this.err('Not logged in. Please run `qfg login` first.');
45
- }
46
- const activeProfile = getActiveProfile();
47
- const profile = authConfig.profiles[activeProfile] || authConfig.profiles[authConfig.defaultProfile || 'default'];
48
- if (!profile) {
49
- return this.err('No active profile found. Please run `qfg login` first.');
50
- }
51
- this.workspaceId = profile.workspace;
42
+ const resolved = await resolveWorkspaceUuid(this);
43
+ this.workspaceId = resolved.workspaceId;
44
+ this.orgSlug = resolved.orgSlug;
52
45
  // Validate the directory is a git repo
53
46
  const isRepo = await isGitRepo(this.resolvedDir);
54
47
  if (!isRepo) {
@@ -82,7 +75,7 @@ export default class Sync extends BaseCommand {
82
75
  return msg.includes('401') || msg.toLowerCase().includes('authentication failed');
83
76
  }
84
77
  async refreshToken() {
85
- const data = await mintGiteaToken(this.workspaceId, 'read', 'pull');
78
+ const data = await mintGiteaToken(this.workspaceId, this.orgSlug, 'read', 'pull');
86
79
  const entry = { token: data.token, repoUrl: data.repoUrl, expiresAt: data.expiresAt };
87
80
  await saveGiteaToken(this.workspaceId, entry);
88
81
  await gitSetRemote(this.resolvedDir, entry.repoUrl);
@@ -1,6 +1,6 @@
1
1
  import { BaseCommand } from '../index.js';
2
2
  import { getApiUrl } from '../util/domain-urls.js';
3
- import { getValidAccessToken, resolveDefaultOrgId } from '../util/get-valid-token.js';
3
+ import { getValidAccessToken } from '../util/get-valid-token.js';
4
4
  import { decodeJWT } from '../util/oauth-client.js';
5
5
  import { tryParseWorkspacePin } from '../util/quonfig-json.js';
6
6
  import { findOrgIdBySlug, loadTokens } from '../util/token-storage.js';
@@ -82,7 +82,11 @@ export default class Whoami extends BaseCommand {
82
82
  }
83
83
  if (defaultOrgId)
84
84
  return defaultOrgId;
85
- return resolveDefaultOrgId().then((id) => id || undefined);
85
+ // Fall back to the first token entry — gives a stable marker rather than nothing.
86
+ const store = await loadTokens();
87
+ if (!store)
88
+ return undefined;
89
+ return Object.keys(store.tokensByOrg)[0];
86
90
  }
87
91
  /**
88
92
  * Best-effort fetch of `me.organizations` for role enrichment. Failure
@@ -92,7 +96,10 @@ export default class Whoami extends BaseCommand {
92
96
  async fetchOrganizations() {
93
97
  let jwt;
94
98
  try {
95
- const orgId = await resolveDefaultOrgId();
99
+ const store = await loadTokens();
100
+ if (!store)
101
+ return [];
102
+ const orgId = pickAnyOrgId(store);
96
103
  if (!orgId)
97
104
  return [];
98
105
  jwt = await getValidAccessToken(orgId, this.verboseLog);
@@ -136,3 +143,8 @@ function mergeMemberships(local, server) {
136
143
  merged.set(o.workosOrgId, o);
137
144
  return [...merged.values()].sort((a, b) => a.slug.localeCompare(b.slug));
138
145
  }
146
+ function pickAnyOrgId(store) {
147
+ if (store.defaultOrgId && store.tokensByOrg[store.defaultOrgId])
148
+ return store.defaultOrgId;
149
+ return Object.keys(store.tokensByOrg)[0];
150
+ }