@quonfig/cli 0.0.53 → 0.0.55

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 CHANGED
@@ -17,7 +17,7 @@ $ npm install -g @quonfig/cli
17
17
  $ qfg COMMAND
18
18
  running command...
19
19
  $ qfg (--version)
20
- @quonfig/cli/0.0.53 linux-x64 node-v24.4.1
20
+ @quonfig/cli/0.0.55 linux-x64 node-v24.4.1
21
21
  $ qfg --help [COMMAND]
22
22
  USAGE
23
23
  $ qfg COMMAND
@@ -196,7 +196,7 @@ EXAMPLES
196
196
  $ qfg activity restore my.flag --yes
197
197
  ```
198
198
 
199
- _See code: [src/commands/activity.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/activity.ts)_
199
+ _See code: [src/commands/activity.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity.ts)_
200
200
 
201
201
  ## `qfg activity deleted`
202
202
 
@@ -225,7 +225,7 @@ EXAMPLES
225
225
  $ qfg activity deleted --json
226
226
  ```
227
227
 
228
- _See code: [src/commands/activity/deleted.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/activity/deleted.ts)_
228
+ _See code: [src/commands/activity/deleted.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/deleted.ts)_
229
229
 
230
230
  ## `qfg activity feed`
231
231
 
@@ -260,7 +260,7 @@ EXAMPLES
260
260
  $ qfg activity feed --json
261
261
  ```
262
262
 
263
- _See code: [src/commands/activity/feed.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/activity/feed.ts)_
263
+ _See code: [src/commands/activity/feed.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/feed.ts)_
264
264
 
265
265
  ## `qfg activity history [NAME]`
266
266
 
@@ -294,7 +294,7 @@ EXAMPLES
294
294
  $ qfg activity history request.timeout --json
295
295
  ```
296
296
 
297
- _See code: [src/commands/activity/history.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/activity/history.ts)_
297
+ _See code: [src/commands/activity/history.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/history.ts)_
298
298
 
299
299
  ## `qfg activity restore [NAME]`
300
300
 
@@ -333,7 +333,7 @@ EXAMPLES
333
333
  $ qfg activity restore request.timeout --type config --yes
334
334
  ```
335
335
 
336
- _See code: [src/commands/activity/restore.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/activity/restore.ts)_
336
+ _See code: [src/commands/activity/restore.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/restore.ts)_
337
337
 
338
338
  ## `qfg audit-log [NAME]`
339
339
 
@@ -361,7 +361,7 @@ EXAMPLES
361
361
  $ qfg audit-log my.flag
362
362
  ```
363
363
 
364
- _See code: [src/commands/audit-log.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/audit-log.ts)_
364
+ _See code: [src/commands/audit-log.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/audit-log.ts)_
365
365
 
366
366
  ## `qfg config-schema`
367
367
 
@@ -390,7 +390,7 @@ EXAMPLES
390
390
  $ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
391
391
  ```
392
392
 
393
- _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/config-schema.ts)_
393
+ _See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/config-schema.ts)_
394
394
 
395
395
  ## `qfg create NAME`
396
396
 
@@ -475,7 +475,7 @@ EXAMPLES
475
475
  $ qfg set-rollout my.new.flag --environment production --true-percent 20
476
476
  ```
477
477
 
478
- _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/create.ts)_
478
+ _See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/create.ts)_
479
479
 
480
480
  ## `qfg delete NAME`
481
481
 
@@ -521,7 +521,7 @@ EXAMPLES
521
521
  $ qfg delete my.config --yes
522
522
  ```
523
523
 
524
- _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/delete.ts)_
524
+ _See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/delete.ts)_
525
525
 
526
526
  ## `qfg flag info [NAME]`
527
527
 
@@ -725,7 +725,7 @@ EXAMPLES
725
725
  $ qfg generate --targets node-ts -o ./dist # combine with targets
726
726
  ```
727
727
 
728
- _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/generate.ts)_
728
+ _See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/generate.ts)_
729
729
 
730
730
  ## `qfg generate-new-hex-key`
731
731
 
@@ -748,7 +748,7 @@ EXAMPLES
748
748
  $ qfg generate-new-hex-key
749
749
  ```
750
750
 
751
- _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/generate-new-hex-key.ts)_
751
+ _See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/generate-new-hex-key.ts)_
752
752
 
753
753
  ## `qfg get [NAME]`
754
754
 
@@ -780,7 +780,7 @@ EXAMPLES
780
780
  $ qfg get my.config.name --environment=production
781
781
  ```
782
782
 
783
- _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/get.ts)_
783
+ _See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/get.ts)_
784
784
 
785
785
  ## `qfg history NAME`
786
786
 
@@ -806,7 +806,7 @@ EXAMPLES
806
806
  $ qfg history my.flag
807
807
  ```
808
808
 
809
- _See code: [src/commands/history.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/history.ts)_
809
+ _See code: [src/commands/history.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/history.ts)_
810
810
 
811
811
  ## `qfg info [NAME]`
812
812
 
@@ -854,7 +854,7 @@ EXAMPLES
854
854
  $ qfg info my.config.name --exclude-evaluations # skip 24h stats
855
855
  ```
856
856
 
857
- _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/info.ts)_
857
+ _See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/info.ts)_
858
858
 
859
859
  ## `qfg init [DIRECTORY]`
860
860
 
@@ -894,7 +894,7 @@ EXAMPLES
894
894
  $ qfg init --dry-run
895
895
  ```
896
896
 
897
- _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/init.ts)_
897
+ _See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/init.ts)_
898
898
 
899
899
  ## `qfg interactive`
900
900
 
@@ -924,7 +924,7 @@ EXAMPLES
924
924
  $ qfg
925
925
  ```
926
926
 
927
- _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/interactive.ts)_
927
+ _See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/interactive.ts)_
928
928
 
929
929
  ## `qfg list`
930
930
 
@@ -964,7 +964,7 @@ EXAMPLES
964
964
  $ qfg list --feature-flags
