@runcofactor/cli 0.10.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/account-22LMP5PX.js +255 -0
  2. package/dist/account-22LMP5PX.js.map +1 -0
  3. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-WTFQNLK6.js} +5 -4
  4. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-WTFQNLK6.js.map} +1 -1
  5. package/dist/{attachment-SJC7D7FY.js → attachment-U3NKVPEY.js} +5 -4
  6. package/dist/{attachment-SJC7D7FY.js.map → attachment-U3NKVPEY.js.map} +1 -1
  7. package/dist/augmentor-5GMNAX4M.js +198 -0
  8. package/dist/augmentor-5GMNAX4M.js.map +1 -0
  9. package/dist/{auth-EN6NQCJY.js → auth-VQG2CDUD.js} +3 -2
  10. package/dist/{chunk-A4OFTUVV.js → chunk-3X3NW7QA.js} +7 -4
  11. package/dist/chunk-3X3NW7QA.js.map +1 -0
  12. package/dist/{chunk-N7HFALX3.js → chunk-46IUJXSD.js} +2 -2
  13. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  14. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  15. package/dist/{chunk-B2VISN2V.js → chunk-7VMO2WOZ.js} +238 -68
  16. package/dist/chunk-7VMO2WOZ.js.map +1 -0
  17. package/dist/{chunk-QFDOWABX.js → chunk-CMGI7UZD.js} +6 -3
  18. package/dist/chunk-CMGI7UZD.js.map +1 -0
  19. package/dist/{chunk-XQXVG7RA.js → chunk-FNPMWDUQ.js} +3 -3
  20. package/dist/chunk-G4UYGRZR.js +2 -0
  21. package/dist/{chunk-I4QLI3JO.js → chunk-HDFYUFBI.js} +2 -2
  22. package/dist/chunk-JMJVGMXI.js +92 -0
  23. package/dist/chunk-JMJVGMXI.js.map +1 -0
  24. package/dist/{chunk-SJJW77WN.js → chunk-JYGNMKZ5.js} +129 -38
  25. package/dist/chunk-JYGNMKZ5.js.map +1 -0
  26. package/dist/{chunk-52IO7ACJ.js → chunk-KCHKFYVX.js} +68 -10
  27. package/dist/{chunk-52IO7ACJ.js.map → chunk-KCHKFYVX.js.map} +1 -1
  28. package/dist/{chunk-ZD3KHESI.js → chunk-KRWRUU6R.js} +27 -7
  29. package/dist/chunk-KRWRUU6R.js.map +1 -0
  30. package/dist/{chunk-CLAWT7BJ.js → chunk-NHTEKEYE.js} +3 -3
  31. package/dist/{chunk-WOHL4FTB.js → chunk-PRBRSIZP.js} +16 -10
  32. package/dist/chunk-PRBRSIZP.js.map +1 -0
  33. package/dist/{chunk-GKY2KXO5.js → chunk-UETBLPKM.js} +154 -37
  34. package/dist/chunk-UETBLPKM.js.map +1 -0
  35. package/dist/{chunk-4PNOV7OV.js → chunk-VGYN2KU2.js} +9284 -1421
  36. package/dist/chunk-VGYN2KU2.js.map +1 -0
  37. package/dist/{chunk-Q4BO4Z5V.js → chunk-XJ5YYYGC.js} +4 -4
  38. package/dist/{credential-COLTBV6D.js → credential-E6KUHRGU.js} +24 -20
  39. package/dist/credential-E6KUHRGU.js.map +1 -0
  40. package/dist/{credential-template-EEPYFG7R.js → credential-template-4GPOPBGD.js} +30 -28
  41. package/dist/credential-template-4GPOPBGD.js.map +1 -0
  42. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-XCLCY6S7.js} +5 -4
  43. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-XCLCY6S7.js.map} +1 -1
  44. package/dist/{docs-3SSORHB5.js → docs-COCZYMW6.js} +95 -10
  45. package/dist/docs-COCZYMW6.js.map +1 -0
  46. package/dist/{doctor-OTSIWV6G.js → doctor-ERIVTCQS.js} +9 -5
  47. package/dist/doctor-ERIVTCQS.js.map +1 -0
  48. package/dist/environment-7Y4BV6XY.js +221 -0
  49. package/dist/environment-7Y4BV6XY.js.map +1 -0
  50. package/dist/{feature-flag-T2LRZME3.js → feature-flag-MHONEX6T.js} +164 -16
  51. package/dist/feature-flag-MHONEX6T.js.map +1 -0
  52. package/dist/index.js +123 -58
  53. package/dist/index.js.map +1 -1
  54. package/dist/{init-TGWRECT2.js → init-OROOM4HF.js} +5 -4
  55. package/dist/{init-TGWRECT2.js.map → init-OROOM4HF.js.map} +1 -1
  56. package/dist/{install-DXSNWZKO.js → install-OJFXHY6U.js} +7 -7
  57. package/dist/{issues-XIZOPX6T.js → issues-32GONR6C.js} +5 -4
  58. package/dist/{issues-XIZOPX6T.js.map → issues-32GONR6C.js.map} +1 -1
  59. package/dist/{knobs-NBINZIUB.js → knobs-UQGGY3CP.js} +7 -5
  60. package/dist/knobs-UQGGY3CP.js.map +1 -0
  61. package/dist/{list-3TDHK637.js → list-ZMIKEC5P.js} +5 -4
  62. package/dist/{list-3TDHK637.js.map → list-ZMIKEC5P.js.map} +1 -1
  63. package/dist/{local-HICVD6PU.js → local-PUD24GGQ.js} +7 -6
  64. package/dist/{local-HICVD6PU.js.map → local-PUD24GGQ.js.map} +1 -1
  65. package/dist/{login-UOIUXVUK.js → login-BSO7YX2D.js} +5 -4
  66. package/dist/{login-UOIUXVUK.js.map → login-BSO7YX2D.js.map} +1 -1
  67. package/dist/{mcp-54IJEOUA.js → mcp-4LS7OJUT.js} +24 -25
  68. package/dist/mcp-4LS7OJUT.js.map +1 -0
  69. package/dist/{pdf-extract-ABWV3L5X.js → pdf-extract-YHE54XU4.js} +2 -2
  70. package/dist/product-3JKSJCBC.js +346 -0
  71. package/dist/product-3JKSJCBC.js.map +1 -0
  72. package/dist/{property-UXLOLPTD.js → property-K5DBL4UE.js} +76 -4
  73. package/dist/property-K5DBL4UE.js.map +1 -0
  74. package/dist/{psql-BYUUNLGG.js → psql-PQFUFTSE.js} +3 -2
  75. package/dist/{psql-BYUUNLGG.js.map → psql-PQFUFTSE.js.map} +1 -1
  76. package/dist/{record-4TR2UIIS.js → record-EIIYLFJ4.js} +16 -17
  77. package/dist/record-EIIYLFJ4.js.map +1 -0
  78. package/dist/{redis-35VBUYW7.js → redis-BU36EZZI.js} +3 -2
  79. package/dist/redis-BU36EZZI.js.map +1 -0
  80. package/dist/{release-OOLTUBUT.js → release-LYC2P7DZ.js} +5 -4
  81. package/dist/{release-OOLTUBUT.js.map → release-LYC2P7DZ.js.map} +1 -1
  82. package/dist/{sandbox-XGSUU6G7.js → sandbox-CKHXXZHU.js} +55 -10
  83. package/dist/sandbox-CKHXXZHU.js.map +1 -0
  84. package/dist/{scenarios-GWLJEGHA.js → scenarios-2XJ2G3KR.js} +5 -4
  85. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-2XJ2G3KR.js.map} +1 -1
  86. package/dist/{session-MYFNZPRP.js → session-UTOD5ZYH.js} +77 -10
  87. package/dist/session-UTOD5ZYH.js.map +1 -0
  88. package/dist/{skill-KM34LDMO.js → skill-GMAERMQL.js} +220 -70
  89. package/dist/skill-GMAERMQL.js.map +1 -0
  90. package/dist/skills/debug-run/SKILL.md +40 -23
  91. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  92. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  93. package/dist/skills/debug-run/history/1.3.1.md +279 -0
  94. package/dist/skills/debug-run/version.json +1 -1
  95. package/dist/skills/optimize-cache/SKILL.md +233 -0
  96. package/dist/{spec-RA3CFEKG.js → spec-2NP36U3Y.js} +54 -25
  97. package/dist/spec-2NP36U3Y.js.map +1 -0
  98. package/dist/{src-LB4HBTJQ.js → src-4T6GHXVO.js} +19 -11
  99. package/dist/src-4T6GHXVO.js.map +1 -0
  100. package/dist/{src-N3TPKGTY.js → src-CG52HEE7.js} +10 -7
  101. package/dist/{src-UVDX5UP6.js → src-PK5OZXCM.js} +4 -2
  102. package/dist/src-UEOOC425.js +5941 -0
  103. package/dist/src-UEOOC425.js.map +1 -0
  104. package/dist/{start-PSEQSQA3.js → start-3C7EEFW2.js} +5 -4
  105. package/dist/{start-PSEQSQA3.js.map → start-3C7EEFW2.js.map} +1 -1
  106. package/dist/{tag-YY6YAABQ.js → tag-JSHOMLBR.js} +6 -5
  107. package/dist/{task-IQC3YR5K.js → task-YGWVYTBJ.js} +5 -4
  108. package/dist/{task-IQC3YR5K.js.map → task-YGWVYTBJ.js.map} +1 -1
  109. package/dist/{test-suite-DZNJFXDW.js → test-suite-LI7ZH367.js} +12 -7
  110. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-LI7ZH367.js.map} +1 -1
  111. package/dist/trace-UMLOSIIR.js +16 -0
  112. package/dist/{triage-ORG45R5W.js → triage-ZWJG4NBJ.js} +6 -5
  113. package/dist/triage-ZWJG4NBJ.js.map +1 -0
  114. package/dist/{upgrade-L2VYMP2V.js → upgrade-S7CGEK6H.js} +3 -3
  115. package/dist/{workflow-XYFH5LTP.js → workflow-T4HSAOGW.js} +316 -241
  116. package/dist/workflow-T4HSAOGW.js.map +1 -0
  117. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-XVEZ36KF.js} +140 -7
  118. package/dist/workflow-cache-XVEZ36KF.js.map +1 -0
  119. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-T2BULXZ7.js} +9 -8
  120. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-T2BULXZ7.js.map} +1 -1
  121. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-XXEZICN2.js} +5 -6
  122. package/dist/workflow-proposals-XXEZICN2.js.map +1 -0
  123. package/package.json +2 -1
  124. package/dist/chunk-4PNOV7OV.js.map +0 -1
  125. package/dist/chunk-A4OFTUVV.js.map +0 -1
  126. package/dist/chunk-B2VISN2V.js.map +0 -1
  127. package/dist/chunk-GKY2KXO5.js.map +0 -1
  128. package/dist/chunk-JICSYFRJ.js +0 -46
  129. package/dist/chunk-JICSYFRJ.js.map +0 -1
  130. package/dist/chunk-QFDOWABX.js.map +0 -1
  131. package/dist/chunk-SJJW77WN.js.map +0 -1
  132. package/dist/chunk-WOHL4FTB.js.map +0 -1
  133. package/dist/chunk-ZD3KHESI.js.map +0 -1
  134. package/dist/credential-COLTBV6D.js.map +0 -1
  135. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  136. package/dist/docs-3SSORHB5.js.map +0 -1
  137. package/dist/doctor-OTSIWV6G.js.map +0 -1
  138. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  139. package/dist/knobs-NBINZIUB.js.map +0 -1
  140. package/dist/mcp-54IJEOUA.js.map +0 -1
  141. package/dist/property-UXLOLPTD.js.map +0 -1
  142. package/dist/record-4TR2UIIS.js.map +0 -1
  143. package/dist/redis-35VBUYW7.js.map +0 -1
  144. package/dist/sandbox-XGSUU6G7.js.map +0 -1
  145. package/dist/session-MYFNZPRP.js.map +0 -1
  146. package/dist/skill-KM34LDMO.js.map +0 -1
  147. package/dist/spec-RA3CFEKG.js.map +0 -1
  148. package/dist/src-LB4HBTJQ.js.map +0 -1
  149. package/dist/trace-HXIW7U2D.js +0 -14
  150. package/dist/workflow-XYFH5LTP.js.map +0 -1
  151. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  152. package/dist/workflow-proposals-MTUDX46O.js.map +0 -1
  153. /package/dist/{auth-EN6NQCJY.js.map → auth-VQG2CDUD.js.map} +0 -0
  154. /package/dist/{chunk-N7HFALX3.js.map → chunk-46IUJXSD.js.map} +0 -0
  155. /package/dist/{chunk-XQXVG7RA.js.map → chunk-FNPMWDUQ.js.map} +0 -0
  156. /package/dist/{pdf-extract-ABWV3L5X.js.map → chunk-G4UYGRZR.js.map} +0 -0
  157. /package/dist/{chunk-I4QLI3JO.js.map → chunk-HDFYUFBI.js.map} +0 -0
  158. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-NHTEKEYE.js.map} +0 -0
  159. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-XJ5YYYGC.js.map} +0 -0
  160. /package/dist/{install-DXSNWZKO.js.map → install-OJFXHY6U.js.map} +0 -0
  161. /package/dist/{src-N3TPKGTY.js.map → pdf-extract-YHE54XU4.js.map} +0 -0
  162. /package/dist/{src-UVDX5UP6.js.map → src-CG52HEE7.js.map} +0 -0
  163. /package/dist/{tag-YY6YAABQ.js.map → src-PK5OZXCM.js.map} +0 -0
  164. /package/dist/{trace-HXIW7U2D.js.map → tag-JSHOMLBR.js.map} +0 -0
  165. /package/dist/{triage-ORG45R5W.js.map → trace-UMLOSIIR.js.map} +0 -0
  166. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-S7CGEK6H.js.map} +0 -0
