@quonfig/cli 0.0.20 → 0.0.21
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 +76 -59
- package/dist/codegen/fetch-workspace-snapshot.d.ts +18 -0
- package/dist/codegen/fetch-workspace-snapshot.js +52 -0
- package/dist/codegen/local-config-reader.js +6 -1
- package/dist/commands/create.js +3 -1
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +33 -4
- package/dist/commands/login.js +36 -20
- package/dist/commands/override.d.ts +11 -5
- package/dist/commands/override.js +244 -148
- package/dist/util/coerce.js +21 -1
- package/dist/util/config-value-dto.js +3 -1
- package/dist/util/resolve-workspace.js +69 -3
- package/dist/util/token-storage.d.ts +4 -2
- package/dist/util/token-storage.js +27 -0
- package/oclif.manifest.json +34 -20
- 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.21 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -45,7 +45,7 @@ USAGE
|
|
|
45
45
|
* [`qfg migrate doctor`](#qfg-migrate-doctor)
|
|
46
46
|
* [`qfg migrate my-code`](#qfg-migrate-my-code)
|
|
47
47
|
* [`qfg migrate status`](#qfg-migrate-status)
|
|
48
|
-
* [`qfg override [NAME]`](#qfg-override-name)
|
|
48
|
+
* [`qfg override [NAME] [VALUE]`](#qfg-override-name-value)
|
|
49
49
|
* [`qfg pull`](#qfg-pull)
|
|
50
50
|
* [`qfg push`](#qfg-push)
|
|
51
51
|
* [`qfg schema NAME`](#qfg-schema-name)
|
|
@@ -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.
|
|
94
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/config-schema.ts)_
|
|
95
95
|
|
|
96
96
|
## `qfg create NAME`
|
|
97
97
|
|
|
@@ -99,9 +99,9 @@ Create a new feature flag, config value, or other item.
|
|
|
99
99
|
|
|
100
100
|
```
|
|
101
101
|
USAGE
|
|
102
|
-
$ qfg create NAME --type boolean-flag|boolean|string|double|int|string-list|json|log_level
|
|
103
|
-
[--interactive] [--no-color] [--verbose] [-w <value>] [--confidential] [--env-var <value>] [--value
|
|
104
|
-
[--secret] [--secret-key-name <value>]
|
|
102
|
+
$ qfg create NAME --type boolean-flag|boolean|string|double|int|string-list|json|duration|log_level
|
|
103
|
+
[--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--confidential] [--env-var <value>] [--value
|
|
104
|
+
<value>] [--secret] [--secret-key-name <value>]
|
|
105
105
|
|
|
106
106
|
ARGUMENTS
|
|
107
107
|
NAME name for your new item (e.g. my.new.flag)
|
|
@@ -113,7 +113,7 @@ FLAGS
|
|
|
113
113
|
--secret-key-name=<value> [default: quonfig.secrets.encryption.key] name of the secret key to use for
|
|
114
114
|
encryption/decryption
|
|
115
115
|
--type=<option> (required)
|
|
116
|
-
<options: boolean-flag|boolean|string|double|int|string-list|json|log_level>
|
|
116
|
+
<options: boolean-flag|boolean|string|double|int|string-list|json|duration|log_level>
|
|
117
117
|
--value=<value> default value for your new item
|
|
118
118
|
|
|
119
119
|
GLOBAL FLAGS
|
|
@@ -134,6 +134,7 @@ DESCRIPTION
|
|
|
134
134
|
string-list A comma-separated list of strings
|
|
135
135
|
json An arbitrary JSON blob
|
|
136
136
|
boolean A plain boolean (not a feature flag)
|
|
137
|
+
duration An ISO 8601 duration string, e.g. PT30S, PT5M, PT1H30M (SDK returns milliseconds)
|
|
137
138
|
log_level A dynamic log level (value must be one of TRACE/DEBUG/INFO/WARN/ERROR/FATAL)
|
|
138
139
|
|
|
139
140
|
This sets the global default value. Override per-environment with:
|
|
@@ -166,6 +167,8 @@ EXAMPLES
|
|
|
166
167
|
|
|
167
168
|
$ qfg create my.new.string --type json --value="{\"key\": \"value\"}"
|
|
168
169
|
|
|
170
|
+
$ qfg create my.timeout --type duration --value PT90S
|
|
171
|
+
|
|
169
172
|
$ qfg create log-level.my-app --type log_level --value WARN
|
|
170
173
|
|
|
171
174
|
# After creating a flag, set a 20% rollout in production:
|
|
@@ -173,7 +176,7 @@ EXAMPLES
|
|
|
173
176
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
174
177
|
```
|
|
175
178
|
|
|
176
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
179
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/create.ts)_
|
|
177
180
|
|
|
178
181
|
## `qfg flag info [NAME]`
|
|
179
182
|
|
|
@@ -312,11 +315,14 @@ Generate type definitions for your Quonfig configuration
|
|
|
312
315
|
```
|
|
313
316
|
USAGE
|
|
314
317
|
$ qfg generate [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [-o <value>] [--targets
|
|
315
|
-
<value>]
|
|
318
|
+
<value>] [-w <value>]
|
|
316
319
|
|
|
317
320
|
FLAGS
|
|
318
321
|
-o, --output-directory=<value> Override the output directory for generated files
|
|
319
|
-
|
|
322
|
+
-w, --workspace=<value> Workspace slug or UUID for the remote-fetch path (defaults to QUONFIG_WORKSPACE env
|
|
323
|
+
var or active profile). Only used when --dir is omitted.
|
|
324
|
+
--dir=<value> Path to local QUONFIG_DIR (defaults to QUONFIG_DIR env var). When omitted, fetches the
|
|
325
|
+
workspace from the server using your active credentials.
|
|
320
326
|
--targets=<value> [default: react-ts] Determines for language/framework to generate code for (node-ts,
|
|
321
327
|
react-ts)
|
|
322
328
|
|
|
@@ -373,7 +379,7 @@ EXAMPLES
|
|
|
373
379
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
374
380
|
```
|
|
375
381
|
|
|
376
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
382
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/generate.ts)_
|
|
377
383
|
|
|
378
384
|
## `qfg generate-new-hex-key`
|
|
379
385
|
|
|
@@ -396,7 +402,7 @@ EXAMPLES
|
|
|
396
402
|
$ qfg generate-new-hex-key
|
|
397
403
|
```
|
|
398
404
|
|
|
399
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
405
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/generate-new-hex-key.ts)_
|
|
400
406
|
|
|
401
407
|
## `qfg get [NAME]`
|
|
402
408
|
|
|
@@ -428,7 +434,7 @@ EXAMPLES
|
|
|
428
434
|
$ qfg get my.config.name --environment=production
|
|
429
435
|
```
|
|
430
436
|
|
|
431
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
437
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/get.ts)_
|
|
432
438
|
|
|
433
439
|
## `qfg info [NAME]`
|
|
434
440
|
|
|
@@ -476,7 +482,7 @@ EXAMPLES
|
|
|
476
482
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
477
483
|
```
|
|
478
484
|
|
|
479
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
485
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/info.ts)_
|
|
480
486
|
|
|
481
487
|
## `qfg init [DIRECTORY]`
|
|
482
488
|
|
|
@@ -516,7 +522,7 @@ EXAMPLES
|
|
|
516
522
|
$ qfg init --dry-run
|
|
517
523
|
```
|
|
518
524
|
|
|
519
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
525
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/init.ts)_
|
|
520
526
|
|
|
521
527
|
## `qfg interactive`
|
|
522
528
|
|
|
@@ -546,7 +552,7 @@ EXAMPLES
|
|
|
546
552
|
$ qfg
|
|
547
553
|
```
|
|
548
554
|
|
|
549
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
555
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/interactive.ts)_
|
|
550
556
|
|
|
551
557
|
## `qfg list`
|
|
552
558
|
|
|
@@ -586,7 +592,7 @@ EXAMPLES
|
|
|
586
592
|
$ qfg list --feature-flags
|
|
587
593
|
```
|
|
588
594
|
|
|
589
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
595
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/list.ts)_
|
|
590
596
|
|
|
591
597
|
## `qfg log-level NAME`
|
|
592
598
|
|
|
@@ -651,7 +657,7 @@ EXAMPLES
|
|
|
651
657
|
$ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
|
|
652
658
|
```
|
|
653
659
|
|
|
654
|
-
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
660
|
+
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/log-level.ts)_
|
|
655
661
|
|
|
656
662
|
## `qfg login`
|
|
657
663
|
|
|
@@ -676,7 +682,7 @@ EXAMPLES
|
|
|
676
682
|
$ qfg login --profile myprofile
|
|
677
683
|
```
|
|
678
684
|
|
|
679
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
685
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/login.ts)_
|
|
680
686
|
|
|
681
687
|
## `qfg logout`
|
|
682
688
|
|
|
@@ -699,7 +705,7 @@ EXAMPLES
|
|
|
699
705
|
$ qfg logout
|
|
700
706
|
```
|
|
701
707
|
|
|
702
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
708
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/logout.ts)_
|
|
703
709
|
|
|
704
710
|
## `qfg mcp`
|
|
705
711
|
|
|
@@ -732,7 +738,7 @@ EXAMPLES
|
|
|
732
738
|
$ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
|
|
733
739
|
```
|
|
734
740
|
|
|
735
|
-
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
741
|
+
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/mcp.ts)_
|
|
736
742
|
|
|
737
743
|
## `qfg migrate`
|
|
738
744
|
|
|
@@ -786,7 +792,7 @@ EXAMPLES
|
|
|
786
792
|
$ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-config --staging
|
|
787
793
|
```
|
|
788
794
|
|
|
789
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
795
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/migrate.ts)_
|
|
790
796
|
|
|
791
797
|
## `qfg migrate doctor`
|
|
792
798
|
|
|
@@ -824,7 +830,7 @@ EXAMPLES
|
|
|
824
830
|
$ qfg migrate doctor --json
|
|
825
831
|
```
|
|
826
832
|
|
|
827
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
833
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/migrate/doctor.ts)_
|
|
828
834
|
|
|
829
835
|
## `qfg migrate my-code`
|
|
830
836
|
|
|
@@ -856,7 +862,7 @@ EXAMPLES
|
|
|
856
862
|
$ qfg migrate my-code --dry-run
|
|
857
863
|
```
|
|
858
864
|
|
|
859
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
865
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/migrate/my-code.ts)_
|
|
860
866
|
|
|
861
867
|
## `qfg migrate status`
|
|
862
868
|
|
|
@@ -887,24 +893,25 @@ EXAMPLES
|
|
|
887
893
|
$ qfg migrate status --json
|
|
888
894
|
```
|
|
889
895
|
|
|
890
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
896
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/migrate/status.ts)_
|
|
891
897
|
|
|
892
|
-
## `qfg override [NAME]`
|
|
898
|
+
## `qfg override [NAME] [VALUE]`
|
|
893
899
|
|
|
894
|
-
Override
|
|
900
|
+
Override a flag value for your dev user.
|
|
895
901
|
|
|
896
902
|
```
|
|
897
903
|
USAGE
|
|
898
|
-
$ qfg override [NAME] [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--
|
|
899
|
-
[--
|
|
904
|
+
$ qfg override [NAME] [VALUE] [--json] [--interactive] [--no-color] [--verbose] [-w <value>] [--clear]
|
|
905
|
+
[--env <value>] [--remove]
|
|
900
906
|
|
|
901
907
|
ARGUMENTS
|
|
902
|
-
NAME
|
|
908
|
+
NAME flag/config key to override
|
|
909
|
+
VALUE new value (type inferred: bool/int/double/json/string)
|
|
903
910
|
|
|
904
911
|
FLAGS
|
|
905
|
-
--
|
|
906
|
-
--
|
|
907
|
-
--
|
|
912
|
+
--clear remove ALL of your overrides in this env
|
|
913
|
+
--env=<value> environment to operate in (default: $QUONFIG_ENVIRONMENT)
|
|
914
|
+
--remove remove your override on this key
|
|
908
915
|
|
|
909
916
|
GLOBAL FLAGS
|
|
910
917
|
-w, --workspace=<value> Workspace slug to use (overrides QUONFIG_WORKSPACE env var and saved default)
|
|
@@ -914,24 +921,34 @@ GLOBAL FLAGS
|
|
|
914
921
|
--verbose Verbose output
|
|
915
922
|
|
|
916
923
|
DESCRIPTION
|
|
917
|
-
Override
|
|
924
|
+
Override a flag value for your dev user.
|
|
918
925
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
926
|
+
Writes a top-priority rule keyed on the dev-only quonfig-user.email property,
|
|
927
|
+
so the override only fires for SDK clients that set quonfig-user.email in
|
|
928
|
+
their context. Production SDKs typically don't set this property, which makes
|
|
929
|
+
overrides effectively inert in production.
|
|
930
|
+
|
|
931
|
+
Examples
|
|
932
|
+
qfg override # list flags where you have an override
|
|
933
|
+
qfg override my.flag true # set bool override
|
|
934
|
+
qfg override my.flag 42 # set int override
|
|
935
|
+
qfg override my.flag '{"a":1}' # set json override
|
|
936
|
+
qfg override my.flag --remove # remove your override on my.flag
|
|
937
|
+
qfg override --clear # remove ALL of your overrides in this env
|
|
923
938
|
|
|
924
939
|
EXAMPLES
|
|
925
|
-
$ qfg override
|
|
940
|
+
$ qfg override
|
|
941
|
+
|
|
942
|
+
$ qfg override my.flag true
|
|
926
943
|
|
|
927
|
-
$ qfg override my.flag
|
|
944
|
+
$ qfg override my.flag --remove
|
|
928
945
|
|
|
929
|
-
$ qfg override
|
|
946
|
+
$ qfg override --clear
|
|
930
947
|
|
|
931
|
-
$ qfg override my.
|
|
948
|
+
$ qfg override my.flag true --env=staging
|
|
932
949
|
```
|
|
933
950
|
|
|
934
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
951
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/override.ts)_
|
|
935
952
|
|
|
936
953
|
## `qfg pull`
|
|
937
954
|
|
|
@@ -975,7 +992,7 @@ EXAMPLES
|
|
|
975
992
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
976
993
|
```
|
|
977
994
|
|
|
978
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
995
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/pull.ts)_
|
|
979
996
|
|
|
980
997
|
## `qfg push`
|
|
981
998
|
|
|
@@ -1023,7 +1040,7 @@ EXAMPLES
|
|
|
1023
1040
|
$ qfg push --dir ./our-config --yes
|
|
1024
1041
|
```
|
|
1025
1042
|
|
|
1026
|
-
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1043
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/push.ts)_
|
|
1027
1044
|
|
|
1028
1045
|
## `qfg schema NAME`
|
|
1029
1046
|
|
|
@@ -1061,7 +1078,7 @@ EXAMPLES
|
|
|
1061
1078
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
1062
1079
|
```
|
|
1063
1080
|
|
|
1064
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1081
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/schema.ts)_
|
|
1065
1082
|
|
|
1066
1083
|
## `qfg sdk-key`
|
|
1067
1084
|
|
|
@@ -1088,7 +1105,7 @@ EXAMPLES
|
|
|
1088
1105
|
$ qfg sdk-key revoke <key-id>
|
|
1089
1106
|
```
|
|
1090
1107
|
|
|
1091
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1108
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/sdk-key.ts)_
|
|
1092
1109
|
|
|
1093
1110
|
## `qfg sdk-key create`
|
|
1094
1111
|
|
|
@@ -1120,7 +1137,7 @@ EXAMPLES
|
|
|
1120
1137
|
$ qfg sdk-key create --environment staging --type browser
|
|
1121
1138
|
```
|
|
1122
1139
|
|
|
1123
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1140
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/sdk-key/create.ts)_
|
|
1124
1141
|
|
|
1125
1142
|
## `qfg sdk-key list`
|
|
1126
1143
|
|
|
@@ -1149,7 +1166,7 @@ EXAMPLES
|
|
|
1149
1166
|
$ qfg sdk-key list --environment production
|
|
1150
1167
|
```
|
|
1151
1168
|
|
|
1152
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1169
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/sdk-key/list.ts)_
|
|
1153
1170
|
|
|
1154
1171
|
## `qfg sdk-key revoke KEYID`
|
|
1155
1172
|
|
|
@@ -1176,7 +1193,7 @@ EXAMPLES
|
|
|
1176
1193
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1177
1194
|
```
|
|
1178
1195
|
|
|
1179
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1196
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/sdk-key/revoke.ts)_
|
|
1180
1197
|
|
|
1181
1198
|
## `qfg set-default [NAME]`
|
|
1182
1199
|
|
|
@@ -1247,7 +1264,7 @@ EXAMPLES
|
|
|
1247
1264
|
$ qfg pull && qfg config-schema
|
|
1248
1265
|
```
|
|
1249
1266
|
|
|
1250
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1267
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/set-default.ts)_
|
|
1251
1268
|
|
|
1252
1269
|
## `qfg set-rollout [NAME]`
|
|
1253
1270
|
|
|
@@ -1324,7 +1341,7 @@ EXAMPLES
|
|
|
1324
1341
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1325
1342
|
```
|
|
1326
1343
|
|
|
1327
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1344
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/set-rollout.ts)_
|
|
1328
1345
|
|
|
1329
1346
|
## `qfg sync`
|
|
1330
1347
|
|
|
@@ -1356,7 +1373,7 @@ EXAMPLES
|
|
|
1356
1373
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1357
1374
|
```
|
|
1358
1375
|
|
|
1359
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1376
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/sync.ts)_
|
|
1360
1377
|
|
|
1361
1378
|
## `qfg toggle [NAME]`
|
|
1362
1379
|
|
|
@@ -1458,7 +1475,7 @@ EXAMPLES
|
|
|
1458
1475
|
$ qfg verify --json
|
|
1459
1476
|
```
|
|
1460
1477
|
|
|
1461
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1478
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/verify.ts)_
|
|
1462
1479
|
|
|
1463
1480
|
## `qfg whoami`
|
|
1464
1481
|
|
|
@@ -1481,7 +1498,7 @@ EXAMPLES
|
|
|
1481
1498
|
$ qfg whoami
|
|
1482
1499
|
```
|
|
1483
1500
|
|
|
1484
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1501
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/whoami.ts)_
|
|
1485
1502
|
|
|
1486
1503
|
## `qfg workspace`
|
|
1487
1504
|
|
|
@@ -1504,7 +1521,7 @@ EXAMPLES
|
|
|
1504
1521
|
$ qfg workspace
|
|
1505
1522
|
```
|
|
1506
1523
|
|
|
1507
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1524
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/workspace.ts)_
|
|
1508
1525
|
|
|
1509
1526
|
## `qfg workspace bootstrap`
|
|
1510
1527
|
|
|
@@ -1537,7 +1554,7 @@ EXAMPLES
|
|
|
1537
1554
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1538
1555
|
```
|
|
1539
1556
|
|
|
1540
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1557
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/workspace/bootstrap.ts)_
|
|
1541
1558
|
|
|
1542
1559
|
## `qfg workspace create SLUG`
|
|
1543
1560
|
|
|
@@ -1571,7 +1588,7 @@ EXAMPLES
|
|
|
1571
1588
|
$ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
|
|
1572
1589
|
```
|
|
1573
1590
|
|
|
1574
|
-
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1591
|
+
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/workspace/create.ts)_
|
|
1575
1592
|
|
|
1576
1593
|
## `qfg workspace switch [SLUG]`
|
|
1577
1594
|
|
|
@@ -1599,7 +1616,7 @@ EXAMPLES
|
|
|
1599
1616
|
$ qfg workspace switch my-workspace-slug
|
|
1600
1617
|
```
|
|
1601
1618
|
|
|
1602
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1619
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.21/src/commands/workspace/switch.ts)_
|
|
1603
1620
|
<!-- commandsstop -->
|
|
1604
1621
|
|
|
1605
1622
|
## Local Development
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BaseCommand } from '../index.js';
|
|
2
|
+
export interface WorkspaceSnapshot {
|
|
3
|
+
cleanup: () => Promise<void>;
|
|
4
|
+
dir: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Clone the active workspace into a fresh OS tmp directory and return its path
|
|
8
|
+
* along with a cleanup callback. Used by `qfg generate` so codegen works
|
|
9
|
+
* without requiring `qfg pull` first or a long-lived `--dir` checkout.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors `qfg pull`'s auth/clone path: resolves workspace from
|
|
12
|
+
* QUONFIG_API_KEY/QUONFIG_WORKSPACE or active OAuth profile, mints a Gitea
|
|
13
|
+
* read token (refreshing on 401), then `git clone`s into the tmp dir. The
|
|
14
|
+
* caller is responsible for calling `cleanup()` in a `finally` block.
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchWorkspaceSnapshot(command: BaseCommand, options?: {
|
|
17
|
+
workspace?: string;
|
|
18
|
+
}): Promise<WorkspaceSnapshot>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { mintAndStoreGiteaReadToken } from '../util/gitea-api.js';
|
|
5
|
+
import { isGiteaTokenExpired, loadGiteaToken } from '../util/gitea-token-storage.js';
|
|
6
|
+
import { gitClone } from '../util/git-ops.js';
|
|
7
|
+
import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
|
|
8
|
+
const looksLike401 = (err) => {
|
|
9
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
10
|
+
return msg.includes('401') || msg.toLowerCase().includes('authentication failed');
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Clone the active workspace into a fresh OS tmp directory and return its path
|
|
14
|
+
* along with a cleanup callback. Used by `qfg generate` so codegen works
|
|
15
|
+
* without requiring `qfg pull` first or a long-lived `--dir` checkout.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors `qfg pull`'s auth/clone path: resolves workspace from
|
|
18
|
+
* QUONFIG_API_KEY/QUONFIG_WORKSPACE or active OAuth profile, mints a Gitea
|
|
19
|
+
* read token (refreshing on 401), then `git clone`s into the tmp dir. The
|
|
20
|
+
* caller is responsible for calling `cleanup()` in a `finally` block.
|
|
21
|
+
*/
|
|
22
|
+
export async function fetchWorkspaceSnapshot(command, options = {}) {
|
|
23
|
+
const workspaceId = await resolveWorkspaceUuid(command, options.workspace);
|
|
24
|
+
let entry = await loadGiteaToken(workspaceId);
|
|
25
|
+
if (!entry || isGiteaTokenExpired(entry)) {
|
|
26
|
+
command.verboseLog('fetchWorkspaceSnapshot', 'Minting new Gitea read token...');
|
|
27
|
+
entry = await mintAndStoreGiteaReadToken(workspaceId);
|
|
28
|
+
}
|
|
29
|
+
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'qfg-codegen-'));
|
|
30
|
+
const cleanup = () => fs.promises.rm(tmpDir, { force: true, recursive: true });
|
|
31
|
+
try {
|
|
32
|
+
command.verboseLog('fetchWorkspaceSnapshot', `Cloning workspace ${workspaceId} into ${tmpDir}...`);
|
|
33
|
+
try {
|
|
34
|
+
await gitClone(entry.repoUrl, tmpDir);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (looksLike401(error)) {
|
|
38
|
+
command.verboseLog('fetchWorkspaceSnapshot', 'Got 401 on clone, refreshing Gitea token...');
|
|
39
|
+
const fresh = await mintAndStoreGiteaReadToken(workspaceId);
|
|
40
|
+
await gitClone(fresh.repoUrl, tmpDir);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
await cleanup();
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
return { cleanup, dir: tmpDir };
|
|
52
|
+
}
|
|
@@ -10,7 +10,12 @@ function mapGitValue(gitValue) {
|
|
|
10
10
|
return { value: { int: value } };
|
|
11
11
|
}
|
|
12
12
|
case 'json': {
|
|
13
|
-
|
|
13
|
+
// Git-native value may be a parsed JS value (canonical, what `qfg
|
|
14
|
+
// pull` and our-config produce) or a pre-stringified JSON string
|
|
15
|
+
// (legacy). Downstream codegen JSON.parses this string, so emit a
|
|
16
|
+
// string either way.
|
|
17
|
+
const jsonString = typeof value === 'string' ? value : JSON.stringify(value);
|
|
18
|
+
return { value: { json: { json: jsonString } } };
|
|
14
19
|
}
|
|
15
20
|
case 'log_level': {
|
|
16
21
|
return { value: { logLevel: value } };
|
package/dist/commands/create.js
CHANGED
|
@@ -22,6 +22,7 @@ Use --type to specify the kind of item:
|
|
|
22
22
|
string-list A comma-separated list of strings
|
|
23
23
|
json An arbitrary JSON blob
|
|
24
24
|
boolean A plain boolean (not a feature flag)
|
|
25
|
+
duration An ISO 8601 duration string, e.g. PT30S, PT5M, PT1H30M (SDK returns milliseconds)
|
|
25
26
|
log_level A dynamic log level (value must be one of ${LOG_LEVELS.join('/')})
|
|
26
27
|
|
|
27
28
|
This sets the global default value. Override per-environment with:
|
|
@@ -47,6 +48,7 @@ Log levels:
|
|
|
47
48
|
'<%= config.bin %> <%= command.id %> my.new.string --type string --value="hello world" --secret',
|
|
48
49
|
'<%= config.bin %> <%= command.id %> my.new.string --type string --env-var=MY_ENV_VAR_NAME',
|
|
49
50
|
'<%= config.bin %> <%= command.id %> my.new.string --type json --value="{\\"key\\": \\"value\\"}"',
|
|
51
|
+
'<%= config.bin %> <%= command.id %> my.timeout --type duration --value PT90S',
|
|
50
52
|
'<%= config.bin %> <%= command.id %> log-level.my-app --type log_level --value WARN',
|
|
51
53
|
'# After creating a flag, set a 20% rollout in production:',
|
|
52
54
|
'<%= config.bin %> set-rollout my.new.flag --environment production --true-percent 20',
|
|
@@ -55,7 +57,7 @@ Log levels:
|
|
|
55
57
|
confidential: Flags.boolean({ default: false, description: 'mark the value as confidential' }),
|
|
56
58
|
'env-var': Flags.string({ description: 'environment variable to get value from' }),
|
|
57
59
|
type: Flags.string({
|
|
58
|
-
options: ['boolean-flag', 'boolean', 'string', 'double', 'int', 'string-list', 'json', 'log_level'],
|
|
60
|
+
options: ['boolean-flag', 'boolean', 'string', 'double', 'int', 'string-list', 'json', 'duration', 'log_level'],
|
|
59
61
|
required: true,
|
|
60
62
|
}),
|
|
61
63
|
value: Flags.string({ description: 'default value for your new item', required: false }),
|
|
@@ -7,6 +7,7 @@ export default class Generate extends BaseCommand {
|
|
|
7
7
|
dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
8
|
'output-directory': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
9
|
targets: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
|
+
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
11
|
};
|
|
11
12
|
static summary: string;
|
|
12
13
|
run(): Promise<JsonObj | void>;
|
|
@@ -4,6 +4,7 @@ import * as path from 'node:path';
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { NodeTypeScriptGenerator } from '../codegen/code-generators/node-typescript-generator.js';
|
|
6
6
|
import { ReactTypeScriptGenerator } from '../codegen/code-generators/react-typescript-generator.js';
|
|
7
|
+
import { fetchWorkspaceSnapshot } from '../codegen/fetch-workspace-snapshot.js';
|
|
7
8
|
import { LocalConfigReader } from '../codegen/local-config-reader.js';
|
|
8
9
|
import { SupportedLanguage } from '../codegen/types.js';
|
|
9
10
|
import { BaseCommand } from '../index.js';
|
|
@@ -109,7 +110,7 @@ Example quonfig.config.json:
|
|
|
109
110
|
];
|
|
110
111
|
static flags = {
|
|
111
112
|
dir: Flags.string({
|
|
112
|
-
description: 'Path to local QUONFIG_DIR (defaults to QUONFIG_DIR env var)',
|
|
113
|
+
description: 'Path to local QUONFIG_DIR (defaults to QUONFIG_DIR env var). When omitted, fetches the workspace from the server using your active credentials.',
|
|
113
114
|
env: 'QUONFIG_DIR',
|
|
114
115
|
}),
|
|
115
116
|
'output-directory': Flags.string({
|
|
@@ -120,16 +121,25 @@ Example quonfig.config.json:
|
|
|
120
121
|
default: SupportedLanguage.React,
|
|
121
122
|
description: `Determines for language/framework to generate code for (${Object.values(SupportedLanguage).join(', ')})`,
|
|
122
123
|
}),
|
|
124
|
+
workspace: Flags.string({
|
|
125
|
+
char: 'w',
|
|
126
|
+
description: 'Workspace slug or UUID for the remote-fetch path (defaults to QUONFIG_WORKSPACE env var or active profile). Only used when --dir is omitted.',
|
|
127
|
+
}),
|
|
123
128
|
};
|
|
124
129
|
static summary = 'Generate type definitions for your Quonfig configuration';
|
|
125
130
|
async run() {
|
|
126
131
|
const { flags } = await this.parse(Generate);
|
|
127
132
|
this.verboseLog('=== GENERATE COMMAND START ===');
|
|
133
|
+
let snapshot;
|
|
128
134
|
try {
|
|
129
|
-
//
|
|
130
|
-
|
|
135
|
+
// When --dir/QUONFIG_DIR is set, read the local checkout (so users
|
|
136
|
+
// who edit flag JSON in place keep that workflow). Otherwise clone
|
|
137
|
+
// the workspace into a tmp dir for codegen-only callers.
|
|
138
|
+
let dir = flags.dir;
|
|
131
139
|
if (!dir) {
|
|
132
|
-
this.
|
|
140
|
+
this.verboseLog('No --dir/QUONFIG_DIR set; fetching workspace snapshot from server...');
|
|
141
|
+
snapshot = await fetchWorkspaceSnapshot(this, { workspace: flags.workspace });
|
|
142
|
+
dir = snapshot.dir;
|
|
133
143
|
}
|
|
134
144
|
// Look for and read local quonfig.config.json file
|
|
135
145
|
const localConfig = await this.readLocalConfig(flags['output-directory']);
|
|
@@ -142,6 +152,7 @@ Example quonfig.config.json:
|
|
|
142
152
|
const configFile = await reader.read();
|
|
143
153
|
this.verboseLog(`Config read complete. Found ${configFile.configs.length} configs.`);
|
|
144
154
|
const fileCreationPromises = [];
|
|
155
|
+
let needsMustache = false;
|
|
145
156
|
for (const target of targets) {
|
|
146
157
|
// Resolve the language input
|
|
147
158
|
const language = this.resolveLanguage(target);
|
|
@@ -154,6 +165,9 @@ Example quonfig.config.json:
|
|
|
154
165
|
console.log(`Generating ${language} code for configs...`);
|
|
155
166
|
const generatedCode = generator.generate();
|
|
156
167
|
this.verboseLog(`Code generation complete. Size: ${generatedCode.length}`);
|
|
168
|
+
if (generatedCode.includes("from 'mustache'")) {
|
|
169
|
+
needsMustache = true;
|
|
170
|
+
}
|
|
157
171
|
const fileManager = createFileManager({ outputDirectory: outputDir, verboseLog: this.verboseLog.bind(this) });
|
|
158
172
|
fileCreationPromises.push(fileManager.writeFile({ data: generatedCode, filename: targetConfig.clientFileName }));
|
|
159
173
|
if ([SupportedLanguage.Node, SupportedLanguage.React].includes(language)) {
|
|
@@ -163,11 +177,26 @@ Example quonfig.config.json:
|
|
|
163
177
|
}
|
|
164
178
|
}
|
|
165
179
|
await Promise.all(fileCreationPromises);
|
|
180
|
+
if (needsMustache) {
|
|
181
|
+
console.log('\nNote: generated code imports `mustache` for templated config values.\n' +
|
|
182
|
+
' Add it to your project: `npm install mustache @types/mustache`\n' +
|
|
183
|
+
' (or pnpm/yarn equivalent).');
|
|
184
|
+
}
|
|
166
185
|
}
|
|
167
186
|
catch (error) {
|
|
168
187
|
console.error('ERROR:', error);
|
|
169
188
|
this.error(error);
|
|
170
189
|
}
|
|
190
|
+
finally {
|
|
191
|
+
if (snapshot) {
|
|
192
|
+
try {
|
|
193
|
+
await snapshot.cleanup();
|
|
194
|
+
}
|
|
195
|
+
catch (cleanupError) {
|
|
196
|
+
this.verboseLog('Generate', `Snapshot cleanup failed: ${String(cleanupError)}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
171
200
|
this.verboseLog('=== GENERATE COMMAND END ===');
|
|
172
201
|
return { success: true };
|
|
173
202
|
}
|