965
965
  ```
966
966
 
967
- _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/list.ts)_
967
+ _See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/list.ts)_
968
968
 
969
969
  ## `qfg log`
970
970
 
@@ -989,7 +989,7 @@ EXAMPLES
989
989
  $ qfg log --limit 5
990
990
  ```
991
991
 
992
- _See code: [src/commands/log.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/log.ts)_
992
+ _See code: [src/commands/log.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/log.ts)_
993
993
 
994
994
  ## `qfg log-level NAME`
995
995
 
@@ -1054,7 +1054,7 @@ EXAMPLES
1054
1054
  $ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
1055
1055
  ```
1056
1056
 
1057
- _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/log-level.ts)_
1057
+ _See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/log-level.ts)_
1058
1058
 
1059
1059
  ## `qfg login`
1060
1060
 
@@ -1079,7 +1079,7 @@ EXAMPLES
1079
1079
  $ qfg login --profile myprofile
1080
1080
  ```
1081
1081
 
1082
- _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/login.ts)_
1082
+ _See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/login.ts)_
1083
1083
 
1084
1084
  ## `qfg logout`
1085
1085
 
@@ -1102,7 +1102,7 @@ EXAMPLES
1102
1102
  $ qfg logout
1103
1103
  ```
1104
1104
 
1105
- _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/logout.ts)_
1105
+ _See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/logout.ts)_
1106
1106
 
1107
1107
  ## `qfg migrate`
1108
1108
 
@@ -1165,7 +1165,7 @@ EXAMPLES
1165
1165
  $ qfg migrate --from launch --source-api-key $LAUNCH_API_KEY --dir ./quonfig-repo --staging
1166
1166
  ```
1167
1167
 
1168
- _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/migrate.ts)_
1168
+ _See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate.ts)_
1169
1169
 
1170
1170
  ## `qfg migrate doctor`
1171
1171
 
@@ -1203,7 +1203,7 @@ EXAMPLES
1203
1203
  $ qfg migrate doctor --json
1204
1204
  ```
1205
1205
 
1206
- _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/migrate/doctor.ts)_
1206
+ _See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/doctor.ts)_
1207
1207
 
1208
1208
  ## `qfg migrate my-code`
1209
1209
 
@@ -1235,7 +1235,7 @@ EXAMPLES
1235
1235
  $ qfg migrate my-code --dry-run
1236
1236
  ```
1237
1237
 
1238
- _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/migrate/my-code.ts)_
1238
+ _See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/my-code.ts)_
1239
1239
 
1240
1240
  ## `qfg migrate status`
1241
1241
 
@@ -1266,7 +1266,7 @@ EXAMPLES
1266
1266
  $ qfg migrate status --json
1267
1267
  ```
1268
1268
 
1269
- _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/migrate/status.ts)_
1269
+ _See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/status.ts)_
1270
1270
 
1271
1271
  ## `qfg override [NAME] [VALUE]`
1272
1272
 
@@ -1321,7 +1321,7 @@ EXAMPLES
1321
1321
  $ qfg override my.flag true --env=staging
1322
1322
  ```
1323
1323
 
1324
- _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/override.ts)_
1324
+ _See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/override.ts)_
1325
1325
 
1326
1326
  ## `qfg pull`
1327
1327
 
@@ -1369,7 +1369,7 @@ EXAMPLES
1369
1369
  $ qfg pull # uses QUONFIG_DIR env var
1370
1370
  ```
1371
1371
 
1372
- _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/pull.ts)_
1372
+ _See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/pull.ts)_
1373
1373
 
1374
1374
  ## `qfg push`
1375
1375
 
@@ -1418,7 +1418,7 @@ EXAMPLES
1418
1418
  $ qfg push --dir ./our-config --yes
1419
1419
  ```
1420
1420
 
1421
- _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/push.ts)_
1421
+ _See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/push.ts)_
1422
1422
 
1423
1423
  ## `qfg run [COMMAND]`
1424
1424
 
@@ -1469,7 +1469,7 @@ EXAMPLES
1469
1469
  $ qfg run --env DATABASE_URL=db.url --preserve-env --environment=staging -- npm test
1470
1470
  ```
1471
1471
 
1472
- _See code: [src/commands/run.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/run.ts)_
1472
+ _See code: [src/commands/run.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/run.ts)_
1473
1473
 
1474
1474
  ## `qfg schema NAME`
1475
1475
 
@@ -1507,7 +1507,7 @@ EXAMPLES
1507
1507
  $ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
1508
1508
  ```
1509
1509
 
1510
- _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/schema.ts)_
1510
+ _See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/schema.ts)_
1511
1511
 
1512
1512
  ## `qfg sdk-key`
1513
1513
 
@@ -1534,7 +1534,7 @@ EXAMPLES
1534
1534
  $ qfg sdk-key revoke <key-id>
1535
1535
  ```
1536
1536
 
1537
- _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/sdk-key.ts)_
1537
+ _See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key.ts)_
1538
1538
 
1539
1539
  ## `qfg sdk-key create`
1540
1540
 
@@ -1566,7 +1566,7 @@ EXAMPLES
1566
1566
  $ qfg sdk-key create --environment staging --type browser
1567
1567
  ```
1568
1568
 
1569
- _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/sdk-key/create.ts)_
1569
+ _See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/create.ts)_
1570
1570
 
1571
1571
  ## `qfg sdk-key list`
1572
1572
 
@@ -1595,7 +1595,7 @@ EXAMPLES
1595
1595
  $ qfg sdk-key list --environment production
1596
1596
  ```
1597
1597
 
1598
- _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/sdk-key/list.ts)_
1598
+ _See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/list.ts)_
1599
1599
 
1600
1600
  ## `qfg sdk-key revoke KEYID`
1601
1601
 
@@ -1622,7 +1622,7 @@ EXAMPLES
1622
1622
  $ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
1623
1623
  ```
1624
1624
 
1625
- _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/sdk-key/revoke.ts)_
1625
+ _See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/revoke.ts)_
1626
1626
 
1627
1627
  ## `qfg serve`
1628
1628
 
@@ -1679,7 +1679,7 @@ EXAMPLES
1679
1679
  $ qfg serve --port 6581 --frontend-sdk-key PUBLIC_KEY