@@ -4,19 +4,41 @@ import {
4
4
  fetchList,
5
5
  parseLifecycleStage,
6
6
  toLifecycleLabel
7
- } from "./chunk-WOHL4FTB.js";
8
- import "./chunk-SJJW77WN.js";
7
+ } from "./chunk-PRBRSIZP.js";
9
8
  import "./chunk-3CZU7XCS.js";
10
9
  import {
11
10
  getArgValue,
12
11
  hasFlag,
13
12
  resolveConfig
14
- } from "./chunk-A4OFTUVV.js";
13
+ } from "./chunk-3X3NW7QA.js";
14
+ import "./chunk-JMJVGMXI.js";
15
15
  import "./chunk-JWHYXEA2.js";
16
+ import "./chunk-JYGNMKZ5.js";
16
17
  import "./chunk-VKVL7WBN.js";
17
18
 
18
19
  // src/feature-flag.ts
19
20
  import process from "process";
21
+ import { readFileSync } from "fs";
22
+ function readContent(value) {
23
+ if (value.startsWith("@")) {
24
+ return readFileSync(value.slice(1), "utf8");
25
+ }
26
+ return value;
27
+ }
28
+ var DAY_MS = 24 * 60 * 60 * 1e3;
29
+ var DEFAULT_REVIEW_INTERVAL_DAYS = 7;
30
+ var DEFAULT_MAX_WINDOW_DAYS = 30;
31
+ function bakeStatus(flag) {
32
+ if (!flag.bakeStartedAt) return { ageDays: 0, burning: false };
33
+ const startedMs = new Date(flag.bakeStartedAt).getTime();
34
+ const ageMs = Date.now() - startedMs;
35
+ const maxWindow = flag.bakeMaxWindowDays ?? DEFAULT_MAX_WINDOW_DAYS;
36
+ return { ageDays: Math.floor(ageMs / DAY_MS), burning: ageMs > maxWindow * DAY_MS };
37
+ }
38
+ function formatBakeSuffix(flag) {
39
+ const { ageDays, burning } = bakeStatus(flag);
40
+ return ` baked=${ageDays}d${burning ? " \u{1F525}BURNING" : ""}`;
41
+ }
20
42
  function fetchFlags(apiUrl, token) {
21
43
  return fetchList(apiUrl, token, "/superadmin/feature-flags", "flags");
22
44
  }
