@quonfig/cli 0.0.22 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -36
- package/dist/commands/log-level.js +1 -3
- package/dist/commands/login.js +30 -1
- package/dist/commands/pull.js +2 -2
- package/dist/commands/push.d.ts +6 -14
- package/dist/commands/push.js +54 -99
- package/dist/commands/workspace/switch.js +1 -4
- package/dist/init/schema.js +4 -2
- package/dist/migrate/push-to-cloud.js +3 -6
- package/dist/migrate/sources/launch/zod-to-json-schema.js +130 -132
- package/dist/migrate/sources/launch.js +1 -1
- package/dist/push/bare-path-diff.js +19 -17
- package/dist/push/config-push-client.d.ts +18 -0
- package/dist/push/config-push-client.js +82 -0
- package/dist/push/diff-summary.d.ts +11 -0
- package/dist/push/diff-summary.js +1 -8
- package/dist/push/run-push.d.ts +53 -13
- package/dist/push/run-push.js +44 -132
- package/dist/util/gitea-token-storage.js +2 -2
- package/dist/util/open-browser.d.ts +1 -0
- package/dist/util/open-browser.js +27 -0
- package/dist/util/quonfig-home.d.ts +6 -0
- package/dist/util/quonfig-home.js +8 -0
- package/dist/util/resolve-workspace.js +6 -2
- package/dist/util/token-storage.js +2 -2
- package/dist/verify/validate.js +2 -2
- package/oclif.manifest.json +1 -1
- 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.
|
|
17
|
+
@quonfig/cli/0.0.24 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -92,7 +92,7 @@ EXAMPLES
|
|
|
92
92
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
95
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/config-schema.ts)_
|
|
96
96
|
|
|
97
97
|
## `qfg create NAME`
|
|
98
98
|
|
|
@@ -177,7 +177,7 @@ EXAMPLES
|
|
|
177
177
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
180
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/create.ts)_
|
|
181
181
|
|
|
182
182
|
## `qfg delete NAME`
|
|
183
183
|
|
|
@@ -223,7 +223,7 @@ EXAMPLES
|
|
|
223
223
|
$ qfg delete my.config --yes
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
226
|
+
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/delete.ts)_
|
|
227
227
|
|
|
228
228
|
## `qfg flag info [NAME]`
|
|
229
229
|
|
|
@@ -426,7 +426,7 @@ EXAMPLES
|
|
|
426
426
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
427
427
|
```
|
|
428
428
|
|
|
429
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
429
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/generate.ts)_
|
|
430
430
|
|
|
431
431
|
## `qfg generate-new-hex-key`
|
|
432
432
|
|
|
@@ -449,7 +449,7 @@ EXAMPLES
|
|
|
449
449
|
$ qfg generate-new-hex-key
|
|
450
450
|
```
|
|
451
451
|
|
|
452
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
452
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/generate-new-hex-key.ts)_
|
|
453
453
|
|
|
454
454
|
## `qfg get [NAME]`
|
|
455
455
|
|
|
@@ -481,7 +481,7 @@ EXAMPLES
|
|
|
481
481
|
$ qfg get my.config.name --environment=production
|
|
482
482
|
```
|
|
483
483
|
|
|
484
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
484
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/get.ts)_
|
|
485
485
|
|
|
486
486
|
## `qfg info [NAME]`
|
|
487
487
|
|
|
@@ -529,7 +529,7 @@ EXAMPLES
|
|
|
529
529
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
530
530
|
```
|
|
531
531
|
|
|
532
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
532
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/info.ts)_
|
|
533
533
|
|
|
534
534
|
## `qfg init [DIRECTORY]`
|
|
535
535
|
|
|
@@ -569,7 +569,7 @@ EXAMPLES
|
|
|
569
569
|
$ qfg init --dry-run
|
|
570
570
|
```
|
|
571
571
|
|
|
572
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
572
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/init.ts)_
|
|
573
573
|
|
|
574
574
|
## `qfg interactive`
|
|
575
575
|
|
|
@@ -599,7 +599,7 @@ EXAMPLES
|
|
|
599
599
|
$ qfg
|
|
600
600
|
```
|
|
601
601
|
|
|
602
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
602
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/interactive.ts)_
|
|
603
603
|
|
|
604
604
|
## `qfg list`
|
|
605
605
|
|
|
@@ -639,7 +639,7 @@ EXAMPLES
|
|
|
639
639
|
$ qfg list --feature-flags
|
|
640
640
|
```
|
|
641
641
|
|
|
642
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
642
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/list.ts)_
|
|
643
643
|
|
|
644
644
|
## `qfg log-level NAME`
|
|
645
645
|
|
|
@@ -704,7 +704,7 @@ EXAMPLES
|
|
|
704
704
|
$ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
|
|
705
705
|
```
|
|
706
706
|
|
|
707
|
-
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
707
|
+
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/log-level.ts)_
|
|
708
708
|
|
|
709
709
|
## `qfg login`
|
|
710
710
|
|
|
@@ -729,7 +729,7 @@ EXAMPLES
|
|
|
729
729
|
$ qfg login --profile myprofile
|
|
730
730
|
```
|
|
731
731
|
|
|
732
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
732
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/login.ts)_
|
|
733
733
|
|
|
734
734
|
## `qfg logout`
|
|
735
735
|
|
|
@@ -752,7 +752,7 @@ EXAMPLES
|
|
|
752
752
|
$ qfg logout
|
|
753
753
|
```
|
|
754
754
|
|
|
755
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
755
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/logout.ts)_
|
|
756
756
|
|
|
757
757
|
## `qfg mcp`
|
|
758
758
|
|
|
@@ -785,7 +785,7 @@ EXAMPLES
|
|
|
785
785
|
$ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
|
|
786
786
|
```
|
|
787
787
|
|
|
788
|
-
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
788
|
+
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/mcp.ts)_
|
|
789
789
|
|
|
790
790
|
## `qfg migrate`
|
|
791
791
|
|
|
@@ -839,7 +839,7 @@ EXAMPLES
|
|
|
839
839
|
$ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-config --staging
|
|
840
840
|
```
|
|
841
841
|
|
|
842
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
842
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate.ts)_
|
|
843
843
|
|
|
844
844
|
## `qfg migrate doctor`
|
|
845
845
|
|
|
@@ -877,7 +877,7 @@ EXAMPLES
|
|
|
877
877
|
$ qfg migrate doctor --json
|
|
878
878
|
```
|
|
879
879
|
|
|
880
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
880
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/doctor.ts)_
|
|
881
881
|
|
|
882
882
|
## `qfg migrate my-code`
|
|
883
883
|
|
|
@@ -909,7 +909,7 @@ EXAMPLES
|
|
|
909
909
|
$ qfg migrate my-code --dry-run
|
|
910
910
|
```
|
|
911
911
|
|
|
912
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
912
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/my-code.ts)_
|
|
913
913
|
|
|
914
914
|
## `qfg migrate status`
|
|
915
915
|
|
|
@@ -940,7 +940,7 @@ EXAMPLES
|
|
|
940
940
|
$ qfg migrate status --json
|
|
941
941
|
```
|
|
942
942
|
|
|
943
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
943
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/migrate/status.ts)_
|
|
944
944
|
|
|
945
945
|
## `qfg override [NAME] [VALUE]`
|
|
946
946
|
|
|
@@ -995,7 +995,7 @@ EXAMPLES
|
|
|
995
995
|
$ qfg override my.flag true --env=staging
|
|
996
996
|
```
|
|
997
997
|
|
|
998
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
998
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/override.ts)_
|
|
999
999
|
|
|
1000
1000
|
## `qfg pull`
|
|
1001
1001
|
|
|
@@ -1039,7 +1039,7 @@ EXAMPLES
|
|
|
1039
1039
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
1040
1040
|
```
|
|
1041
1041
|
|
|
1042
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1042
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/pull.ts)_
|
|
1043
1043
|
|
|
1044
1044
|
## `qfg push`
|
|
1045
1045
|
|
|
@@ -1087,7 +1087,7 @@ EXAMPLES
|
|
|
1087
1087
|
$ qfg push --dir ./our-config --yes
|
|
1088
1088
|
```
|
|
1089
1089
|
|
|
1090
|
-
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1090
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/push.ts)_
|
|
1091
1091
|
|
|
1092
1092
|
## `qfg schema NAME`
|
|
1093
1093
|
|
|
@@ -1125,7 +1125,7 @@ EXAMPLES
|
|
|
1125
1125
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
1126
1126
|
```
|
|
1127
1127
|
|
|
1128
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1128
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/schema.ts)_
|
|
1129
1129
|
|
|
1130
1130
|
## `qfg sdk-key`
|
|
1131
1131
|
|
|
@@ -1152,7 +1152,7 @@ EXAMPLES
|
|
|
1152
1152
|
$ qfg sdk-key revoke <key-id>
|
|
1153
1153
|
```
|
|
1154
1154
|
|
|
1155
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1155
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key.ts)_
|
|
1156
1156
|
|
|
1157
1157
|
## `qfg sdk-key create`
|
|
1158
1158
|
|
|
@@ -1184,7 +1184,7 @@ EXAMPLES
|
|
|
1184
1184
|
$ qfg sdk-key create --environment staging --type browser
|
|
1185
1185
|
```
|
|
1186
1186
|
|
|
1187
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1187
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/create.ts)_
|
|
1188
1188
|
|
|
1189
1189
|
## `qfg sdk-key list`
|
|
1190
1190
|
|
|
@@ -1213,7 +1213,7 @@ EXAMPLES
|
|
|
1213
1213
|
$ qfg sdk-key list --environment production
|
|
1214
1214
|
```
|
|
1215
1215
|
|
|
1216
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1216
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/list.ts)_
|
|
1217
1217
|
|
|
1218
1218
|
## `qfg sdk-key revoke KEYID`
|
|
1219
1219
|
|
|
@@ -1240,7 +1240,7 @@ EXAMPLES
|
|
|
1240
1240
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1241
1241
|
```
|
|
1242
1242
|
|
|
1243
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1243
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sdk-key/revoke.ts)_
|
|
1244
1244
|
|
|
1245
1245
|
## `qfg set-default [NAME]`
|
|
1246
1246
|
|
|
@@ -1311,7 +1311,7 @@ EXAMPLES
|
|
|
1311
1311
|
$ qfg pull && qfg config-schema
|
|
1312
1312
|
```
|
|
1313
1313
|
|
|
1314
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1314
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/set-default.ts)_
|
|
1315
1315
|
|
|
1316
1316
|
## `qfg set-rollout [NAME]`
|
|
1317
1317
|
|
|
@@ -1388,7 +1388,7 @@ EXAMPLES
|
|
|
1388
1388
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1389
1389
|
```
|
|
1390
1390
|
|
|
1391
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1391
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/set-rollout.ts)_
|
|
1392
1392
|
|
|
1393
1393
|
## `qfg sync`
|
|
1394
1394
|
|
|
@@ -1420,7 +1420,7 @@ EXAMPLES
|
|
|
1420
1420
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1421
1421
|
```
|
|
1422
1422
|
|
|
1423
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1423
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/sync.ts)_
|
|
1424
1424
|
|
|
1425
1425
|
## `qfg toggle [NAME]`
|
|
1426
1426
|
|
|
@@ -1522,7 +1522,7 @@ EXAMPLES
|
|
|
1522
1522
|
$ qfg verify --json
|
|
1523
1523
|
```
|
|
1524
1524
|
|
|
1525
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1525
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/verify.ts)_
|
|
1526
1526
|
|
|
1527
1527
|
## `qfg whoami`
|
|
1528
1528
|
|
|
@@ -1545,7 +1545,7 @@ EXAMPLES
|
|
|
1545
1545
|
$ qfg whoami
|
|
1546
1546
|
```
|
|
1547
1547
|
|
|
1548
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1548
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/whoami.ts)_
|
|
1549
1549
|
|
|
1550
1550
|
## `qfg workspace`
|
|
1551
1551
|
|
|
@@ -1568,7 +1568,7 @@ EXAMPLES
|
|
|
1568
1568
|
$ qfg workspace
|
|
1569
1569
|
```
|
|
1570
1570
|
|
|
1571
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1571
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace.ts)_
|
|
1572
1572
|
|
|
1573
1573
|
## `qfg workspace bootstrap`
|
|
1574
1574
|
|
|
@@ -1601,7 +1601,7 @@ EXAMPLES
|
|
|
1601
1601
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1602
1602
|
```
|
|
1603
1603
|
|
|
1604
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1604
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/bootstrap.ts)_
|
|
1605
1605
|
|
|
1606
1606
|
## `qfg workspace create SLUG`
|
|
1607
1607
|
|
|
@@ -1635,7 +1635,7 @@ EXAMPLES
|
|
|
1635
1635
|
$ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
|
|
1636
1636
|
```
|
|
1637
1637
|
|
|
1638
|
-
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1638
|
+
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/create.ts)_
|
|
1639
1639
|
|
|
1640
1640
|
## `qfg workspace switch [SLUG]`
|
|
1641
1641
|
|
|
@@ -1663,7 +1663,7 @@ EXAMPLES
|
|
|
1663
1663
|
$ qfg workspace switch my-workspace-slug
|
|
1664
1664
|
```
|
|
1665
1665
|
|
|
1666
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1666
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.24/src/commands/workspace/switch.ts)_
|
|
1667
1667
|
<!-- commandsstop -->
|
|
1668
1668
|
|
|
1669
1669
|
## Local Development
|
|
@@ -122,9 +122,7 @@ For more complex rules (regex, multi-criterion, exact match) edit the JSON:
|
|
|
122
122
|
// Merge semantics: if an existing rule has the same exact targeting
|
|
123
123
|
// criterion (same propertyName, same valueToMatch set), replace its value.
|
|
124
124
|
// Otherwise insert the new rule before the catch-all so it takes priority.
|
|
125
|
-
const existingRules = environment
|
|
126
|
-
? findEnvRules(current, environment)
|
|
127
|
-
: current.default?.rules ?? [];
|
|
125
|
+
const existingRules = environment ? findEnvRules(current, environment) : (current.default?.rules ?? []);
|
|
128
126
|
const mergedRules = mergeTargetingRule(existingRules, newRule);
|
|
129
127
|
const logLevelPatch = environment
|
|
130
128
|
? { environments: replaceEnvRules(current.environments ?? [], environment, mergedRules) }
|
package/dist/commands/login.js
CHANGED
|
@@ -3,6 +3,7 @@ import { select } from '@inquirer/prompts';
|
|
|
3
3
|
import { BaseCommand } from '../index.js';
|
|
4
4
|
import { decodeJWT, pollForToken, requestDeviceCode } from '../util/oauth-client.js';
|
|
5
5
|
import { getApiUrl } from '../util/domain-urls.js';
|
|
6
|
+
import { openBrowser } from '../util/open-browser.js';
|
|
6
7
|
import { getAuthConfigFilePath, getTokenFilePath, loadAuthConfig, saveAuthConfig, saveTokens, } from '../util/token-storage.js';
|
|
7
8
|
export default class Login extends BaseCommand {
|
|
8
9
|
static description = 'Log in to Quonfig via WorkOS device authorization';
|
|
@@ -27,9 +28,37 @@ export default class Login extends BaseCommand {
|
|
|
27
28
|
this.log(`\nTo authenticate, visit:\n`);
|
|
28
29
|
this.log(` ${deviceAuth.verification_uri_complete}\n`);
|
|
29
30
|
this.log(`Or go to ${deviceAuth.verification_uri} and enter code: ${deviceAuth.user_code}\n`);
|
|
31
|
+
// Listen for Enter to open the URL in the user's browser.
|
|
32
|
+
// Runs concurrently with token polling so the user can also authenticate
|
|
33
|
+
// by pasting the URL manually without having to press Enter first.
|
|
34
|
+
const stdinIsTTY = Boolean(process.stdin.isTTY);
|
|
35
|
+
let browserOpened = false;
|
|
36
|
+
const onEnter = (chunk) => {
|
|
37
|
+
if (browserOpened)
|
|
38
|
+
return;
|
|
39
|
+
if (!chunk.toString().match(/[\r\n]/))
|
|
40
|
+
return;
|
|
41
|
+
browserOpened = true;
|
|
42
|
+
openBrowser(deviceAuth.verification_uri_complete);
|
|
43
|
+
this.log('Opening browser...');
|
|
44
|
+
};
|
|
45
|
+
if (stdinIsTTY) {
|
|
46
|
+
this.log('Press Enter to open the URL in your browser, or open it manually.');
|
|
47
|
+
process.stdin.on('data', onEnter);
|
|
48
|
+
process.stdin.resume();
|
|
49
|
+
}
|
|
30
50
|
this.log('Waiting for authentication...');
|
|
31
51
|
// Step 3: Poll for token
|
|
32
|
-
|
|
52
|
+
let tokenResponse;
|
|
53
|
+
try {
|
|
54
|
+
tokenResponse = await pollForToken(deviceAuth.device_code, deviceAuth.interval, deviceAuth.expires_in, this.isVerbose);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (stdinIsTTY) {
|
|
58
|
+
process.stdin.off('data', onEnter);
|
|
59
|
+
process.stdin.pause();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
33
62
|
const user = tokenResponse.user;
|
|
34
63
|
const userEmail = user.email;
|
|
35
64
|
// Decode JWT to inspect claims
|
package/dist/commands/pull.js
CHANGED
|
@@ -186,9 +186,9 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
186
186
|
async maybeWriteQuonfigDir(dir) {
|
|
187
187
|
if (process.env.QUONFIG_DIR)
|
|
188
188
|
return; // already set via env
|
|
189
|
-
const os = await import('node:os');
|
|
190
189
|
const pathMod = await import('node:path');
|
|
191
|
-
const
|
|
190
|
+
const { getQuonfigConfigHome } = await import('../util/quonfig-home.js');
|
|
191
|
+
const configFilePath = pathMod.join(getQuonfigConfigHome(), 'config');
|
|
192
192
|
try {
|
|
193
193
|
const existing = await fs.promises.readFile(configFilePath, 'utf8').catch(() => '');
|
|
194
194
|
if (existing.includes('QUONFIG_DIR') || existing.includes('quonfig_dir'))
|
package/dist/commands/push.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { JsonObj } from '../result.js';
|
|
2
2
|
import { BaseCommand } from '../index.js';
|
|
3
3
|
import { GiteaTokenResponse } from '../util/gitea-api.js';
|
|
4
|
-
import type
|
|
5
|
-
import { runPush } from '../push/run-push.js';
|
|
4
|
+
import { runPush, type GiteaTokenMintResult } from '../push/run-push.js';
|
|
6
5
|
export default class Push extends BaseCommand {
|
|
7
6
|
static description: string;
|
|
8
7
|
static examples: string[];
|
|
@@ -34,20 +33,13 @@ export declare function giteaResponseToMintResult(resp: GiteaTokenResponse): Git
|
|
|
34
33
|
*
|
|
35
34
|
* Returns a `{deps, cleanup}` pair. The caller MUST call `cleanup()` in a
|
|
36
35
|
* finally block so the bare-path probe clone is removed even on errors.
|
|
36
|
+
*
|
|
37
|
+
* As of qfg-azk.13 the push code path no longer mints a write-scoped Gitea
|
|
38
|
+
* token; the actual commit is performed server-side by the `configs.push`
|
|
39
|
+
* oRPC procedure. We still mint a READ token to authenticate the bare-path
|
|
40
|
+
* probe-clone and the clone-path `git fetch`.
|
|
37
41
|
*/
|
|
38
42
|
export declare function buildRealDeps(cmd: Push): {
|
|
39
43
|
deps: Parameters<typeof runPush>[1];
|
|
40
44
|
cleanup: () => Promise<void>;
|
|
41
45
|
};
|
|
42
|
-
/**
|
|
43
|
-
* Copy the contents of `sourceDir` into `destDir`, skipping `.git/` and any
|
|
44
|
-
* temp push-clone scratch directories. Used as the `applyDelta` callback for
|
|
45
|
-
* bare-path pushes — we mirror local files into a fresh clone so that
|
|
46
|
-
* `cloneAndStackPush`'s `git add --all` + diff will reflect the union of
|
|
47
|
-
* (deletes, modifies, adds) we intend.
|
|
48
|
-
*
|
|
49
|
-
* IMPORTANT: this deletes files in destDir that are not in sourceDir so that
|
|
50
|
-
* deletions make it into the push. Without this the clone-and-stack produces
|
|
51
|
-
* an "adds-only" delta and destructive heuristics would never fire in the UI.
|
|
52
|
-
*/
|
|
53
|
-
export declare function copyDirMirror(sourceDir: string, destDir: string): Promise<void>;
|
package/dist/commands/push.js
CHANGED
|
@@ -5,9 +5,10 @@ import * as util from 'node:util';
|
|
|
5
5
|
import { Flags } from '@oclif/core';
|
|
6
6
|
import { BaseCommand } from '../index.js';
|
|
7
7
|
import { mintGiteaToken } from '../util/gitea-api.js';
|
|
8
|
-
import { getRemoteUrl, gitFetch, gitSetRemote, isGitRepo
|
|
9
|
-
import { PushFatalError, runPush } from '../push/run-push.js';
|
|
8
|
+
import { getRemoteUrl, gitFetch, gitSetRemote, isGitRepo } from '../util/git-ops.js';
|
|
9
|
+
import { PushFatalError, runPush, } from '../push/run-push.js';
|
|
10
10
|
import { computeBarePathDiff } from '../push/bare-path-diff.js';
|
|
11
|
+
import { callConfigsPush } from '../push/config-push-client.js';
|
|
11
12
|
import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
|
|
12
13
|
const execFile = util.promisify(execFileCb);
|
|
13
14
|
export default class Push extends BaseCommand {
|
|
@@ -127,6 +128,11 @@ export function giteaResponseToMintResult(resp) {
|
|
|
127
128
|
*
|
|
128
129
|
* Returns a `{deps, cleanup}` pair. The caller MUST call `cleanup()` in a
|
|
129
130
|
* finally block so the bare-path probe clone is removed even on errors.
|
|
131
|
+
*
|
|
132
|
+
* As of qfg-azk.13 the push code path no longer mints a write-scoped Gitea
|
|
133
|
+
* token; the actual commit is performed server-side by the `configs.push`
|
|
134
|
+
* oRPC procedure. We still mint a READ token to authenticate the bare-path
|
|
135
|
+
* probe-clone and the clone-path `git fetch`.
|
|
130
136
|
*/
|
|
131
137
|
export function buildRealDeps(cmd) {
|
|
132
138
|
const log = (line) => cmd.log(line);
|
|
@@ -187,12 +193,6 @@ export function buildRealDeps(cmd) {
|
|
|
187
193
|
const probe = await ensureBarePathProbe(dir);
|
|
188
194
|
return probe.deltas;
|
|
189
195
|
},
|
|
190
|
-
async push(dir) {
|
|
191
|
-
await execFile('git', ['-C', dir, 'push', 'origin', 'main']);
|
|
192
|
-
},
|
|
193
|
-
async getLocalAuthor(dir) {
|
|
194
|
-
return readLocalAuthor(dir);
|
|
195
|
-
},
|
|
196
196
|
async countFilesInRemote(dir) {
|
|
197
197
|
if (await isGitRepo(dir)) {
|
|
198
198
|
const fromGit = await countTrackedFilesAtRef(dir, 'origin/main');
|
|
@@ -207,9 +207,11 @@ export function buildRealDeps(cmd) {
|
|
|
207
207
|
};
|
|
208
208
|
const deps = {
|
|
209
209
|
async mintWriteToken(requestedTarget) {
|
|
210
|
-
|
|
211
|
-
//
|
|
212
|
-
//
|
|
210
|
+
// Read scope: this token authenticates probe-clone and clone-path
|
|
211
|
+
// fetch. The actual commit goes through the server via configs.push,
|
|
212
|
+
// which is authorized off the user's WorkOS session — no write PAT
|
|
213
|
+
// needed in the push code path (qfg-azk.13).
|
|
214
|
+
const resp = await mintGiteaToken(requestedTarget, 'read', 'pull');
|
|
213
215
|
authenticatedRepoUrl = resp.repoUrl;
|
|
214
216
|
return giteaResponseToMintResult(resp);
|
|
215
217
|
},
|
|
@@ -220,7 +222,7 @@ export function buildRealDeps(cmd) {
|
|
|
220
222
|
return { errors };
|
|
221
223
|
},
|
|
222
224
|
gitOps,
|
|
223
|
-
|
|
225
|
+
pushToServer: callConfigsPush,
|
|
224
226
|
log,
|
|
225
227
|
errLog,
|
|
226
228
|
};
|
|
@@ -237,26 +239,20 @@ export function buildRealDeps(cmd) {
|
|
|
237
239
|
return { deps, cleanup };
|
|
238
240
|
}
|
|
239
241
|
/**
|
|
240
|
-
* Diff the working tree (what we are about to push) against origin/main
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
242
|
+
* Diff the working tree (what we are about to push) against origin/main and
|
|
243
|
+
* fill in before/after JSON content for each delta as required by the
|
|
244
|
+
* `configs.push` wire shape (qfg-azk.13).
|
|
245
|
+
*
|
|
246
|
+
* Added — afterJson = working tree
|
|
247
|
+
* Modified — beforeJson = `git show origin/main:<path>`, afterJson = working tree
|
|
248
|
+
* Deleted — beforeJson = `git show origin/main:<path>`
|
|
245
249
|
*
|
|
246
250
|
* `git diff A..B` reports B relative to A. So `HEAD..origin/main` tells us
|
|
247
251
|
* what origin has that we don't. We want "local vs remote" with local as the
|
|
248
252
|
* baseline — i.e. origin/main..HEAD.
|
|
249
253
|
*/
|
|
250
254
|
async function diffHeadVsOrigin(dir) {
|
|
251
|
-
|
|
252
|
-
// 'A' -> added locally, 'D' -> deleted locally, 'M' -> modified locally.
|
|
253
|
-
const { stdout } = await execFile('git', [
|
|
254
|
-
'-C',
|
|
255
|
-
dir,
|
|
256
|
-
'diff',
|
|
257
|
-
'--name-status',
|
|
258
|
-
'origin/main..HEAD',
|
|
259
|
-
]);
|
|
255
|
+
const { stdout } = await execFile('git', ['-C', dir, 'diff', '--name-status', 'origin/main..HEAD']);
|
|
260
256
|
const deltas = [];
|
|
261
257
|
for (const raw of stdout.split('\n')) {
|
|
262
258
|
const line = raw.trim();
|
|
@@ -266,95 +262,54 @@ async function diffHeadVsOrigin(dir) {
|
|
|
266
262
|
const pathStr = rest.join(' ');
|
|
267
263
|
if (!pathStr)
|
|
268
264
|
continue;
|
|
269
|
-
if (status.startsWith('A'))
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
deltas.push({ kind: '
|
|
265
|
+
if (status.startsWith('A')) {
|
|
266
|
+
// eslint-disable-next-line no-await-in-loop
|
|
267
|
+
const afterJson = await readWorkingTreeFile(dir, pathStr);
|
|
268
|
+
deltas.push({ kind: 'added', path: pathStr, ...(afterJson === undefined ? {} : { afterJson }) });
|
|
269
|
+
}
|
|
270
|
+
else if (status.startsWith('D')) {
|
|
271
|
+
// eslint-disable-next-line no-await-in-loop
|
|
272
|
+
const beforeJson = await showAtRef(dir, 'origin/main', pathStr);
|
|
273
|
+
deltas.push({ kind: 'deleted', path: pathStr, ...(beforeJson === undefined ? {} : { beforeJson }) });
|
|
274
|
+
}
|
|
273
275
|
else if (status.startsWith('M') || status.startsWith('R') || status.startsWith('C')) {
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
+
// eslint-disable-next-line no-await-in-loop
|
|
277
|
+
const beforeJson = await showAtRef(dir, 'origin/main', pathStr);
|
|
278
|
+
// eslint-disable-next-line no-await-in-loop
|
|
279
|
+
const afterJson = await readWorkingTreeFile(dir, pathStr);
|
|
280
|
+
deltas.push({
|
|
281
|
+
kind: 'modified',
|
|
282
|
+
path: pathStr,
|
|
283
|
+
...(beforeJson === undefined ? {} : { beforeJson }),
|
|
284
|
+
...(afterJson === undefined ? {} : { afterJson }),
|
|
285
|
+
});
|
|
276
286
|
}
|
|
277
287
|
}
|
|
278
288
|
return deltas;
|
|
279
289
|
}
|
|
280
|
-
async function
|
|
290
|
+
async function showAtRef(dir, ref, relPath) {
|
|
281
291
|
try {
|
|
282
|
-
const { stdout } = await execFile('git', ['-C', dir, '
|
|
283
|
-
return stdout
|
|
292
|
+
const { stdout } = await execFile('git', ['-C', dir, 'show', `${ref}:${relPath}`]);
|
|
293
|
+
return stdout;
|
|
284
294
|
}
|
|
285
295
|
catch {
|
|
286
|
-
return
|
|
296
|
+
return undefined;
|
|
287
297
|
}
|
|
288
298
|
}
|
|
289
|
-
async function
|
|
299
|
+
async function readWorkingTreeFile(dir, relPath) {
|
|
290
300
|
try {
|
|
291
|
-
|
|
292
|
-
const { stdout: email } = await execFile('git', ['-C', dir, 'config', 'user.email']);
|
|
293
|
-
const n = name.trim();
|
|
294
|
-
const e = email.trim();
|
|
295
|
-
if (!n || !e)
|
|
296
|
-
return undefined;
|
|
297
|
-
return { name: n, email: e };
|
|
301
|
+
return await fs.promises.readFile(path.join(dir, relPath), 'utf8');
|
|
298
302
|
}
|
|
299
303
|
catch {
|
|
300
304
|
return undefined;
|
|
301
305
|
}
|
|
302
306
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
* `cloneAndStackPush`'s `git add --all` + diff will reflect the union of
|
|
308
|
-
* (deletes, modifies, adds) we intend.
|
|
309
|
-
*
|
|
310
|
-
* IMPORTANT: this deletes files in destDir that are not in sourceDir so that
|
|
311
|
-
* deletions make it into the push. Without this the clone-and-stack produces
|
|
312
|
-
* an "adds-only" delta and destructive heuristics would never fire in the UI.
|
|
313
|
-
*/
|
|
314
|
-
export async function copyDirMirror(sourceDir, destDir) {
|
|
315
|
-
// Collect source entries (except .git, .quonfig-push-clone-*)
|
|
316
|
-
const sourceRel = collectRelPaths(sourceDir);
|
|
317
|
-
const destRel = collectRelPaths(destDir);
|
|
318
|
-
// Delete anything in dest that isn't in source
|
|
319
|
-
for (const rel of destRel) {
|
|
320
|
-
if (!sourceRel.has(rel)) {
|
|
321
|
-
const full = path.join(destDir, rel);
|
|
322
|
-
try {
|
|
323
|
-
fs.rmSync(full, { force: true });
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
/* ignore */
|
|
327
|
-
}
|
|
328
|
-
}
|
|
307
|
+
async function countTrackedFilesAtRef(dir, ref) {
|
|
308
|
+
try {
|
|
309
|
+
const { stdout } = await execFile('git', ['-C', dir, 'ls-tree', '-r', '--name-only', ref]);
|
|
310
|
+
return stdout.split('\n').filter((l) => l.trim().length > 0).length;
|
|
329
311
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const src = path.join(sourceDir, rel);
|
|
333
|
-
const dst = path.join(destDir, rel);
|
|
334
|
-
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
335
|
-
fs.copyFileSync(src, dst);
|
|
312
|
+
catch {
|
|
313
|
+
return 0;
|
|
336
314
|
}
|
|
337
315
|
}
|
|
338
|
-
function collectRelPaths(root) {
|
|
339
|
-
const out = new Set();
|
|
340
|
-
if (!fs.existsSync(root))
|
|
341
|
-
return out;
|
|
342
|
-
const walk = (dir, prefix) => {
|
|
343
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
344
|
-
if (entry.name === '.git')
|
|
345
|
-
continue;
|
|
346
|
-
if (entry.name.startsWith('.quonfig-push-clone-'))
|
|
347
|
-
continue;
|
|
348
|
-
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
349
|
-
const full = path.join(dir, entry.name);
|
|
350
|
-
if (entry.isDirectory()) {
|
|
351
|
-
walk(full, rel);
|
|
352
|
-
}
|
|
353
|
-
else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
354
|
-
out.add(rel);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
walk(root, '');
|
|
359
|
-
return out;
|
|
360
|
-
}
|
|
@@ -13,10 +13,7 @@ export default class WorkspaceSwitch extends BaseCommand {
|
|
|
13
13
|
}),
|
|
14
14
|
};
|
|
15
15
|
static description = 'Switch to a different workspace';
|
|
16
|
-
static examples = [
|
|
17
|
-
'<%= config.bin %> <%= command.id %>',
|
|
18
|
-
'<%= config.bin %> <%= command.id %> my-workspace-slug',
|
|
19
|
-
];
|
|
16
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> my-workspace-slug'];
|
|
20
17
|
async run() {
|
|
21
18
|
const { args } = await this.parse(WorkspaceSwitch);
|
|
22
19
|
// Get a valid token — refresh silently if needed, error if not logged in
|