1680
1680
  ```
1681
1681
 
1682
- _See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/serve.ts)_
1682
+ _See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/serve.ts)_
1683
1683
 
1684
1684
  ## `qfg set-default [NAME]`
1685
1685
 
@@ -1750,7 +1750,7 @@ EXAMPLES
1750
1750
  $ qfg pull && qfg config-schema
1751
1751
  ```
1752
1752
 
1753
- _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/set-default.ts)_
1753
+ _See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/set-default.ts)_
1754
1754
 
1755
1755
  ## `qfg set-rollout [NAME]`
1756
1756
 
@@ -1827,7 +1827,7 @@ EXAMPLES
1827
1827
  $ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
1828
1828
  ```
1829
1829
 
1830
- _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/set-rollout.ts)_
1830
+ _See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/set-rollout.ts)_
1831
1831
 
1832
1832
  ## `qfg sync`
1833
1833
 
@@ -1859,7 +1859,7 @@ EXAMPLES
1859
1859
  $ qfg sync --watch --dir ./our-config --interval 30
1860
1860
  ```
1861
1861
 
1862
- _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/sync.ts)_
1862
+ _See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sync.ts)_
1863
1863
 
1864
1864
  ## `qfg toggle [NAME]`
1865
1865
 
@@ -1961,7 +1961,7 @@ EXAMPLES
1961
1961
  $ qfg verify --json
1962
1962
  ```
1963
1963
 
1964
- _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/verify.ts)_
1964
+ _See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/verify.ts)_
1965
1965
 
1966
1966
  ## `qfg whoami`
1967
1967
 
@@ -1984,7 +1984,7 @@ EXAMPLES
1984
1984
  $ qfg whoami
1985
1985
  ```
1986
1986
 
1987
- _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/whoami.ts)_
1987
+ _See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/whoami.ts)_
1988
1988
 
1989
1989
  ## `qfg workspace`
1990
1990
 
@@ -2007,7 +2007,7 @@ EXAMPLES
2007
2007
  $ qfg workspace
2008
2008
  ```
2009
2009
 
2010
- _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/workspace.ts)_
2010
+ _See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace.ts)_
2011
2011
 
2012
2012
  ## `qfg workspace bootstrap`
2013
2013
 
@@ -2040,7 +2040,7 @@ EXAMPLES
2040
2040
  $ qfg workspace bootstrap --dir ./our-config --skip-validate
2041
2041
  ```
2042
2042
 
2043
- _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/workspace/bootstrap.ts)_
2043
+ _See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/bootstrap.ts)_
2044
2044
 
2045
2045
  ## `qfg workspace create SLUG`
2046
2046
 
@@ -2074,7 +2074,7 @@ EXAMPLES
2074
2074
  $ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
2075
2075
  ```
2076
2076
 
2077
- _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/workspace/create.ts)_
2077
+ _See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/create.ts)_
2078
2078
 
2079
2079
  ## `qfg workspace switch [SLUG]`
2080
2080
 
@@ -2102,7 +2102,7 @@ EXAMPLES
2102
2102
  $ qfg workspace switch acme/production
2103
2103
  ```
2104
2104
 
2105
- _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.53/src/commands/workspace/switch.ts)_
2105
+ _See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/switch.ts)_
2106
2106
  <!-- commandsstop -->
2107
2107
 
2108
2108
  ## Local Development
@@ -19,5 +19,11 @@ export default class Migrate extends BaseCommand {
19
19
  workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
20
20
  };
21
21
  run(): Promise<JsonObj | void>;
22
+ /**
23
+ * After a local (non-push) migration commits, the user is left at a prompt
24
+ * with a fresh directory and no obvious next move. Spell out the review →
25
+ * push flow explicitly.
26
+ */
27
+ private printLocalNextSteps;
22
28
  private resolvePushWorkspace;
23
29
  }
@@ -5,6 +5,7 @@ import { BaseCommand } from '../index.js';
5
5
  import { CrossSourceError, assertSourceMatches, readImportState } from '../migrate/import-state.js';
6
6
  import { applyLocalMigration, MigratorKeyCollisionError } from '../migrate/local-write.js';
7
7
  import { buildPushConflictSuggestion } from '../migrate/migrate-suggestion.js';
8
+ import { migrationReportPath } from '../migrate/migration-report.js';
8
9
  import { PushConflictError, PushHookRejectedError } from '../util/clone-and-stack-push.js';
9
10
  import { MigratorVerifyError, pushMigrationToCloud } from '../migrate/push-to-cloud.js';
10
11
  import { UnknownSourceError, getSource } from '../migrate/registry.js';
@@ -20,6 +21,10 @@ import { displayUrl } from '../util/git-ops.js';
20
21
  import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
21
22
  import { loadAuthConfig } from '../util/token-storage.js';
22
23
  const DEFAULT_DIR = 'quonfig-repo';