@@ -35,28 +57,77 @@ async function handleList(argv, apiUrl, token) {
35
57
  const orgCount = flag.organizations?.length ?? 0;
36
58
  const userCount = flag.users?.length ?? 0;
37
59
  const lifecycle = `lifecycle=${toLifecycleLabel(flag.lifecycleStage)}`;
38
- const finalValue = flag.lifecycleStage === "active" ? "" : ` final=${flag.finalValue === true ? "true" : "false"}`;
60
+ const finalValue = flag.lifecycleStage === "active" || flag.lifecycleStage === "baking" ? "" : ` final=${flag.finalValue === true ? "true" : "false"}`;
61
+ const bake = flag.lifecycleStage === "baking" ? formatBakeSuffix(flag) : "";
39
62
  const stale = flag.isStale ? " [STALE]" : "";
40
- console.log(` ${flag.name} ${flag.description ?? ""} (${lifecycle}${finalValue}) [orgs=${orgCount}, users=${userCount}]${stale}`);
63
+ const product = flag.productCardId ? ` product=${flag.productTitle ?? flag.productCardId}` : "";
64
+ console.log(` ${flag.name} ${flag.description ?? ""} (${lifecycle}${finalValue})${bake} [orgs=${orgCount}, users=${userCount}]${product}${stale}`);
41
65
  }
