@hs-x/cli 0.3.6 → 0.4.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 (211) hide show
  1. package/README.md +126 -0
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +58 -59
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/cli/kit.d.ts.map +1 -1
  6. package/dist/cli/kit.js +1 -1
  7. package/dist/cli/kit.js.map +1 -1
  8. package/dist/cloudflare-auth.d.ts +19 -0
  9. package/dist/cloudflare-auth.d.ts.map +1 -1
  10. package/dist/cloudflare-auth.js +83 -2
  11. package/dist/cloudflare-auth.js.map +1 -1
  12. package/dist/cloudflare-oauth.d.ts.map +1 -1
  13. package/dist/cloudflare-oauth.js +18 -19
  14. package/dist/cloudflare-oauth.js.map +1 -1
  15. package/dist/cloudflare-scoped-resource.d.ts +41 -0
  16. package/dist/cloudflare-scoped-resource.d.ts.map +1 -0
  17. package/dist/cloudflare-scoped-resource.js +74 -0
  18. package/dist/cloudflare-scoped-resource.js.map +1 -0
  19. package/dist/command-catalog.d.ts +13 -0
  20. package/dist/command-catalog.d.ts.map +1 -0
  21. package/dist/command-catalog.js +50 -0
  22. package/dist/command-catalog.js.map +1 -0
  23. package/dist/commands/completion.d.ts.map +1 -1
  24. package/dist/commands/completion.js +287 -47
  25. package/dist/commands/completion.js.map +1 -1
  26. package/dist/commands/connect.d.ts.map +1 -1
  27. package/dist/commands/connect.js +274 -38
  28. package/dist/commands/connect.js.map +1 -1
  29. package/dist/commands/control-plane-read.d.ts +31 -3
  30. package/dist/commands/control-plane-read.d.ts.map +1 -1
  31. package/dist/commands/control-plane-read.js +136 -37
  32. package/dist/commands/control-plane-read.js.map +1 -1
  33. package/dist/commands/deploy-retry.d.ts +20 -0
  34. package/dist/commands/deploy-retry.d.ts.map +1 -0
  35. package/dist/commands/deploy-retry.js +106 -0
  36. package/dist/commands/deploy-retry.js.map +1 -0
  37. package/dist/commands/deploy.d.ts +16 -8
  38. package/dist/commands/deploy.d.ts.map +1 -1
  39. package/dist/commands/deploy.js +1027 -373
  40. package/dist/commands/deploy.js.map +1 -1
  41. package/dist/commands/dev.d.ts +19 -1
  42. package/dist/commands/dev.d.ts.map +1 -1
  43. package/dist/commands/dev.js +1378 -88
  44. package/dist/commands/dev.js.map +1 -1
  45. package/dist/commands/doctor.d.ts.map +1 -1
  46. package/dist/commands/doctor.js +27 -10
  47. package/dist/commands/doctor.js.map +1 -1
  48. package/dist/commands/flags.d.ts +9 -4
  49. package/dist/commands/flags.d.ts.map +1 -1
  50. package/dist/commands/flags.js +208 -80
  51. package/dist/commands/flags.js.map +1 -1
  52. package/dist/commands/help-command.d.ts.map +1 -1
  53. package/dist/commands/help-command.js +177 -86
  54. package/dist/commands/help-command.js.map +1 -1
  55. package/dist/commands/init.d.ts.map +1 -1
  56. package/dist/commands/init.js +5 -0
  57. package/dist/commands/init.js.map +1 -1
  58. package/dist/commands/link.d.ts.map +1 -1
  59. package/dist/commands/link.js +74 -15
  60. package/dist/commands/link.js.map +1 -1
  61. package/dist/commands/project.d.ts +107 -0
  62. package/dist/commands/project.d.ts.map +1 -0
  63. package/dist/commands/project.js +586 -0
  64. package/dist/commands/project.js.map +1 -0
  65. package/dist/commands/secrets.d.ts +2 -2
  66. package/dist/commands/status.d.ts.map +1 -1
  67. package/dist/commands/status.js +15 -5
  68. package/dist/commands/status.js.map +1 -1
  69. package/dist/commands/validate.d.ts +19 -0
  70. package/dist/commands/validate.d.ts.map +1 -1
  71. package/dist/commands/validate.js +26 -7
  72. package/dist/commands/validate.js.map +1 -1
  73. package/dist/constants.d.ts +1 -1
  74. package/dist/constants.js +1 -1
  75. package/dist/control-plane-tenant-provisioning.d.ts +24 -0
  76. package/dist/control-plane-tenant-provisioning.d.ts.map +1 -0
  77. package/dist/control-plane-tenant-provisioning.js +85 -0
  78. package/dist/control-plane-tenant-provisioning.js.map +1 -0
  79. package/dist/deploy-targets.d.ts +24 -0
  80. package/dist/deploy-targets.d.ts.map +1 -0
  81. package/dist/deploy-targets.js +38 -0
  82. package/dist/deploy-targets.js.map +1 -0
  83. package/dist/dev/account-selection.d.ts +16 -0
  84. package/dist/dev/account-selection.d.ts.map +1 -0
  85. package/dist/dev/account-selection.js +22 -0
  86. package/dist/dev/account-selection.js.map +1 -0
  87. package/dist/dev/browser-log-bridge.d.ts +32 -0
  88. package/dist/dev/browser-log-bridge.d.ts.map +1 -0
  89. package/dist/dev/browser-log-bridge.js +156 -0
  90. package/dist/dev/browser-log-bridge.js.map +1 -0
  91. package/dist/dev/compat-shim.d.ts +3 -3
  92. package/dist/dev/compat-shim.d.ts.map +1 -1
  93. package/dist/dev/compat-shim.js +29 -4
  94. package/dist/dev/compat-shim.js.map +1 -1
  95. package/dist/dev/component-selection.d.ts +25 -0
  96. package/dist/dev/component-selection.d.ts.map +1 -0
  97. package/dist/dev/component-selection.js +79 -0
  98. package/dist/dev/component-selection.js.map +1 -0
  99. package/dist/dev/console-capture.d.ts +18 -0
  100. package/dist/dev/console-capture.d.ts.map +1 -0
  101. package/dist/dev/console-capture.js +57 -0
  102. package/dist/dev/console-capture.js.map +1 -0
  103. package/dist/dev/event-bus.d.ts +1 -1
  104. package/dist/dev/event-bus.js +1 -1
  105. package/dist/dev/frontend-log-relay.d.ts +27 -0
  106. package/dist/dev/frontend-log-relay.d.ts.map +1 -0
  107. package/dist/dev/frontend-log-relay.js +102 -0
  108. package/dist/dev/frontend-log-relay.js.map +1 -0
  109. package/dist/dev/invoke.d.ts +3 -4
  110. package/dist/dev/invoke.d.ts.map +1 -1
  111. package/dist/dev/invoke.js +9 -4
  112. package/dist/dev/invoke.js.map +1 -1
  113. package/dist/dev/keyboard.d.ts +1 -0
  114. package/dist/dev/keyboard.d.ts.map +1 -1
  115. package/dist/dev/keyboard.js +9 -3
  116. package/dist/dev/keyboard.js.map +1 -1
  117. package/dist/dev/log-server.js +3 -0
  118. package/dist/dev/log-server.js.map +1 -1
  119. package/dist/dev/loopback-http-listener.d.ts +18 -0
  120. package/dist/dev/loopback-http-listener.d.ts.map +1 -0
  121. package/dist/dev/loopback-http-listener.js +91 -0
  122. package/dist/dev/loopback-http-listener.js.map +1 -0
  123. package/dist/dev/managed-session.d.ts +124 -0
  124. package/dist/dev/managed-session.d.ts.map +1 -0
  125. package/dist/dev/managed-session.js +597 -0
  126. package/dist/dev/managed-session.js.map +1 -0
  127. package/dist/dev/port-process.d.ts +21 -0
  128. package/dist/dev/port-process.d.ts.map +1 -0
  129. package/dist/dev/port-process.js +145 -0
  130. package/dist/dev/port-process.js.map +1 -0
  131. package/dist/dev/session-manager.d.ts +11 -1
  132. package/dist/dev/session-manager.d.ts.map +1 -1
  133. package/dist/dev/session-manager.js +65 -14
  134. package/dist/dev/session-manager.js.map +1 -1
  135. package/dist/dev/stream-event-format.d.ts +14 -0
  136. package/dist/dev/stream-event-format.d.ts.map +1 -0
  137. package/dist/dev/stream-event-format.js +262 -0
  138. package/dist/dev/stream-event-format.js.map +1 -0
  139. package/dist/dev/stream-filter.d.ts.map +1 -1
  140. package/dist/dev/stream-filter.js +16 -1
  141. package/dist/dev/stream-filter.js.map +1 -1
  142. package/dist/dev/stream-renderer.d.ts +4 -2
  143. package/dist/dev/stream-renderer.d.ts.map +1 -1
  144. package/dist/dev/stream-renderer.js +224 -84
  145. package/dist/dev/stream-renderer.js.map +1 -1
  146. package/dist/errors-registry.d.ts.map +1 -1
  147. package/dist/errors-registry.js +246 -0
  148. package/dist/errors-registry.js.map +1 -1
  149. package/dist/help.d.ts.map +1 -1
  150. package/dist/help.js +20 -3
  151. package/dist/help.js.map +1 -1
  152. package/dist/hubspot-app-logs.d.ts +38 -5
  153. package/dist/hubspot-app-logs.d.ts.map +1 -1
  154. package/dist/hubspot-app-logs.js +88 -17
  155. package/dist/hubspot-app-logs.js.map +1 -1
  156. package/dist/hubspot-auth.d.ts +19 -1
  157. package/dist/hubspot-auth.d.ts.map +1 -1
  158. package/dist/hubspot-auth.js +228 -83
  159. package/dist/hubspot-auth.js.map +1 -1
  160. package/dist/hubspot-developer-client.d.ts +87 -0
  161. package/dist/hubspot-developer-client.d.ts.map +1 -1
  162. package/dist/hubspot-developer-client.js +232 -1
  163. package/dist/hubspot-developer-client.js.map +1 -1
  164. package/dist/hubspot-project-archive.d.ts +21 -0
  165. package/dist/hubspot-project-archive.d.ts.map +1 -0
  166. package/dist/hubspot-project-archive.js +281 -0
  167. package/dist/hubspot-project-archive.js.map +1 -0
  168. package/dist/hubspot-project.d.ts +32 -0
  169. package/dist/hubspot-project.d.ts.map +1 -0
  170. package/dist/hubspot-project.js +269 -0
  171. package/dist/hubspot-project.js.map +1 -0
  172. package/dist/local-project-picker.js +1 -1
  173. package/dist/local-project-picker.js.map +1 -1
  174. package/dist/machine-artifact.d.ts +16 -0
  175. package/dist/machine-artifact.d.ts.map +1 -0
  176. package/dist/machine-artifact.js +18 -0
  177. package/dist/machine-artifact.js.map +1 -0
  178. package/dist/machine-id.d.ts +15 -0
  179. package/dist/machine-id.d.ts.map +1 -1
  180. package/dist/machine-id.js +53 -0
  181. package/dist/machine-id.js.map +1 -1
  182. package/dist/mutation-consent.d.ts +20 -0
  183. package/dist/mutation-consent.d.ts.map +1 -0
  184. package/dist/mutation-consent.js +9 -0
  185. package/dist/mutation-consent.js.map +1 -0
  186. package/dist/output-context.d.ts +3 -0
  187. package/dist/output-context.d.ts.map +1 -1
  188. package/dist/output-context.js +7 -1
  189. package/dist/output-context.js.map +1 -1
  190. package/dist/prompt.d.ts.map +1 -1
  191. package/dist/prompt.js +123 -48
  192. package/dist/prompt.js.map +1 -1
  193. package/dist/reporter/index.js +1 -1
  194. package/dist/reporter/index.js.map +1 -1
  195. package/dist/reporter/style.d.ts.map +1 -1
  196. package/dist/reporter/style.js +5 -0
  197. package/dist/reporter/style.js.map +1 -1
  198. package/dist/services/hubspot-dev-client.js +3 -0
  199. package/dist/services/hubspot-dev-client.js.map +1 -1
  200. package/dist/services/telemetry.d.ts +1 -2
  201. package/dist/services/telemetry.d.ts.map +1 -1
  202. package/dist/services/telemetry.js +1 -10
  203. package/dist/services/telemetry.js.map +1 -1
  204. package/dist/telemetry.d.ts.map +1 -1
  205. package/dist/telemetry.js +1 -7
  206. package/dist/telemetry.js.map +1 -1
  207. package/dist/tenant-data-plane-provisioning.d.ts +35 -0
  208. package/dist/tenant-data-plane-provisioning.d.ts.map +1 -0
  209. package/dist/tenant-data-plane-provisioning.js +38 -0
  210. package/dist/tenant-data-plane-provisioning.js.map +1 -0
  211. package/package.json +14 -12