24
+ // How many fetched changes between progress lines during the (silent,
25
+ // paginated) change-history fetch. Large accounts page through thousands of
26
+ // changes 50 at a time; without periodic output the CLI looks frozen.
27
+ const FETCH_PROGRESS_INTERVAL = 1000;
23
28
  export default class Migrate extends BaseCommand {
24
29
  static description = 'Migrate flags and configs from a legacy feature-flag system into a Quonfig workspace.\n\n' +
25
30
  'Currently supported sources:\n' +
@@ -172,9 +177,28 @@ export default class Migrate extends BaseCommand {
172
177
  return this.err(error.message);
173
178
  throw error;
174
179
  }
180
+ // The fetch and write phases below are silent for a long time on a large
181
+ // account. Print a plan up front and a step line before each slow phase so
182
+ // the CLI never looks frozen, even without --verbose.
183
+ this.log(`Migrating from ${flags.from} into ${dir}.`);
184
+ if (flags['dry-run']) {
185
+ this.log('Mode: dry run — changes are summarized but nothing is written.');
186
+ }
187
+ else if (flags.push) {
188
+ this.log('Mode: migrate to a local dir, then push to the Quonfig cloud workspace.');
189
+ }
190
+ else {
191
+ this.log('Mode: local migration only — re-run with --push to also publish to a cloud workspace.');
192
+ }
193
+ this.log('');
175
194
  let duplicateResolutionsForWarn = null;
195
+ // Set to the target dir once a local (non-push) migration commits, so the
196
+ // `finally` block can print "what now?" guidance after the warnings.
197
+ let localNextStepsDir = null;
176
198
  try {
199
+ this.log(`Authenticating with ${flags.from}...`);
177
200
  await source.validateAuth(sourceApiKey);
201
+ this.log(`Reading the environment list from ${flags.from}...`);
178
202
  const environments = await source.listEnvironments();
179
203
  // Plan §4.1.1: tell the user the real history horizon BEFORE the slow
180
204
  // Phase-2 walk starts. validateAuth populated this for LD under
@@ -187,8 +211,15 @@ export default class Migrate extends BaseCommand {
187
211
  }
188
212
  const existing = readImportState(dir);
189
213
  const sinceEpochMs = computeSince(flags, existing);
214
+ this.log(`Fetching change history from ${flags.from} — this can take a few minutes for a large account...`);
190
215
  const changes = [];
191
- for await (const change of source.fetchChanges(sinceEpochMs)) {
216
+ let lastReportedFetch = 0;
217
+ for await (const change of source.fetchChanges(sinceEpochMs, (fetched) => {
218
+ if (fetched - lastReportedFetch >= FETCH_PROGRESS_INTERVAL) {
219
+ lastReportedFetch = fetched;
220
+ this.log(` ...fetched ${fetched} change(s) so far`);
221
+ }
222
+ })) {
192
223
  changes.push(change);
193
224
  }
194
225
  const toProcess = flags.recent === undefined ? changes : changes.slice(-flags.recent);
@@ -239,6 +270,8 @@ export default class Migrate extends BaseCommand {
239
270
  this.log(result.committed
240
271
  ? `Pushed ${toProcess.length} change(s). commit=${result.commitSha?.slice(0, 8) ?? ''} action=${result.action}`
241
272
  : `No net changes produced by this run. Nothing to commit or push.`);
273
+ this.log(`Migration report written to ${migrationReportPath(dir)} — review it before cutover ` +
274
+ '(it lives in the hidden .qf/ directory).');
242
275
  return {
243
276
  ...payload,
244
277
  action: result.action,
@@ -285,6 +318,10 @@ export default class Migrate extends BaseCommand {
285
318
  this.log(localResult.committed
286
319
  ? `Committed ${toProcess.length} change(s). commit=${localResult.commitSha?.slice(0, 8) ?? ''} action=${localResult.action}`
287
320
  : `No net changes produced by this run. Nothing to commit.`);
321
+ // Print "what now?" guidance from the `finally` block (after the
322
+ // warnings) so it is the last thing the user sees.
323
+ if (localResult.committed)
324
+ localNextStepsDir = dir;
288
325
  return {
289
326
  ...payload,
290
327
  action: localResult.action,
@@ -307,8 +344,33 @@ export default class Migrate extends BaseCommand {
307
344
  warnAboutCoercedSentinels(this, source.getCoercedSentinels?.() ?? null);
308
345
  warnAboutDuplicateResolutions(this, duplicateResolutionsForWarn);
309
346
  warnAboutConversionNotes(this, source.getConversionNotes?.() ?? null);
347
+ if (localNextStepsDir)
348
+ this.printLocalNextSteps(localNextStepsDir);
310
349
  }
311
350
  }
351
+ /**
352
+ * After a local (non-push) migration commits, the user is left at a prompt
353
+ * with a fresh directory and no obvious next move. Spell out the review →
354
+ * push flow explicitly.
355
+ */
356
+ printLocalNextSteps(dir) {
357
+ this.log('');
358
+ this.log('Migration written to a local directory — nothing has been pushed to Quonfig cloud yet.');
359
+ this.log('');
360
+ this.log('Next steps:');
361
+ this.log(` 1. Review the migrated config files in ${dir}`);
362
+ this.log(' 2. Read the migration report — it lists every config that was skipped,');
363
+ this.log(' coerced, or needs a manual follow-up:');
364
+ this.log(` ${migrationReportPath(dir)}`);
365
+ this.log(' (it lives in the hidden .qf/ directory — use `ls -a` to see it)');
366
+ this.log(' 3. Publish to a Quonfig workspace once the migration looks right:');
367
+ this.log(` qfg push --dir ${dir} --workspace <org-slug>/<workspace-slug>`);
368
+ this.log(' (run `qfg login` first if you have not). You can also re-run this command');
369
+ this.log(' with `--push --workspace <org-slug>/<workspace-slug>` to migrate and push');
370
+ this.log(' in one step.');
371
+ this.log('');
372
+ this.log('Re-running `qfg migrate` later picks up only the changes made since this run.');
373
+ }
312
374
  async resolvePushWorkspace(slugOrId) {
313
375
  const authConfig = await loadAuthConfig();
314
376
  if (!authConfig)
@@ -368,7 +430,7 @@ function warnAboutDroppedOverrides(cmd, dropped) {
368
430
  cmd.warn(` - ${flagPath} (${perFlag[flagPath]})`);
369
431
  }
370
432
  }
371
- cmd.warn(`If any of these envs are still in use, restore them in the source system and re-run the migration. Full detail is also written to MIGRATION_REPORT.md.`);
433
+ cmd.warn(`If any of these envs are still in use, restore them in the source system and re-run the migration. Full detail is also written to .qf/MIGRATION_REPORT.md.`);
372
434
  }
373
435
  function warnAboutDuplicateResolutions(cmd, resolved) {
374
436
  if (!resolved || resolved.total === 0)
@@ -378,7 +440,7 @@ function warnAboutDuplicateResolutions(cmd, resolved) {
378
440
  for (const entry of sorted) {
379
441
  cmd.warn(` ${entry.key} (${entry.collisionTypes.join(', ')}): kept ${entry.kept}, deleted ${entry.deleted.join(', ')}`);
380
442
  }
381
- cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
443
+ cmd.warn('Full detail is also written to .qf/MIGRATION_REPORT.md.');
382
444
  }
383
445
  function warnAboutCoercedSentinels(cmd, coerced) {
384
446
  if (!coerced || coerced.total === 0)
@@ -393,7 +455,7 @@ function warnAboutCoercedSentinels(cmd, coerced) {
393
455
  cmd.warn(` - ${flagPath} (${perFlag[flagPath]})`);
394
456
  }
395
457
  }
396
- cmd.warn(`If you want a real default for these rules, set one in the source system and re-run the migration. Full detail is also written to MIGRATION_REPORT.md.`);
458
+ cmd.warn(`If you want a real default for these rules, set one in the source system and re-run the migration. Full detail is also written to .qf/MIGRATION_REPORT.md.`);
397
459
  }
398
460
  function warnAboutConversionNotes(cmd, notes) {
399
461
  if (!notes || notes.length === 0)
@@ -402,12 +464,12 @@ function warnAboutConversionNotes(cmd, notes) {
402
464
  if (rebucketed.length > 0) {
403
465
  cmd.warn(`${rebucketed.length} flag(s)/segment(s) use a percentage rollout — LaunchDarkly and Quonfig bucket users ` +
404
466
  `differently, so individual user assignments WILL change after migration (the rollout percentage is preserved). ` +
405
- `See the "Users will be re-bucketed" section of MIGRATION_REPORT.md.`);
467
+ `See the "Users will be re-bucketed" section of .qf/MIGRATION_REPORT.md.`);
406
468
  }
407
469
  const other = notes.filter((n) => n.category !== 'rebucketed-rollout' && n.category !== 'skipped-config');
408
470
  if (other.length > 0) {
409
471
  cmd.warn(`${other.length} source concept(s) could not be converted exactly (dropped prerequisites, lossy individual ` +
410
- `targets, etc.). Nothing was silently dropped — see the "Conversion notes" section of MIGRATION_REPORT.md.`);
472
+ `targets, etc.). Nothing was silently dropped — see the "Conversion notes" section of .qf/MIGRATION_REPORT.md.`);
411
473
  }
412
474
  }
413
475
  function warnAboutSkippedConfigs(cmd, skipped) {
@@ -418,7 +480,7 @@ function warnAboutSkippedConfigs(cmd, skipped) {
418
480
  for (const entry of sorted) {
419
481
  cmd.warn(` ${entry.key}: ${entry.reason}`);
420
482
  }
421
- cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
483
+ cmd.warn('Full detail is also written to .qf/MIGRATION_REPORT.md.');
422
484
  }
423
485
  function resolveTargetDir(dirFlag, cwd) {
424
486
  if (dirFlag)
@@ -238,9 +238,15 @@ export function buildRealDeps(cmd, orgSlug) {
238
238
  }
239
239
  },
240
240
  async getOriginMainSha(dir) {
241
- // qfg-gj3i: clone-path returns the local origin/main SHA after
242
- // fetch. Bare path has no `.git/` to rev-parse — return undefined
243
- // and let the server apply its bare-path lock policy.
241
+ // Bare path: `diffHeadVsOrigin` (always called first by runPush)
242
+ // populated the probe clone. Its HEAD is our view of the workspace
243
+ // tip — and is exactly what the diff was computed against — so it is
244
+ // the correct `expectedSha` for the server-side optimistic lock
245
+ // (qfg-gj3i bare-path follow-up, qfg-nhcb). Without it the bare-path
246
+ // push omits expectedSha and the server rejects it with a 426.
247
+ if (barePathProbe)
248
+ return barePathProbe.remoteHeadSha;
249
+ // Clone path: the local origin/main SHA after fetch.
244
250
  if (!(await isGitRepo(dir)))
245
251
  return;
246
252
  return getOriginMainSha(dir);
@@ -136,7 +136,15 @@ export default class WorkspaceCreate extends BaseCommand {
136
136
  this.log(` Envs: ${created.environments.map((e) => e.name).join(', ')}`);
137
137
  this.log('');
138
138
  this.log(`To use this workspace locally:`);
139
- this.log(` qfg workspace switch ${created.workspaceSlug}`);
139
+ // `qfg workspace switch` requires the `<org-slug>/<workspace-slug>` pin
140
+ // form — bare slugs are rejected. Fall back to the interactive picker if
141
+ // the server did not return an org slug.
142
+ if (created.organizationSlug) {
143
+ this.log(` qfg workspace switch ${created.organizationSlug}/${created.workspaceSlug}`);
144
+ }
145
+ else {
146
+ this.log(` qfg workspace switch`);
147
+ }
140
148
  return {
141
149
  workspaceId: created.workspaceId,
142
150
  workspaceSlug: created.workspaceSlug,
@@ -94,7 +94,7 @@ export interface BuildAuditFinalCommitOptions {
94
94
  }
95
95
  /**
96
96
  * qfg-wbkj: build the final state-file commit for audit-log mode. Writes
97
- * `.qf/import-state.json` + `MIGRATION_REPORT.md` with cumulative counts read
97
+ * `.qf/import-state.json` + `.qf/MIGRATION_REPORT.md` with cumulative counts read
98
98
  * from disk, and surfaces the source's accumulated dropped/skipped/coerced
99
99
  * summaries via `onAccumulatorUpdate`. The `preWrite` / `postWrite` hooks let
100
100
  * push-to-cloud merge environments and run server-side-equivalent validation
@@ -301,7 +301,7 @@ export const buildAuditPerChangeCommits = (changes, source) => changes.map((chan
301
301
  });
302
302
  /**
303
303
  * qfg-wbkj: build the final state-file commit for audit-log mode. Writes
304
- * `.qf/import-state.json` + `MIGRATION_REPORT.md` with cumulative counts read
304
+ * `.qf/import-state.json` + `.qf/MIGRATION_REPORT.md` with cumulative counts read
305
305
  * from disk, and surfaces the source's accumulated dropped/skipped/coerced
306
306
  * summaries via `onAccumulatorUpdate`. The `preWrite` / `postWrite` hooks let
307
307
  * push-to-cloud merge environments and run server-side-equivalent validation
@@ -85,5 +85,16 @@ export interface MigrationReportData {
85
85
  */
86
86
  export declare const deriveFollowUpFromConversionNotes: (base: FollowUpChecklist, notes: ConversionNote[] | undefined) => FollowUpChecklist;
87
87
  export declare const buildMigrationReport: (data: MigrationReportData) => string;
88
+ /**
89
+ * Path of the migration report. It lives inside the `.qf/` bookkeeping
90
+ * directory (next to `import-state.json`), NOT the workspace root (qfg-a631).
91
+ *
92
+ * `qfg push` mirrors every non-dotfile on disk and the server's
93
+ * config-path-allowlist only permits `configs/`, `feature-flags/`, etc. plus
94
+ * `quonfig.json`/`README.md`. A root-level `MIGRATION_REPORT.md` got swept
95
+ * into the push and rejected ("Path not allowed by push allow-list"). Keeping
96
+ * it under the `.qf/` dotdir means the existing dotfile/dotdir skip in
97
+ * `bare-path-diff.ts` excludes it — no server change needed.
98
+ */
88
99
  export declare const migrationReportPath: (outputDir: string) => string;
89
100
  export declare const writeMigrationReport: (outputDir: string, data: MigrationReportData) => string;
@@ -565,10 +565,21 @@ export const buildMigrationReport = (data) => {
565
565
  }
566
566
  return sections.join('\n\n') + '\n';
567
567
  };
568
- export const migrationReportPath = (outputDir) => path.join(outputDir, 'MIGRATION_REPORT.md');
568
+ /**
569
+ * Path of the migration report. It lives inside the `.qf/` bookkeeping
570
+ * directory (next to `import-state.json`), NOT the workspace root (qfg-a631).
571
+ *
572
+ * `qfg push` mirrors every non-dotfile on disk and the server's
573
+ * config-path-allowlist only permits `configs/`, `feature-flags/`, etc. plus
574
+ * `quonfig.json`/`README.md`. A root-level `MIGRATION_REPORT.md` got swept
575
+ * into the push and rejected ("Path not allowed by push allow-list"). Keeping
576
+ * it under the `.qf/` dotdir means the existing dotfile/dotdir skip in
577
+ * `bare-path-diff.ts` excludes it — no server change needed.
578
+ */
579
+ export const migrationReportPath = (outputDir) => path.join(outputDir, '.qf', 'MIGRATION_REPORT.md');
569
580
  export const writeMigrationReport = (outputDir, data) => {
570
- fs.mkdirSync(outputDir, { recursive: true });
571
581
  const filePath = migrationReportPath(outputDir);
582
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
572
583
  fs.writeFileSync(filePath, buildMigrationReport(data), 'utf8');
573
584
  return filePath;
574
585
  };
@@ -28,8 +28,8 @@ export interface PushMigrationToCloudOptions {
28
28
  * each commit authored as the original Launch user with their `changedAt` as
29
29
  * GIT_AUTHOR_DATE and `summary` as the message. One final state-file commit
30
30
  * (migrator identity, now) lands on top carrying .qf/import-state.json +
31
- * MIGRATION_REPORT.md so the audit-log commits aren't polluted by bookkeeping
32
- * churn. Requires that `source.getCommitMeta` be defined.
31
+ * .qf/MIGRATION_REPORT.md so the audit-log commits aren't polluted by
32
+ * bookkeeping churn. Requires that `source.getCommitMeta` be defined.
33
33
  */
34
34
  fullHistory?: boolean;
35
35
  importState: ImportState;
@@ -87,7 +87,13 @@ export interface CommitMeta {
87
87
  message: string;
88
88
  }
89
89
  export interface MigrationSource {
90
- fetchChanges(sinceEpochMs: null | number): AsyncIterable<LegacyChange>;
90
+ /**
91
+ * Yields the source's change history oldest→newest. `onProgress`, when
92
+ * provided, is called with the running fetched-change count as pagination
93
+ * proceeds, so the CLI can show progress during an otherwise-silent fetch.
94
+ * Sources that fetch in a single request may ignore it.
95
+ */
96
+ fetchChanges(sinceEpochMs: null | number, onProgress?: (fetched: number) => void): AsyncIterable<LegacyChange>;
91
97
  /**
92
98
  * Optional post-translate accumulator. Returns any rule values that translate()
93
99
  * coerced from a sentinel like Launch's empty-string "no value set yet" to the
@@ -4,4 +4,4 @@ export declare function selectLaunchBaseUrl(staging: boolean): string;
4
4
  export declare function applyLaunchBaseUrl(staging: boolean): void;
5
5
  export declare function fetchEnvironments(apiKey: string): Promise<Record<string, string>>;
6
6
  export declare function fetchChangeHistoryPage(apiKey: string, cursor?: string): Promise<LaunchChangeHistoryResponse>;
7
- export declare function fetchAllChangeHistory(apiKey: string, sinceEpochMs?: number): Promise<LaunchChangeEntry[]>;
7
+ export declare function fetchAllChangeHistory(apiKey: string, sinceEpochMs?: number, onProgress?: (fetched: number) => void): Promise<LaunchChangeEntry[]>;
@@ -49,7 +49,7 @@ export async function fetchChangeHistoryPage(apiKey, cursor) {
49
49
  }
50
50
  return (await apiFetch(`/api/v1/change-history?${params}`, apiKey));
51
51
  }
52
- export async function fetchAllChangeHistory(apiKey, sinceEpochMs) {
52
+ export async function fetchAllChangeHistory(apiKey, sinceEpochMs, onProgress) {
53
53
  const collected = [];
54
54
  const seenCursors = new Set();
55
55
  let cursor;
@@ -66,6 +66,9 @@ export async function fetchAllChangeHistory(apiKey, sinceEpochMs) {
66
66
  }
67
67
  collected.push(change);
68
68
  }
69
+ // Report cumulative progress after each page — pagination is silent and
70
+ // can run for many pages (50 changes/page) on a large account.
71
+ onProgress?.(collected.length);
69
72
  if (done || !page.cursor || seenCursors.has(page.cursor))
70
73
  break;
71
74
  seenCursors.add(page.cursor);
@@ -19,10 +19,10 @@ function requireApiKey(operation) {
19
19
  throw new MissingAuthError(operation);
20
20
  return state.apiKey;
21
21
  }
22
- async function* fetchChangesImpl(sinceEpochMs) {
22
+ async function* fetchChangesImpl(sinceEpochMs, onProgress) {
23
23
  const apiKey = requireApiKey('fetchChanges');
24
24
  const since = sinceEpochMs === null ? undefined : sinceEpochMs;
25
- const changes = await fetchAllChangeHistory(apiKey, since);
25
+ const changes = await fetchAllChangeHistory(apiKey, since, onProgress);
26
26
  for (const change of changes) {
27
27
  yield {
28
28
  changedAt: change.changedAt,
@@ -147,8 +147,8 @@ async function validateAuthImpl(apiKey) {
147
147
  state.coercedSentinels = new Map();
148
148
  }
149
149
  export const launchSource = {
150
- fetchChanges(sinceEpochMs) {
151
- return fetchChangesImpl(sinceEpochMs);
150
+ fetchChanges(sinceEpochMs, onProgress) {
151
+ return fetchChangesImpl(sinceEpochMs, onProgress);
152
152
  },
153
153
  getCoercedSentinels: getCoercedSentinelsImpl,
154
154
  getCommitMeta: getCommitMetaImpl,
@@ -27,6 +27,13 @@
27
27
  import { FileDelta } from './diff-summary.js';
28
28
  export interface BarePathDiffResult {
29
29
  deltas: FileDelta[];
30
+ /**
31
+ * The probe clone's HEAD sha — the workspace's current tip on the cloud
32
+ * repo. Threaded into `configs.push` as `expectedSha` so the server-side
33
+ * optimistic lock (qfg-gj3i) accepts bare-path pushes instead of rejecting
34
+ * them with a forced-upgrade 426.
35
+ */
36
+ remoteHeadSha: string;
30
37
  /** Tmp dir holding the probe clone. Caller must rm -rf in a finally block. */
31
38
  scratchDir: string;
32
39
  /** Total file count in the scratch clone (under the same prefix rules as `deltas`). */
@@ -52,6 +52,22 @@ export async function computeBarePathDiff(localDir, remoteUrl) {
52
52
  }
53
53
  throw error;
54
54
  }
55
+ // The probe clone's HEAD is the workspace tip — the optimistic-lock value
56
+ // `configs.push` needs as `expectedSha` (qfg-gj3i bare-path follow-up).
57
+ let remoteHeadSha;
58
+ try {
59
+ const { stdout } = await runGit(['-C', scratchDir, 'rev-parse', 'HEAD']);
60
+ remoteHeadSha = stdout.trim();
61
+ }
62
+ catch (error) {
63
+ try {
64
+ fs.rmSync(scratchDir, { force: true, recursive: true });
65
+ }
66
+ catch {
67
+ /* ignore */
68
+ }
69
+ throw error;
70
+ }
55
71
  const localFiles = collectFiles(localDir);
56
72
  const remoteFiles = collectFiles(scratchDir);
57
73
  const deltas = [];
@@ -69,7 +85,7 @@ export async function computeBarePathDiff(localDir, remoteUrl) {
69
85
  deltas.push({ kind: 'deleted', path: rel, beforeJson: remote.content });
70
86
  }
71
87
  }
72
- return { deltas, scratchDir, totalRemoteFiles: remoteFiles.size };
88
+ return { deltas, remoteHeadSha, scratchDir, totalRemoteFiles: remoteFiles.size };
73
89
  }
74
90
  /**
75
91
  * Walk `root` and return a map of relative-path -> {content, hash} for every
@@ -87,13 +87,11 @@ export interface GitOps {
87
87
  /** Pack-push — local HEAD SHA. Forwarded as `newSha` on the wire. */
88
88
  getHeadSha(dir: string): Promise<string>;
89
89
  /**
90
- * Returns the local SHA of `origin/main` after fetch — the remote tip
91
- * the diff was computed against — or undefined if no `origin/main` ref
92
- * exists (bare-path push, or repo never fetched).
93
- *
94
- * Threaded into the `configs.push` call as `expectedSha` so the
95
- * server-side optimistic lock (qfg-gj3i) can reject pushes whose
96
- * origin moved between fetch and push.
90
+ * Returns the workspace-HEAD sha that the diff was computed against —
91
+ * threaded into `configs.push` as `expectedSha` for the server-side
92
+ * optimistic lock (qfg-gj3i). Clone path: the local `origin/main` SHA
93
+ * after fetch. Bare path: the probe clone's HEAD. Undefined only when
94
+ * neither is available; the server rejects a missing value with a 426.
97
95
  */
98
96
  getOriginMainSha(dir: string): Promise<string | undefined>;
99
97
  /**
@@ -354,10 +354,10 @@ export async function runPush(input, deps) {
354
354
  ...(d.beforeJson === undefined ? {} : { beforeJson: d.beforeJson }),
355
355
  ...(d.afterJson === undefined ? {} : { afterJson: d.afterJson }),
356
356
  }));
357
- // qfg-gj3i: pass origin/main SHA as expectedSha so the server-side
358
- // optimistic lock can reject the push if origin moved between fetch
359
- // and now. Undefined on bare-path; the server treats absence as
360
- // "non-clone client" and applies its bare-path policy.
357
+ // qfg-gj3i: expectedSha is the workspace HEAD the server checks its
358
+ // optimistic lock against. For the bare path it is the probe clone's
359
+ // HEAD (resolved inside getOriginMainSha, qfg-nhcb). It must be present
360
+ // — the server rejects a missing expectedSha with a forced-upgrade 426.
361
361
  const expectedSha = await deps.gitOps.getOriginMainSha(input.dir);
362
362
  log('Sending push to Quonfig cloud...');
363
363
  const result = await deps.pushToServer({
@@ -87,7 +87,7 @@ export async function handleEvalContext(req, res, ctxToken, deps) {
87
87
  const result = {
88
88
  value: {
89
89
  type: match.value.type,
90
- value: coerceNumeric(match.value.type, match.value.value),
90
+ value: match.value.value,
91
91
  },
92
92
  configId: cfg.id,
93
93
  configType: cfg.type,
@@ -113,35 +113,6 @@ export async function handleEvalContext(req, res, ctxToken, deps) {
113
113
  res.writeHead(200, { 'Content-Type': 'application/json' });
114
114
  res.end(JSON.stringify(envelope));
115
115
  }
116
- /**
117
- * qfg-38sf.7: coerce `int`/`double` values to JS numbers so the envelope emits
118
- * JSON numbers, not JSON strings.
119
- *
120
- * Config files (and integration-test-data) store numeric values as JSON
121
- * strings — e.g. `{"type": "int", "value": "5"}`. api-delivery's
122
- * `Value.UnmarshalJSON` (api-delivery/internal/config/types.go:182-215) coerces
123
- * `int` via `strconv.ParseInt` and `double` via `strconv.ParseFloat` at config
124
- * *load* time, so its EvalEnvelope emits JSON numbers. sdk-node's datadir
125
- * loader does no such coercion and passes the value through verbatim, so
126
- * `match.value.value` arrives here as a string. We coerce on output to match
127
- * api-delivery — a browser SDK consumer doing arithmetic on an integer flag
128
- * would otherwise get string-concatenation behavior.
129
- *
130
- * Discriminate on `valueType` — the value's *own* type, the same field
131
- * api-delivery switches on (`raw.Type`). This is correct even when the matched
132
- * value's type differs from the config's declared `valueType` (e.g. a
133
- * weighted-values config resolving to an `int` arm). ONLY `int` and `double`
134
- * are coerced; `string`, `bool`, `string_list`, `json`, `provided`, etc. pass
135
- * through untouched. Integration-test-data's largest numeric value is ~2.5e12,
136
- * far below Number.MAX_SAFE_INTEGER, so `Number(...)` is lossless here.
137
- */
138
- function coerceNumeric(valueType, value) {
139
- if ((valueType === 'int' || valueType === 'double') && typeof value === 'string') {
140
- const n = Number(value);
141
- return Number.isNaN(n) ? value : n;
142
- }
143
- return value;
144
- }
145
116
  function resolutionReason(cfg, _match) {
146
117
  if (cfg.valueType === 'weighted_values')
147
118
  return 'SPLIT';
@@ -4,7 +4,13 @@ export interface FrictionEntry {
4
4
  exitCode: number;
5
5
  ts: string;
6
6
  }
7
- export declare function getDefaultFrictionLogPath(home?: string): string;
7
+ /**
8
+ * Default friction-log location: a `friction.log` file inside the shared
9
+ * Quonfig config home (`~/.quonfig`, the same dir as tokens/profile config).
10
+ * Honors `QUONFIG_CONFIG_HOME` via `getQuonfigConfigHome()` so tests and
11
+ * isolated environments redirect it along with everything else.
12
+ */
13
+ export declare function getDefaultFrictionLogPath(configHome?: string): string;
8
14
  export declare function getFrictionLogPath(envValue?: string | undefined, cwd?: string): null | string;
9
15
  export declare function buildFrictionEntry(opts: {
10
16
  argv?: string[];
@@ -1,8 +1,14 @@
1
1
  import fs from 'node:fs';
2
- import os from 'node:os';
3
2
  import path from 'node:path';
4
- export function getDefaultFrictionLogPath(home = os.homedir()) {
5
- return path.join(home, '.qfg', 'friction.log');
3
+ import { getQuonfigConfigHome } from './quonfig-home.js';
4
+ /**
5
+ * Default friction-log location: a `friction.log` file inside the shared
6
+ * Quonfig config home (`~/.quonfig`, the same dir as tokens/profile config).
7
+ * Honors `QUONFIG_CONFIG_HOME` via `getQuonfigConfigHome()` so tests and
8
+ * isolated environments redirect it along with everything else.
9
+ */
10
+ export function getDefaultFrictionLogPath(configHome = getQuonfigConfigHome()) {
11
+ return path.join(configHome, 'friction.log');
6
12
  }
7
13
  export function getFrictionLogPath(envValue, cwd = process.cwd()) {
8
14
  if (envValue === undefined)
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "0.0.53";
1
+ declare const _default: "0.0.55";
2
2
  export default _default;
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.
2
- export default '0.0.53';
2
+ export default '0.0.55';
@@ -3738,5 +3738,5 @@
3738
3738
  ]
3739
3739
  }
3740
3740
  },
3741
- "version": "0.0.53"
3741
+ "version": "0.0.55"
3742
3742
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54",
3
3
  "name": "@quonfig/cli",
4
- "version": "0.0.53",
4
+ "version": "0.0.55",
5
5
  "bugs": {
6
6
  "url": "https://github.com/quonfig/cli/issues"
7
7
  },
@@ -21,10 +21,10 @@
21
21
  "@oclif/core": "^3.18.2",
22
22
  "@oclif/plugin-help": "^6",
23
23
  "@oclif/plugin-plugins": "^4",
24
- "@quonfig/node": "^0.0.30",
24
+ "@quonfig/node": "^0.0.32",
25
25
  "acorn": "^8.10.0",
26
26
  "acorn-walk": "^8.3.5",
27
- "chalk": "^5.4.1",
27
+ "chalk": "^5.6.2",
28
28
  "common-tags": "^1.8.2",
29
29
  "fuzzy": "^0.1.3",
30
30
  "inquirer-autocomplete-standalone": "^0.8.1",
@@ -54,11 +54,11 @@
54
54
  "eslint-plugin-only-error": "^1.0.2",
55
55
  "mocha": "^11.7.5",
56
56
  "msw": "^2.14.6",
57
- "oclif": "^4.22.29",
57
+ "oclif": "^4.23.7",
58
58
  "prettier": "^3.8.3",
59
59
  "shx": "^0.3.4",
60
60
  "ts-node": "^10.9.1",
61
- "tsx": "^4.21.0"
61
+ "tsx": "^4.22.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=20.9.0"