42
66
  }
43
67
  async function handleCreate(argv, apiUrl, token) {
44
68
  const name = argv[0];
45
69
  if (!name || name.startsWith("--")) {
46
70
  console.error("Error: Missing flag name.");
47
- console.error("Usage: canary feature-flag create <name> [--description <text>]");
71
+ console.error("Usage: canary feature-flag create <name> [--description <text>] [--product <productCardId>]");
48
72
  process.exit(1);
49
73
  }
50
74
  const description = getArgValue(argv, "--description") ?? null;
75
+ const productCardId = getArgValue(argv, "--product") ?? null;
51
76
  const result = await apiRequest(apiUrl, token, "POST", "/superadmin/feature-flags", {
52
77
  name,
53
- description
78
+ description,
79
+ productCardId
54
80
  });
55
81
  if (!result.ok) {
56
82
  console.error(`Error: ${result.error}`);
57
83
  process.exit(1);
58
84
  }
59
- console.log(`Created feature flag: ${name}`);
85
+ console.log(`Created feature flag: ${name}${productCardId ? ` (product ${productCardId})` : ""}`);
86
+ }
87
+ async function handleProduct(argv, apiUrl, token) {
88
+ const name = argv[0];
89
+ const productCardId = getArgValue(argv, "--product");
90
+ const clear = hasFlag(argv, "--clear");
91
+ if (!name || name.startsWith("--")) {
92
+ console.error("Error: Missing flag name.");
93
+ console.error("Usage: canary feature-flag product <name> (--product <productCardId> | --clear)");
94
+ process.exit(1);
95
+ }
96
+ if (productCardId && clear || !productCardId && !clear) {
97
+ console.error("Error: Provide either --product <productCardId> or --clear.");
98
+ process.exit(1);
99
+ }
100
+ const result = await apiRequest(
101
+ apiUrl,
102
+ token,
103
+ "POST",
104
+ `/superadmin/feature-flags/${encodeURIComponent(name)}/product`,
105
+ { productCardId: clear ? null : productCardId }
106
+ );
107
+ if (!result.ok) {
108
+ console.error(`Error: ${result.error}`);
109
+ process.exit(1);
110
+ }
111
+ console.log(
112
+ clear ? `Cleared product link for ${name}` : `Linked ${name} to product ${result.flag?.productTitle ?? productCardId}`
113
+ );
114
+ }
115
+ async function handleProducts(argv, apiUrl, token) {
116
+ const jsonOutput = hasFlag(argv, "--json");
117
+ const search = getArgValue(argv, "--search");
118
+ const path = search ? `/superadmin/product-cards?search=${encodeURIComponent(search)}` : "/superadmin/product-cards";
119
+ const products = await fetchList(apiUrl, token, path, "products");
120
+ if (jsonOutput) {
121
+ console.log(JSON.stringify(products, null, 2));
122
+ return;
123
+ }
124
+ if (products.length === 0) {
125
+ console.log("No product cards found.");
126
+ return;
127
+ }
128
+ for (const product of products) {
129
+ console.log(` ${product.id} ${product.title} (stage=${product.stage}, org=${product.orgName})`);
130
+ }
60
131
  }