@@ -5,7 +5,7 @@ import type { FlagDefinition } from '@hs-x/types';
5
5
  import { Option } from 'effect';
6
6
  import { CliError } from '../cli-error.js';
7
7
  import { readLocalProjectContext } from '../project-context.js';
8
- import { BACK, type ConfirmOptions, type PromptOptions, type SelectOptions, promptConfirm, promptSelect } from '../prompt.js';
8
+ import { BACK, type ConfirmOptions, type PromptOptions, type SelectOptions, promptSelect } from '../prompt.js';
9
9
  import type { Reporter as SyncReporter } from '../reporter/types.js';
10
10
  import type { CliResult } from '../result.js';
11
11
  import { AccountStore } from '../services/account-store.js';
@@ -24,6 +24,10 @@ export interface FlagCreatePromptDeps {
24
24
  readonly text: (opts: PromptOptions) => Promise<string | undefined | typeof BACK>;
25
25
  readonly confirm: (opts: ConfirmOptions) => Promise<boolean | undefined | typeof BACK>;
26
26
  }
27
+ interface FlagConsentPromptDeps {
28
+ readonly isInteractive: () => boolean;
29
+ readonly confirm: (opts: ConfirmOptions) => Promise<boolean | undefined>;
30
+ }
27
31
  export interface ResolvedFlagsContext {
28
32
  readonly projectId: string;
29
33
  readonly environment: string;
@@ -80,6 +84,8 @@ export declare function runCreate(input: {
80
84
  readonly client: FlagAuthoringClient;
81
85
  readonly scope: RuntimeTenantSyncReadScope;
82
86
  readonly reporter: SyncReporter;
87
+ readonly yes?: boolean;
88
+ readonly prompts?: FlagConsentPromptDeps;
83
89
  }): Promise<CliResult>;
84
90
  export type FlagsAction = 'list' | 'create' | 'enable' | 'disable' | 'archive';
85
91
  /** Dependency-injected action seam used by the bare `hs-x flags` TTY flow. */
@@ -94,10 +100,8 @@ export interface FlagCreateWizardInput {
94
100
  }
95
101
  /** Build and submit a bounded typed definition using only schema-supported exact-match rules. */
96
102
  export declare function runCreateWizard(input: FlagCreateWizardInput): Promise<CliResult>;
97
- export interface FlagMutationPromptDeps {
98
- readonly isInteractive: () => boolean;
103
+ export interface FlagMutationPromptDeps extends FlagConsentPromptDeps {
99
104
  readonly select: typeof promptSelect;
100
- readonly confirm: typeof promptConfirm;
101
105
  }
102
106
  export declare function runSetState(input: {
103
107
  readonly argv: readonly string[];
@@ -156,4 +160,5 @@ export declare const flagsCmd: Command.Command<"flags", AccountStore | FileSyste
156
160
  readonly cloudflareApiToken: Option.Option<string>;
157
161
  }>;
158
162
  }>;
163
+ export {};
159
164
  //# sourceMappingURL=flags.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/commands/flags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/commands/flags.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { activeSession } from '../account-store.js';
9
9
  import { CliError, exitWith } from '../cli-error.js';
10
10
  import { accountIdOption, controlPlaneUrlOption, jsonOption, runHandler, yesOption, } from '../cli/kit.js';
11
11
  import { DEFAULT_CONTROL_PLANE_URL } from '../config.js';
12
+ import { requestMutationConsent } from '../mutation-consent.js';
12
13
  import { isMachineOutput } from '../output-context.js';
13
14
  import { canDiscoverHubSpotAppIdFromTenantBindings, readLocalProjectContext, resolveHubSpotAppId, resolveHubSpotAppIdFromTenantBindings, resolveProjectId, resolveRuntimeOrigin, } from '../project-context.js';
14
15
  import { ProjectPickerCancelled, createControlPlaneFlagAuthoringClient, discoverProjectRuntimeBindings, discoverProjects, resolveAccountId, resolveProjectSelection, resolveRuntimeBinding, } from '../project-picker.js';
@@ -30,21 +31,56 @@ function resolveFlag(argv, flag) {
30
31
  }
31
32
  return undefined;
32
33
  }
33
- function fail(reporter, json, message) {
34
- if (json) {
35
- reporter.raw(`${JSON.stringify({ ok: false, error: message })}\n`);
34
+ function isMachineReporter(reporter) {
35
+ return reporter.mode === 'json' || reporter.mode === 'ndjson';
36
+ }
37
+ function fail(reporter, _json, message, code = 'HSX_E_INPUT_INVALID') {
38
+ if (isMachineReporter(reporter)) {
39
+ reporter.error(code, message);
40
+ reporter.done(undefined, 1);
36
41
  }
37
42
  else {
38
43
  reporter.rawErr(`Error: ${message}\n`);
39
44
  }
40
45
  return { exitCode: 1 };
41
46
  }
47
+ function succeed(reporter, data, human) {
48
+ if (isMachineReporter(reporter)) {
49
+ reporter.data(data);
50
+ reporter.done();
51
+ return;
52
+ }
53
+ human();
54
+ }
55
+ function flagTransportErrorCode(error) {
56
+ if (!(error instanceof RuntimeFlagAuthoringError))
57
+ return 'HSX_E_INTERNAL';
58
+ if (error.status === 401 || error.status === 403)
59
+ return 'HSX_E_API_UNAUTHORIZED';
60
+ if (error.status >= 500)
61
+ return 'HSX_E_API_SERVER';
62
+ return 'HSX_E_API_BAD_REQUEST';
63
+ }
42
64
  const liveFlagsPrompts = {
43
65
  isInteractive,
44
66
  select: promptSelect,
45
67
  text: promptText,
46
68
  confirm: promptConfirm,
47
69
  };
70
+ const liveFlagConsentPrompts = {
71
+ isInteractive,
72
+ confirm: promptConfirm,
73
+ };
74
+ const FLAG_DEFINITION_CONSENT_ERROR = 'Creating or updating a flag requires explicit consent in machine or non-interactive mode. Re-run with --yes.';
75
+ function flagStateConsentError(state) {
76
+ return `Changing a flag to ${state} requires explicit consent in machine or non-interactive mode. Re-run with --yes.`;
77
+ }
78
+ function hasExplicitYes(argv) {
79
+ return argv.some((arg) => arg === '--yes' || arg === '-y');
80
+ }
81
+ function flagDefinitionPlan(definition, options) {
82
+ return `\nFlag definition plan\n${JSON.stringify(definition, null, 2)}\n\n${options.wizard ? 'Percentage rollouts are available through --file only.\n\n' : ''}`;
83
+ }
48
84
  class FlagsPromptCancelled extends Error {
49
85
  constructor() {
50
86
  super('Cancelled.');
@@ -237,10 +273,45 @@ export async function flagsCommand(input) {
237
273
  const { argv, json } = input;
238
274
  const root = input.root ?? process.cwd();
239
275
  const subcommand = argv[1];
240
- const reporter = createReporter({ command: 'flags', argv });
276
+ const reporter = createReporter({
277
+ command: 'flags',
278
+ argv: json && !argv.includes('--json') ? [...argv, '--json'] : argv,
279
+ });
241
280
  if (!subcommand || !SUBCOMMANDS.has(subcommand)) {
242
281
  return fail(reporter, json, `Usage: hs-x flags <${[...SUBCOMMANDS].join('|')}> [options]`);
243
282
  }
283
+ const machineOutput = json || isMachineOutput();
284
+ const interactive = isInteractive();
285
+ const yes = hasExplicitYes(argv);
286
+ if ((subcommand === 'create' || subcommand === 'define') && !resolveFlag(argv, '--file')) {
287
+ if (machineOutput || !interactive) {
288
+ return fail(reporter, json, 'Require --file <flag-definition.json> for `flags create` outside an interactive terminal.');
289
+ }
290
+ }
291
+ if ((subcommand === 'enable' || subcommand === 'disable' || subcommand === 'archive') &&
292
+ !resolveFlag(argv, '--key') &&
293
+ (machineOutput || !interactive)) {
294
+ const state = subcommand === 'enable' ? 'enabled' : subcommand === 'disable' ? 'disabled' : 'archived';
295
+ return fail(reporter, json, `Require --key <flagKey> to ${state} a flag.`);
296
+ }
297
+ if (subcommand !== 'list' && (machineOutput || !interactive)) {
298
+ const consent = await requestMutationConsent({
299
+ yes,
300
+ machineOutput,
301
+ interactive,
302
+ message: 'Apply this flag mutation?',
303
+ confirm: promptConfirm,
304
+ });
305
+ if (consent === 'require-yes') {
306
+ return fail(reporter, json, subcommand === 'create' || subcommand === 'define'
307
+ ? FLAG_DEFINITION_CONSENT_ERROR
308
+ : flagStateConsentError(subcommand === 'enable'
309
+ ? 'enabled'
310
+ : subcommand === 'disable'
311
+ ? 'disabled'
312
+ : 'archived'));
313
+ }
314
+ }
244
315
  const local = await readLocalProjectContext(root);
245
316
  const projectId = resolveProjectId({
246
317
  explicit: resolveFlag(argv, '--project-id'),
@@ -312,7 +383,7 @@ export async function flagsCommand(input) {
312
383
  return await runList({ argv, json, client, reporter });
313
384
  case 'create':
314
385
  case 'define':
315
- return await runCreate({ argv, json, client, scope, reporter });
386
+ return await runCreate({ argv, json, client, scope, reporter, yes });
316
387
  case 'enable':
317
388
  return await runSetState({ argv, json, client, state: 'enabled', reporter });
318
389
  case 'disable':
@@ -325,29 +396,31 @@ export async function flagsCommand(input) {
325
396
  }
326
397
  catch (error) {
327
398
  if (error instanceof RuntimeFlagAuthoringError) {
328
- return fail(reporter, json, `Tenant Worker rejected the request (${error.status}): ${error.message}`);
399
+ return fail(reporter, json, `Tenant Worker rejected the request (${error.status}): ${error.message}`, flagTransportErrorCode(error));
329
400
  }
330
- return fail(reporter, json, error instanceof Error ? error.message : String(error));
401
+ return fail(reporter, json, error instanceof Error ? error.message : String(error), flagTransportErrorCode(error));
331
402
  }
332
403
  }
333
404
  async function runList(input) {
334
405
  const limitRaw = resolveFlag(input.argv, '--limit');
335
406
  const flags = await input.client.listFlags(limitRaw !== undefined ? { limit: Number(limitRaw) } : undefined);
336
- if (input.json) {
337
- input.reporter.raw(`${JSON.stringify({ ok: true, flags }, null, 2)}\n`);
338
- }
339
- else if (flags.length === 0) {
340
- input.reporter.raw('No flags defined.\n');
341
- }
342
- else {
407
+ succeed(input.reporter, { flags }, () => {
408
+ if (flags.length === 0) {
409
+ input.reporter.raw('No flags defined.\n');
410
+ return;
411
+ }
343
412
  for (const flag of flags) {
344
413
  const label = flag.label ? ` — ${flag.label}` : '';
345
414
  input.reporter.raw(`${flag.key} [${flag.type}, ${flag.state}, v${flag.version}]${label}\n`);
346
415
  }
347
- }
416
+ });
348
417
  return { exitCode: 0 };
349
418
  }
350
419
  export async function runCreate(input) {
420
+ const prompts = input.prompts ?? liveFlagConsentPrompts;
421
+ const yes = input.yes ?? hasExplicitYes(input.argv);
422
+ const machineOutput = input.json || isMachineOutput();
423
+ const interactive = prompts.isInteractive();
351
424
  const file = resolveFlag(input.argv, '--file');
352
425
  if (!file)
353
426
  return fail(input.reporter, input.json, 'Require --file <flag-definition.json> for `flags create`.');
@@ -377,13 +450,25 @@ export async function runCreate(input) {
377
450
  if (decoded._tag === 'Left') {
378
451
  return fail(input.reporter, input.json, `Invalid flag definition: ${String(decoded.left)}`);
379
452
  }
380
- const result = await input.client.defineFlag(decoded.right);
381
- if (input.json) {
382
- input.reporter.raw(`${JSON.stringify({ ok: true, flag: result.flag, syncedSnapshots: result.syncedSnapshots }, null, 2)}\n`);
453
+ if (!machineOutput && interactive) {
454
+ input.reporter.raw(flagDefinitionPlan(decoded.right, { wizard: false }));
383
455
  }
384
- else {
385
- input.reporter.raw(`Defined "${result.flag.key}" [${result.flag.type}, ${result.flag.state}, v${result.flag.version}] — synced ${result.syncedSnapshots} snapshot(s).\n`);
456
+ const consent = await requestMutationConsent({
457
+ yes,
458
+ machineOutput,
459
+ interactive,
460
+ message: `Create or update flag "${decoded.right.key}"?`,
461
+ confirm: prompts.confirm,
462
+ });
463
+ if (consent === 'require-yes') {
464
+ return fail(input.reporter, input.json, FLAG_DEFINITION_CONSENT_ERROR);
386
465
  }
466
+ if (consent === 'cancelled')
467
+ return cancelled(input.reporter);
468
+ const result = await input.client.defineFlag(decoded.right);
469
+ succeed(input.reporter, { flag: result.flag, syncedSnapshots: result.syncedSnapshots }, () => {
470
+ input.reporter.raw(`Defined "${result.flag.key}" [${result.flag.type}, ${result.flag.state}, v${result.flag.version}] — synced ${result.syncedSnapshots} snapshot(s).\n`);
471
+ });
387
472
  return { exitCode: 0 };
388
473
  }
389
474
  /** Dependency-injected action seam used by the bare `hs-x flags` TTY flow. */
@@ -815,22 +900,28 @@ export async function runCreateWizard(input) {
815
900
  const referenceError = validateWizardReferences(decoded.right);
816
901
  if (referenceError)
817
902
  return fail(input.reporter, false, referenceError);
818
- input.reporter.raw(`\nFlag definition preview\n${JSON.stringify(decoded.right, null, 2)}\n\nPercentage rollouts are available through --file only.\n\n`);
819
- if (input.yes) {
820
- definition = decoded.right;
821
- break;
822
- }
823
- const confirmed = await prompts.confirm({
903
+ input.reporter.raw(flagDefinitionPlan(decoded.right, { wizard: true }));
904
+ let requestedBack = false;
905
+ const consent = await requestMutationConsent({
906
+ yes: input.yes ?? false,
907
+ machineOutput: false,
908
+ interactive: true,
824
909
  message: `Create or update flag "${decoded.right.key}"?`,
825
- default: false,
826
- allowBack: true,
910
+ confirm: async (options) => {
911
+ const answer = await prompts.confirm({ ...options, allowBack: true });
912
+ if (answer === BACK) {
913
+ requestedBack = true;
914
+ return undefined;
915
+ }
916
+ return answer;
917
+ },
827
918
  });
828
- if (confirmed === undefined || confirmed === false)
829
- return cancelled(input.reporter);
830
- if (confirmed === true) {
919
+ if (consent === 'proceed') {
831
920
  definition = decoded.right;
832
921
  break;
833
922
  }
923
+ if (!requestedBack)
924
+ return cancelled(input.reporter);
834
925
  // Esc from the final confirmation returns to the immediately preceding
835
926
  // targeting-rule step without discarding the rest of the draft.
836
927
  const revisedRules = await buildRules(prompts, variations ?? []);
@@ -844,7 +935,13 @@ export async function runCreateWizard(input) {
844
935
  return { exitCode: 0 };
845
936
  }
846
937
  function cancelled(reporter) {
847
- reporter.rawErr('Cancelled.\n');
938
+ if (isMachineReporter(reporter)) {
939
+ reporter.error('HSX_E_INPUT_INVALID', 'Cancelled.');
940
+ reporter.done(undefined, 130);
941
+ }
942
+ else {
943
+ reporter.rawErr('Cancelled.\n');
944
+ }
848
945
  return { exitCode: 130 };
849
946
  }
850
947
  const liveFlagMutationPrompts = {
@@ -854,13 +951,21 @@ const liveFlagMutationPrompts = {
854
951
  };
855
952
  export async function runSetState(input) {
856
953
  const prompts = input.prompts ?? liveFlagMutationPrompts;
954
+ const yes = input.yes ?? hasExplicitYes(input.argv);
955
+ const machineOutput = input.json || isMachineOutput();
956
+ const interactive = prompts.isInteractive();
857
957
  let key = resolveFlag(input.argv, '--key');
858
- const canPrompt = !input.json && !isMachineOutput() && prompts.isInteractive();
958
+ const canPrompt = !machineOutput && interactive;
859
959
  // Deterministic machine/non-interactive validation must not touch the tenant
860
960
  // transport merely to discover that a required mutation key is absent.
861
961
  if (!key && !canPrompt) {
862
962
  return fail(input.reporter, input.json, `Require --key <flagKey> to ${input.state} a flag.`);
863
963
  }
964
+ // Machine and non-interactive mutations require consent before even the
965
+ // read-only flag lookup, so a missing --yes can never reach tenant transport.
966
+ if (key && !yes && (machineOutput || !interactive)) {
967
+ return fail(input.reporter, input.json, flagStateConsentError(input.state));
968
+ }
864
969
  const flags = await input.client.listFlags();
865
970
  const candidates = flags.filter((flag) => flag.state !== 'archived' && flag.state !== input.state);
866
971
  if (!key && canPrompt) {
@@ -892,19 +997,21 @@ export async function runSetState(input) {
892
997
  if (existing.state === input.state) {
893
998
  return fail(input.reporter, input.json, `Flag "${key}" is already ${input.state}.`);
894
999
  }
895
- if (input.state === 'archived' &&
896
- !input.json &&
897
- !input.yes &&
898
- !isMachineOutput() &&
899
- prompts.isInteractive()) {
900
- const confirmed = await prompts.confirm({
901
- message: `Archive flag "${key}"?`,
902
- default: false,
903
- });
904
- if (confirmed !== true) {
905
- input.reporter.rawErr('Cancelled.\n');
906
- return { exitCode: 130 };
907
- }
1000
+ if (!machineOutput && interactive) {
1001
+ input.reporter.raw(`\nFlag state-change plan\nKey: ${key}\nCurrent state: ${existing.state}\nCurrent version: ${existing.version}\nNew state: ${input.state}\nNew version: ${existing.version + 1}\n\n`);
1002
+ }
1003
+ const consent = await requestMutationConsent({
1004
+ yes,
1005
+ machineOutput,
1006
+ interactive,
1007
+ message: `Set flag "${key}" to ${input.state}?`,
1008
+ confirm: prompts.confirm,
1009
+ });
1010
+ if (consent === 'require-yes') {
1011
+ return fail(input.reporter, input.json, flagStateConsentError(input.state));
1012
+ }
1013
+ if (consent === 'cancelled') {
1014
+ return cancelled(input.reporter);
908
1015
  }
909
1016
  const updated = {
910
1017
  ...existing,
@@ -913,12 +1020,9 @@ export async function runSetState(input) {
913
1020
  updatedAt: new Date().toISOString(),
914
1021
  };
915
1022
  const result = await input.client.defineFlag(updated);
916
- if (input.json) {
917
- input.reporter.raw(`${JSON.stringify({ ok: true, key, state: input.state, flag: result.flag }, null, 2)}\n`);
918
- }
919
- else {
1023
+ succeed(input.reporter, { key, state: input.state, flag: result.flag }, () => {
920
1024
  input.reporter.raw(`Flag "${key}" is now ${input.state} (v${result.flag.version}).\n`);
921
- }
1025
+ });
922
1026
  return { exitCode: 0 };
923
1027
  }
924
1028
  // ─── Native @effect/cli command (Phase 4) ───────────────────────────────────
@@ -929,15 +1033,9 @@ export async function runSetState(input) {
929
1033
  // `runList`/`runCreate`/`runSetState` bodies and the `createFlagAuthoringClient`
930
1034
  // transport are reused verbatim from the legacy path.
931
1035
  //
932
- // JSON-shape MISMATCH (flagged, NOT changed): every flags output is a bespoke
933
- // shape — `{ ok, flags }`, `{ ok, flag, syncedSnapshots }`, `{ ok:false, error }`,
934
- // all pretty-printed (2-space) and NOT the `JsonReporter`
935
- // `{schema_version, command, ok, data}` envelope. Errors in human mode print
936
- // `Error: <msg>` to stderr. To preserve the exact bytes the subprocess tests
937
- // assert, the bodies emit through the legacy `fail()` / `reporter.raw` writers
938
- // and the handler fails with a silent `exitWith(1)` so `runHandler` does not
939
- // re-render. TODO(phase-5): reconcile these payloads with the standard envelope
940
- // once the dispatch swap lets the fixtures move in lockstep.
1036
+ // Machine output uses the shared reporter contract: JSON emits one
1037
+ // `{schema_version, command, ok, data|error}` envelope and JSON-stream emits
1038
+ // start/data-or-error/done events. Human output retains the established text.
941
1039
  const projectIdOption = Options.text('project-id').pipe(Options.withAlias('project'), Options.optional);
942
1040
  const appIdOption = Options.integer('app-id').pipe(Options.optional);
943
1041
  const environmentOption = Options.text('environment').pipe(Options.optional);
@@ -958,12 +1056,13 @@ const identityOptions = {
958
1056
  runtimeOrigin: runtimeOriginOption,
959
1057
  cloudflareApiToken: cloudflareApiTokenOption,
960
1058
  };
961
- /** Effect twin of `fail()` for the native path: emit the bespoke output, then fail silently. */
962
- function failEff(json, message) {
1059
+ /** Effect twin of `fail()` for the native path, rendered once before a silent exit. */
1060
+ function failEff(_json, message, code = 'HSX_E_INPUT_INVALID') {
963
1061
  return Effect.gen(function* () {
964
1062
  const reporter = yield* Reporter;
965
- if (json) {
966
- yield* reporter.raw(`${JSON.stringify({ ok: false, error: message })}\n`);
1063
+ if (reporter.mode === 'json' || reporter.mode === 'ndjson') {
1064
+ yield* reporter.error(code, message);
1065
+ yield* reporter.done(undefined, 1);
967
1066
  }
968
1067
  else {
969
1068
  yield* reporter.rawErr(`Error: ${message}\n`);
@@ -974,16 +1073,20 @@ function failEff(json, message) {
974
1073
  function cancelEff() {
975
1074
  return Effect.gen(function* () {
976
1075
  const reporter = yield* Reporter;
977
- yield* reporter.rawErr('Cancelled.\n');
1076
+ if (reporter.mode === 'json' || reporter.mode === 'ndjson') {
1077
+ yield* reporter.error('HSX_E_INPUT_INVALID', 'Cancelled.');
1078
+ yield* reporter.done(undefined, 130);
1079
+ }
1080
+ else {
1081
+ yield* reporter.rawErr('Cancelled.\n');
1082
+ }
978
1083
  return yield* Effect.fail(exitWith(130));
979
1084
  });
980
1085
  }
981
1086
  /**
982
1087
  * Synchronous reporter sink for the shared `runList`/`runCreate`/`runSetState`
983
- * bodies on the native path. These helpers emit verbatim bytes (the bespoke
984
- * `--json`/human shapes) and so route through `reporter.raw`/`rawErr`, the
985
- * sanctioned I/O edge, instead of `process.std*`. Mode is derived solely from
986
- * `--json` so the produced bytes match the legacy path exactly.
1088
+ * bodies on the native path. Machine modes use data/error/done; human mode
1089
+ * retains the established raw text through the sanctioned reporter edge.
987
1090
  */
988
1091
  const syncReporter = Effect.gen(function* () {
989
1092
  const reporter = yield* Reporter;
@@ -1191,16 +1294,15 @@ function resolveClient(opts, config = {}) {
1191
1294
  });
1192
1295
  }
1193
1296
  /**
1194
- * Map a thrown `RuntimeFlagAuthoringError`/`Error` to the legacy bespoke
1195
- * failure. A `CliError` (an already-rendered `failEff`) is re-raised untouched;
1196
- * anything else is rendered like the legacy `try/catch` around the dispatch.
1297
+ * Map a thrown `RuntimeFlagAuthoringError`/`Error` to the standard machine
1298
+ * failure contract while preserving the legacy human message.
1197
1299
  */
1198
1300
  function withClientErrors(json, body) {
1199
1301
  return body.pipe(Effect.catchAll((error) => error instanceof CliError
1200
1302
  ? Effect.fail(error)
1201
1303
  : error instanceof RuntimeFlagAuthoringError
1202
- ? failEff(json, `Tenant Worker rejected the request (${error.status}): ${error.message}`)
1203
- : failEff(json, error instanceof Error ? error.message : String(error))));
1304
+ ? failEff(json, `Tenant Worker rejected the request (${error.status}): ${error.message}`, flagTransportErrorCode(error))
1305
+ : failEff(json, error instanceof Error ? error.message : String(error), flagTransportErrorCode(error))));
1204
1306
  }
1205
1307
  const flagsListCmd = Command.make('list', { ...identityOptions, limit: limitOption }, (opts) => runHandler('flags', { json: opts.json }, Effect.gen(function* () {
1206
1308
  const { client } = yield* resolveClient(opts);
@@ -1225,6 +1327,11 @@ function makeCreateCmd(name) {
1225
1327
  if (!file && !wizardAllowed) {
1226
1328
  return yield* failEff(opts.json, 'Require --file <flag-definition.json> for `flags create` outside an interactive terminal.');
1227
1329
  }
1330
+ const machineOutput = opts.json || isMachineOutput();
1331
+ const interactive = isInteractive();
1332
+ if (file && !opts.yes && (machineOutput || !interactive)) {
1333
+ return yield* failEff(opts.json, FLAG_DEFINITION_CONSENT_ERROR);
1334
+ }
1228
1335
  // A definition file is authoritative and never opens context/wizard
1229
1336
  // pickers. If its scope is ambiguous, report the required identity flag.
1230
1337
  const { client, scope } = yield* resolveClient(opts, { allowPrompt: !file });
@@ -1250,6 +1357,7 @@ function makeCreateCmd(name) {
1250
1357
  client,
1251
1358
  scope,
1252
1359
  reporter,
1360
+ yes: opts.yes,
1253
1361
  }));
1254
1362
  })));
1255
1363
  }
@@ -1258,9 +1366,14 @@ const flagsDefineCmd = makeCreateCmd('define');
1258
1366
  function makeSetStateCmd(name, state) {
1259
1367
  return Command.make(name, { ...identityOptions, key: keyOption, yes: yesOption }, (opts) => runHandler('flags', { json: opts.json }, Effect.gen(function* () {
1260
1368
  const key = Option.getOrUndefined(opts.key);
1261
- if (key === undefined && (isMachineOutput() || !isInteractive())) {
1369
+ if (key === undefined && (opts.json || isMachineOutput() || !isInteractive())) {
1262
1370
  return yield* failEff(opts.json, `Require --key <flagKey> to ${state} a flag.`);
1263
1371
  }
1372
+ if (key !== undefined &&
1373
+ !opts.yes &&
1374
+ (opts.json || isMachineOutput() || !isInteractive())) {
1375
+ return yield* failEff(opts.json, flagStateConsentError(state));
1376
+ }
1264
1377
  const { client } = yield* resolveClient(opts);
1265
1378
  const reporter = yield* syncReporter;
1266
1379
  yield* withClientErrors(opts.json, Effect.tryPromise({
@@ -1321,6 +1434,9 @@ export const flagsCmd = bareFlagsCmd.pipe(Command.withSubcommands([
1321
1434
  */
1322
1435
  function runCreateFromSource(input) {
1323
1436
  return Effect.gen(function* () {
1437
+ const prompts = input.prompts ?? liveFlagConsentPrompts;
1438
+ const machineOutput = input.json || isMachineOutput();
1439
+ const interactive = prompts.isInteractive();
1324
1440
  let parsed;
1325
1441
  try {
1326
1442
  parsed = JSON.parse(input.source);
@@ -1344,16 +1460,28 @@ function runCreateFromSource(input) {
1344
1460
  if (decoded._tag === 'Left') {
1345
1461
  return yield* failEff(input.json, `Invalid flag definition: ${String(decoded.left)}`);
1346
1462
  }
1463
+ if (!machineOutput && interactive) {
1464
+ input.reporter.raw(flagDefinitionPlan(decoded.right, { wizard: false }));
1465
+ }
1466
+ const consent = yield* Effect.promise(() => requestMutationConsent({
1467
+ yes: input.yes,
1468
+ machineOutput,
1469
+ interactive,
1470
+ message: `Create or update flag "${decoded.right.key}"?`,
1471
+ confirm: prompts.confirm,
1472
+ }));
1473
+ if (consent === 'require-yes') {
1474
+ return yield* failEff(input.json, FLAG_DEFINITION_CONSENT_ERROR);
1475
+ }
1476
+ if (consent === 'cancelled')
1477
+ return yield* cancelEff();
1347
1478
  const result = yield* Effect.tryPromise({
1348
1479
  try: () => input.client.defineFlag(decoded.right),
1349
1480
  catch: (cause) => cause,
1350
1481
  });
1351
- if (input.json) {
1352
- input.reporter.raw(`${JSON.stringify({ ok: true, flag: result.flag, syncedSnapshots: result.syncedSnapshots }, null, 2)}\n`);
1353
- }
1354
- else {
1482
+ succeed(input.reporter, { flag: result.flag, syncedSnapshots: result.syncedSnapshots }, () => {
1355
1483
  input.reporter.raw(`Defined "${result.flag.key}" [${result.flag.type}, ${result.flag.state}, v${result.flag.version}] — synced ${result.syncedSnapshots} snapshot(s).\n`);
1356
- }
1484
+ });
1357
1485
  });
1358
1486
  }
1359
1487
  //# sourceMappingURL=flags.js.map