61
132
  async function handleDelete(argv, apiUrl, token) {
62
133
  const name = argv[0];
@@ -169,20 +240,21 @@ async function handleLifecycle(argv, apiUrl, token) {
169
240
  const name = argv[0];
170
241
  if (!name || name.startsWith("--")) {
171
242
  console.error("Error: Missing flag name.");
172
- console.error("Usage: canary feature-flag lifecycle <name> --stage <active|deprecated|ready_for_cleanup> [--final-value true|false]");
243
+ console.error('Usage: canary feature-flag lifecycle <name> --stage <active|baking|deprecated|ready_for_cleanup> [--final-value true|false] [--plan @file|"text"] [--review-interval <days>] [--max-window <days>]');
173
244
  process.exit(1);
174
245
  }
175
246
  const stage = parseLifecycleStage(argv);
176
247
  const rawFinalValue = getArgValue(argv, "--final-value");
177
248
  const clearFinalValue = hasFlag(argv, "--clear-final-value");
249
+ const gateDriven = stage === "active" || stage === "baking";
178
250
  if (rawFinalValue !== void 0 && clearFinalValue) {
179
251
  console.error("Error: use either --final-value or --clear-final-value, not both.");
180
252
  process.exit(1);
181
253
  }
182
254
  let finalValue = void 0;
183
- if (stage === "active") {
255
+ if (gateDriven) {
184
256
  if (rawFinalValue !== void 0) {
185
- console.error("Error: active stage does not accept --final-value.");
257
+ console.error(`Error: ${stage} stage does not accept --final-value.`);
186
258
  process.exit(1);
187
259
  }
188
260
  finalValue = void 0;
@@ -201,20 +273,74 @@ async function handleLifecycle(argv, apiUrl, token) {
201
273
  }
202
274
  finalValue = rawFinalValue === "true";
203
275
  }
276
+ let monitoringPlan;
277
+ let reviewIntervalDays;
278
+ let maxWindowDays;
279
+ if (stage === "baking") {
280
+ const rawPlan = getArgValue(argv, "--plan");
281
+ if (rawPlan !== void 0) monitoringPlan = readContent(rawPlan);
282
+ const rawInterval = getArgValue(argv, "--review-interval");
283
+ if (rawInterval !== void 0) {
284
+ reviewIntervalDays = Number.parseInt(rawInterval, 10);
285
+ if (!Number.isInteger(reviewIntervalDays) || reviewIntervalDays <= 0) {
286
+ console.error("Error: --review-interval must be a positive integer (days).");
287
+ process.exit(1);
288
+ }
289
+ }
290
+ const rawWindow = getArgValue(argv, "--max-window");
291
+ if (rawWindow !== void 0) {
292
+ maxWindowDays = Number.parseInt(rawWindow, 10);
293
+ if (!Number.isInteger(maxWindowDays) || maxWindowDays <= 0) {
294
+ console.error("Error: --max-window must be a positive integer (days).");
295
+ process.exit(1);
296
+ }
297
+ }
298
+ }
204
299
  const result = await apiRequest(
205
300
  apiUrl,
206
301
  token,
207
302
  "POST",
208
303
  `/superadmin/feature-flags/${encodeURIComponent(name)}/lifecycle`,
209
- { stage, finalValue }
304
+ { stage, finalValue, monitoringPlan, reviewIntervalDays, maxWindowDays }
210
305
  );
211
306
  if (!result.ok || !result.flag) {
212
307
  console.error(`Error: ${result.error ?? "Failed to update lifecycle"}`);
213
308
  process.exit(1);
214
309
  }
310
+ if (result.flag.lifecycleStage === "baking") {
311
+ const { ageDays } = bakeStatus(result.flag);
312
+ console.log(
313
+ `Put ${name} in the oven: stage=baking, baked=${ageDays}d, review every ${result.flag.bakeReviewIntervalDays ?? DEFAULT_REVIEW_INTERVAL_DAYS}d, burns after ${result.flag.bakeMaxWindowDays ?? DEFAULT_MAX_WINDOW_DAYS}d.`
314
+ );
315
+ return;
316
+ }
215
317
  const final = result.flag.lifecycleStage === "active" ? "(none)" : result.flag.finalValue === true ? "true" : "false";
216
318
  console.log(`Updated lifecycle for ${name}: stage=${toLifecycleLabel(result.flag.lifecycleStage)}, final=${final}`);
217
319
  }
320
+ async function handleOven(argv, apiUrl, token) {
321
+ const jsonOutput = hasFlag(argv, "--json");
322
+ const flags = await fetchList(apiUrl, token, "/superadmin/feature-flags/oven", "flags");
323
+ if (jsonOutput) {
324
+ console.log(JSON.stringify(flags, null, 2));
325
+ return;
326
+ }
327
+ if (flags.length === 0) {
328
+ console.log("The oven is empty \u2014 no flags are baking.");
329
+ return;
330
+ }
331
+ console.log(`In the oven (${flags.length}):`);
332
+ for (const flag of flags) {
333
+ const burn = flag.burning ? " \u{1F525}BURNING" : "";
334
+ const product = flag.productCardId ? ` product=${flag.productTitle ?? flag.productCardId}` : "";
335
+ console.log(
336
+ ` ${flag.name} baked=${flag.bakeAgeDays}d (review ${flag.bakeReviewIntervalDays}d / burns ${flag.bakeMaxWindowDays}d)${burn}${product}`
337
+ );
338
+ if (flag.bakeMonitoringPlan) {
339
+ const firstLine = flag.bakeMonitoringPlan.split("\n")[0]?.slice(0, 100) ?? "";
340
+ console.log(` plan: ${firstLine}`);
341
+ }
342
+ }
343
+ }
218
344
  function printFeatureFlagHelp() {
219
345
  console.log(
220
346
  [
@@ -222,22 +348,35 @@ function printFeatureFlagHelp() {
222
348
  "",
223
349
  "Sub-commands:",
224
350
  " list List all feature flags",
225
- " create <name> [--description <text>] Create a new flag",
351
+ " create <name> [--description <text>] [--product <productCardId>]",
352
+ " Create a new flag (optionally linked to a product)",
226
353
  " delete <name> Delete a flag and all its gates",
227
354
  " enable <name> (--org <orgId> | --user <userId>)",
228
355
  " Enable a flag for an org or user",
229
356
  " disable <name> (--org <orgId> | --user <userId>)",
230
- " Disable a flag for an org or user",
357
+ " Disable a flag for an org or user (kept as 'was enabled' history)",
231
358
  " lifecycle <name> --stage <stage> [--final-value true|false]",
232
359
  " Mark lifecycle + final value",
360
+ " --stage baking --plan @file [--review-interval <days>] [--max-window <days>]",
361
+ " Put a flag in the oven (requires a linked product card)",
362
+ " product <name> (--product <productCardId> | --clear)",
363
+ " Link/unlink a flag to a product board item",
364
+ " products [--search <text>] List product board items (find product card ids)",
365
+ " oven [--json] List flags currently baking (age + burning state)",
233
366
  "",
234
- "Stages: active, deprecated, ready_for_cleanup",
367
+ "Stages: active, baking, deprecated, ready_for_cleanup",
235
368
  "",
236
369
  "Options:",
237
370
  " --org <orgId> Target organization",
238
371
  " --user <userId> Target user",
372
+ " --product <productCardId> Product board item (cards.id) to link",
373
+ " --clear Clear the product link (product sub-command only)",
374
+ " --search <text> Filter product cards by title (products sub-command only)",
239
375
  " --final-value <true|false> Final value for deprecated/ready_for_cleanup",
240
376
  " --clear-final-value Clear final value (only valid with --stage active)",
377
+ ' --plan @file|"text" Monitoring/telemetry plan (required to enter baking)',
378
+ " --review-interval <days> Days between oven-check reviews (baking; default 7)",
379
+ " --max-window <days> Days before a baking flag 'burns' (default 30)",
241
380
  " --env <env> Target environment (prod, dev, local)",
242
381
  " --json Output as JSON (list only)",
243
382
  " --api-url <url> API URL override (takes precedence over --env)",
@@ -271,6 +410,15 @@ async function runFeatureFlag(argv) {
271
410
  case "lifecycle":
272
411
  await handleLifecycle(rest, apiUrl, token);
273
412
  break;
413
+ case "product":
414
+ await handleProduct(rest, apiUrl, token);
415
+ break;
416
+ case "products":
417
+ await handleProducts(rest, apiUrl, token);
418
+ break;
419
+ case "oven":
420
+ await handleOven(rest, apiUrl, token);
421
+ break;
274
422
  default:
275
423
  console.error(`Unknown sub-command: ${subCommand}`);
276
424
  printFeatureFlagHelp();
@@ -280,4 +428,4 @@ async function runFeatureFlag(argv) {
280
428
  export {
281
429
  runFeatureFlag
282
430
  };
283
- //# sourceMappingURL=feature-flag-T2LRZME3.js.map
431
+ //# sourceMappingURL=feature-flag-MHONEX6T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feature-flag.ts"],"sourcesContent":["/**\n * CLI Feature Flag Management\n *\n * Allows superadmins to manage feature flags via the CLI.\n */\n\nimport process from \"node:process\";\nimport { readFileSync } from \"node:fs\";\nimport { resolveConfig, getArgValue, hasFlag } from \"./auth.js\";\nimport {\n type LifecycleStage,\n toLifecycleLabel,\n parseLifecycleStage,\n apiRequest,\n fetchList,\n} from \"./cli-helpers.js\";\n\n/** Resolve a `--plan @file.md` (read file) or inline string value. */\nfunction readContent(value: string): string {\n if (value.startsWith(\"@\")) {\n return readFileSync(value.slice(1), \"utf8\");\n }\n return value;\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_REVIEW_INTERVAL_DAYS = 7;\nconst DEFAULT_MAX_WINDOW_DAYS = 30;\n\n/** Bake age (days) + burning state from a flag's bake metadata. */\nfunction bakeStatus(flag: {\n bakeStartedAt?: string | null;\n bakeMaxWindowDays?: number | null;\n}): { ageDays: number; burning: boolean } {\n if (!flag.bakeStartedAt) return { ageDays: 0, burning: false };\n const startedMs = new Date(flag.bakeStartedAt).getTime();\n const ageMs = Date.now() - startedMs;\n const maxWindow = flag.bakeMaxWindowDays ?? DEFAULT_MAX_WINDOW_DAYS;\n return { ageDays: Math.floor(ageMs / DAY_MS), burning: ageMs > maxWindow * DAY_MS };\n}\n\n/** ` baking 4d 🔥` style suffix for list rows. */\nfunction formatBakeSuffix(flag: {\n bakeStartedAt?: string | null;\n bakeMaxWindowDays?: number | null;\n}): string {\n const { ageDays, burning } = bakeStatus(flag);\n return ` baked=${ageDays}d${burning ? \" 🔥BURNING\" : \"\"}`;\n}\n\ntype FeatureFlag = {\n id: string;\n name: string;\n description: string | null;\n lifecycleStage: LifecycleStage;\n finalValue: boolean | null;\n bakeMonitoringPlan?: string | null;\n bakeStartedAt?: string | null;\n bakeReviewIntervalDays?: number | null;\n bakeMaxWindowDays?: number | null;\n createdAt: string;\n lastToggledAt: string;\n isStale: boolean;\n productCardId: string | null;\n productTitle: string | null;\n organizations?: Array<{\n gateId: string;\n orgId: string;\n orgName: string;\n createdAt: string;\n }>;\n users?: Array<{\n gateId: string;\n userId: string;\n userEmail: string | null;\n createdAt: string;\n }>;\n};\n\ntype FlagApiResponse = {\n ok: boolean;\n error?: string;\n flag?: FeatureFlag;\n};\n\ntype ProductCard = {\n id: string;\n title: string;\n stage: string;\n orgId: string;\n orgName: string;\n};\n\nfunction fetchFlags(apiUrl: string, token: string): Promise<FeatureFlag[]> {\n return fetchList<FeatureFlag>(apiUrl, token, \"/superadmin/feature-flags\", \"flags\");\n}\n\nasync function handleList(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const flags = await fetchFlags(apiUrl, token);\n\n if (jsonOutput) {\n console.log(JSON.stringify(flags, null, 2));\n return;\n }\n\n if (flags.length === 0) {\n console.log(\"No feature flags found.\");\n return;\n }\n\n for (const flag of flags) {\n const orgCount = flag.organizations?.length ?? 0;\n const userCount = flag.users?.length ?? 0;\n const lifecycle = `lifecycle=${toLifecycleLabel(flag.lifecycleStage)}`;\n // baking is gate-driven like active — no final value.\n const finalValue =\n flag.lifecycleStage === \"active\" || flag.lifecycleStage === \"baking\"\n ? \"\"\n : ` final=${flag.finalValue === true ? \"true\" : \"false\"}`;\n const bake = flag.lifecycleStage === \"baking\" ? formatBakeSuffix(flag) : \"\";\n const stale = flag.isStale ? \" [STALE]\" : \"\";\n const product = flag.productCardId\n ? ` product=${flag.productTitle ?? flag.productCardId}`\n : \"\";\n console.log(` ${flag.name} ${flag.description ?? \"\"} (${lifecycle}${finalValue})${bake} [orgs=${orgCount}, users=${userCount}]${product}${stale}`);\n }\n}\n\nasync function handleCreate(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag create <name> [--description <text>] [--product <productCardId>]\");\n process.exit(1);\n }\n\n const description = getArgValue(argv, \"--description\") ?? null;\n const productCardId = getArgValue(argv, \"--product\") ?? null;\n const result = await apiRequest<FlagApiResponse>(apiUrl, token, \"POST\", \"/superadmin/feature-flags\", {\n name,\n description,\n productCardId,\n });\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Created feature flag: ${name}${productCardId ? ` (product ${productCardId})` : \"\"}`);\n}\n\nasync function handleProduct(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const productCardId = getArgValue(argv, \"--product\");\n const clear = hasFlag(argv, \"--clear\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag product <name> (--product <productCardId> | --clear)\");\n process.exit(1);\n }\n\n if ((productCardId && clear) || (!productCardId && !clear)) {\n console.error(\"Error: Provide either --product <productCardId> or --clear.\");\n process.exit(1);\n }\n\n const result = await apiRequest<FlagApiResponse & { flag?: { productTitle?: string | null } }>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/product`,\n { productCardId: clear ? null : productCardId }\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(\n clear\n ? `Cleared product link for ${name}`\n : `Linked ${name} to product ${result.flag?.productTitle ?? productCardId}`\n );\n}\n\nasync function handleProducts(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const search = getArgValue(argv, \"--search\");\n const path = search\n ? `/superadmin/product-cards?search=${encodeURIComponent(search)}`\n : \"/superadmin/product-cards\";\n const products = await fetchList<ProductCard>(apiUrl, token, path, \"products\");\n\n if (jsonOutput) {\n console.log(JSON.stringify(products, null, 2));\n return;\n }\n\n if (products.length === 0) {\n console.log(\"No product cards found.\");\n return;\n }\n\n for (const product of products) {\n console.log(` ${product.id} ${product.title} (stage=${product.stage}, org=${product.orgName})`);\n }\n}\n\nasync function handleDelete(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag delete <name>\");\n process.exit(1);\n }\n\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\", `/superadmin/feature-flags/${encodeURIComponent(name)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Deleted feature flag: ${name}`);\n}\n\nasync function handleEnable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n const userId = getArgValue(argv, \"--user\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag enable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId && userId) {\n console.error(\"Error: Provide either --org or --user, not both.\");\n process.exit(1);\n }\n\n if (!orgId && !userId) {\n console.error(\"Error: Provide either --org <orgId> or --user <userId>.\");\n console.error(\"Usage: canary feature-flag enable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId) {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Enabled ${name} for org ${orgId}`);\n } else {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/users/${encodeURIComponent(userId!)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Enabled ${name} for user ${userId}`);\n }\n}\n\nasync function handleDisable(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n const orgId = getArgValue(argv, \"--org\");\n const userId = getArgValue(argv, \"--user\");\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag disable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId && userId) {\n console.error(\"Error: Provide either --org or --user, not both.\");\n process.exit(1);\n }\n\n if (!orgId && !userId) {\n console.error(\"Error: Provide either --org <orgId> or --user <userId>.\");\n console.error(\"Usage: canary feature-flag disable <name> (--org <orgId> | --user <userId>)\");\n process.exit(1);\n }\n\n if (orgId) {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/organizations/${encodeURIComponent(orgId)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Disabled ${name} for org ${orgId}`);\n } else {\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"DELETE\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/users/${encodeURIComponent(userId!)}`\n );\n\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n process.exit(1);\n }\n\n console.log(`Disabled ${name} for user ${userId}`);\n }\n}\n\nasync function handleLifecycle(argv: string[], apiUrl: string, token: string): Promise<void> {\n const name = argv[0];\n\n if (!name || name.startsWith(\"--\")) {\n console.error(\"Error: Missing flag name.\");\n console.error(\"Usage: canary feature-flag lifecycle <name> --stage <active|baking|deprecated|ready_for_cleanup> [--final-value true|false] [--plan @file|\\\"text\\\"] [--review-interval <days>] [--max-window <days>]\");\n process.exit(1);\n }\n\n const stage = parseLifecycleStage(argv);\n const rawFinalValue = getArgValue(argv, \"--final-value\");\n const clearFinalValue = hasFlag(argv, \"--clear-final-value\");\n\n // baking and active are gate-driven (no final value); deprecated and\n // ready_for_cleanup carry a forced final value.\n const gateDriven = stage === \"active\" || stage === \"baking\";\n\n if (rawFinalValue !== undefined && clearFinalValue) {\n console.error(\"Error: use either --final-value or --clear-final-value, not both.\");\n process.exit(1);\n }\n\n let finalValue: boolean | undefined = undefined;\n\n if (gateDriven) {\n if (rawFinalValue !== undefined) {\n console.error(`Error: ${stage} stage does not accept --final-value.`);\n process.exit(1);\n }\n finalValue = undefined;\n } else {\n if (clearFinalValue) {\n console.error(\"Error: --clear-final-value can only be used with --stage active.\");\n process.exit(1);\n }\n if (!rawFinalValue) {\n console.error(\"Error: --final-value true|false is required for deprecated or ready_for_cleanup.\");\n process.exit(1);\n }\n if (rawFinalValue !== \"true\" && rawFinalValue !== \"false\") {\n console.error(\"Error: --final-value must be true or false.\");\n process.exit(1);\n }\n finalValue = rawFinalValue === \"true\";\n }\n\n // Oven (baking) options: monitoring plan + review cadence.\n let monitoringPlan: string | undefined;\n let reviewIntervalDays: number | undefined;\n let maxWindowDays: number | undefined;\n if (stage === \"baking\") {\n const rawPlan = getArgValue(argv, \"--plan\");\n if (rawPlan !== undefined) monitoringPlan = readContent(rawPlan);\n const rawInterval = getArgValue(argv, \"--review-interval\");\n if (rawInterval !== undefined) {\n reviewIntervalDays = Number.parseInt(rawInterval, 10);\n if (!Number.isInteger(reviewIntervalDays) || reviewIntervalDays <= 0) {\n console.error(\"Error: --review-interval must be a positive integer (days).\");\n process.exit(1);\n }\n }\n const rawWindow = getArgValue(argv, \"--max-window\");\n if (rawWindow !== undefined) {\n maxWindowDays = Number.parseInt(rawWindow, 10);\n if (!Number.isInteger(maxWindowDays) || maxWindowDays <= 0) {\n console.error(\"Error: --max-window must be a positive integer (days).\");\n process.exit(1);\n }\n }\n }\n\n const result = await apiRequest<FlagApiResponse>(\n apiUrl, token, \"POST\",\n `/superadmin/feature-flags/${encodeURIComponent(name)}/lifecycle`,\n { stage, finalValue, monitoringPlan, reviewIntervalDays, maxWindowDays }\n );\n\n if (!result.ok || !result.flag) {\n console.error(`Error: ${result.error ?? \"Failed to update lifecycle\"}`);\n process.exit(1);\n }\n\n if (result.flag.lifecycleStage === \"baking\") {\n const { ageDays } = bakeStatus(result.flag);\n console.log(\n `Put ${name} in the oven: stage=baking, baked=${ageDays}d, review every ${result.flag.bakeReviewIntervalDays ?? DEFAULT_REVIEW_INTERVAL_DAYS}d, burns after ${result.flag.bakeMaxWindowDays ?? DEFAULT_MAX_WINDOW_DAYS}d.`\n );\n return;\n }\n\n const final =\n result.flag.lifecycleStage === \"active\"\n ? \"(none)\"\n : result.flag.finalValue === true\n ? \"true\"\n : \"false\";\n console.log(`Updated lifecycle for ${name}: stage=${toLifecycleLabel(result.flag.lifecycleStage)}, final=${final}`);\n}\n\ntype OvenFlag = {\n id: string;\n name: string;\n description: string | null;\n productCardId: string | null;\n productTitle: string | null;\n bakeMonitoringPlan: string | null;\n bakeStartedAt: string | null;\n bakeReviewIntervalDays: number;\n bakeMaxWindowDays: number;\n bakeAgeDays: number;\n burning: boolean;\n};\n\nasync function handleOven(argv: string[], apiUrl: string, token: string): Promise<void> {\n const jsonOutput = hasFlag(argv, \"--json\");\n const flags = await fetchList<OvenFlag>(apiUrl, token, \"/superadmin/feature-flags/oven\", \"flags\");\n\n if (jsonOutput) {\n console.log(JSON.stringify(flags, null, 2));\n return;\n }\n\n if (flags.length === 0) {\n console.log(\"The oven is empty — no flags are baking.\");\n return;\n }\n\n console.log(`In the oven (${flags.length}):`);\n for (const flag of flags) {\n const burn = flag.burning ? \" 🔥BURNING\" : \"\";\n const product = flag.productCardId ? ` product=${flag.productTitle ?? flag.productCardId}` : \"\";\n console.log(\n ` ${flag.name} baked=${flag.bakeAgeDays}d (review ${flag.bakeReviewIntervalDays}d / burns ${flag.bakeMaxWindowDays}d)${burn}${product}`\n );\n if (flag.bakeMonitoringPlan) {\n const firstLine = flag.bakeMonitoringPlan.split(\"\\n\")[0]?.slice(0, 100) ?? \"\";\n console.log(` plan: ${firstLine}`);\n }\n }\n}\n\nfunction printFeatureFlagHelp(): void {\n console.log(\n [\n \"Usage: canary feature-flag <sub-command> [options]\",\n \"\",\n \"Sub-commands:\",\n \" list List all feature flags\",\n \" create <name> [--description <text>] [--product <productCardId>]\",\n \" Create a new flag (optionally linked to a product)\",\n \" delete <name> Delete a flag and all its gates\",\n \" enable <name> (--org <orgId> | --user <userId>)\",\n \" Enable a flag for an org or user\",\n \" disable <name> (--org <orgId> | --user <userId>)\",\n \" Disable a flag for an org or user (kept as 'was enabled' history)\",\n \" lifecycle <name> --stage <stage> [--final-value true|false]\",\n \" Mark lifecycle + final value\",\n \" --stage baking --plan @file [--review-interval <days>] [--max-window <days>]\",\n \" Put a flag in the oven (requires a linked product card)\",\n \" product <name> (--product <productCardId> | --clear)\",\n \" Link/unlink a flag to a product board item\",\n \" products [--search <text>] List product board items (find product card ids)\",\n \" oven [--json] List flags currently baking (age + burning state)\",\n \"\",\n \"Stages: active, baking, deprecated, ready_for_cleanup\",\n \"\",\n \"Options:\",\n \" --org <orgId> Target organization\",\n \" --user <userId> Target user\",\n \" --product <productCardId> Product board item (cards.id) to link\",\n \" --clear Clear the product link (product sub-command only)\",\n \" --search <text> Filter product cards by title (products sub-command only)\",\n \" --final-value <true|false> Final value for deprecated/ready_for_cleanup\",\n \" --clear-final-value Clear final value (only valid with --stage active)\",\n \" --plan @file|\\\"text\\\" Monitoring/telemetry plan (required to enter baking)\",\n \" --review-interval <days> Days between oven-check reviews (baking; default 7)\",\n \" --max-window <days> Days before a baking flag 'burns' (default 30)\",\n \" --env <env> Target environment (prod, dev, local)\",\n \" --json Output as JSON (list only)\",\n \" --api-url <url> API URL override (takes precedence over --env)\",\n \" --token <key> API token override\",\n ].join(\"\\n\")\n );\n}\n\nexport async function runFeatureFlag(argv: string[]): Promise<void> {\n const [subCommand, ...rest] = argv;\n\n if (!subCommand || subCommand === \"help\" || hasFlag(argv, \"--help\", \"-h\")) {\n printFeatureFlagHelp();\n return;\n }\n\n const { apiUrl, token } = await resolveConfig(argv);\n\n switch (subCommand) {\n case \"list\":\n await handleList(rest, apiUrl, token);\n break;\n case \"create\":\n await handleCreate(rest, apiUrl, token);\n break;\n case \"delete\":\n await handleDelete(rest, apiUrl, token);\n break;\n case \"enable\":\n await handleEnable(rest, apiUrl, token);\n break;\n case \"disable\":\n await handleDisable(rest, apiUrl, token);\n break;\n case \"lifecycle\":\n await handleLifecycle(rest, apiUrl, token);\n break;\n case \"product\":\n await handleProduct(rest, apiUrl, token);\n break;\n case \"products\":\n await handleProducts(rest, apiUrl, token);\n break;\n case \"oven\":\n await handleOven(rest, apiUrl, token);\n break;\n default:\n console.error(`Unknown sub-command: ${subCommand}`);\n printFeatureFlagHelp();\n process.exit(1);\n }\n}\n"],"mappings":"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","names":[]}