@scalebun/cli 1.10.1 → 1.11.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 (95) hide show
  1. package/lib/commands/ci-init.d.ts.map +1 -1
  2. package/lib/commands/ci-init.js +80 -2
  3. package/lib/commands/ci-init.js.map +1 -1
  4. package/lib/commands/doctor.d.ts.map +1 -1
  5. package/lib/commands/doctor.js +148 -3
  6. package/lib/commands/doctor.js.map +1 -1
  7. package/lib/commands/link.d.ts +16 -0
  8. package/lib/commands/link.d.ts.map +1 -0
  9. package/lib/commands/link.js +164 -0
  10. package/lib/commands/link.js.map +1 -0
  11. package/lib/commands/login.d.ts.map +1 -1
  12. package/lib/commands/login.js +29 -7
  13. package/lib/commands/login.js.map +1 -1
  14. package/lib/commands/ota-bundle.d.ts +15 -2
  15. package/lib/commands/ota-bundle.d.ts.map +1 -1
  16. package/lib/commands/ota-bundle.js +142 -12
  17. package/lib/commands/ota-bundle.js.map +1 -1
  18. package/lib/commands/ota-channels.d.ts.map +1 -1
  19. package/lib/commands/ota-channels.js +213 -21
  20. package/lib/commands/ota-channels.js.map +1 -1
  21. package/lib/commands/ota-inspect.d.ts +3 -0
  22. package/lib/commands/ota-inspect.d.ts.map +1 -0
  23. package/lib/commands/ota-inspect.js +258 -0
  24. package/lib/commands/ota-inspect.js.map +1 -0
  25. package/lib/commands/ota-keys.d.ts +18 -0
  26. package/lib/commands/ota-keys.d.ts.map +1 -1
  27. package/lib/commands/ota-keys.js +149 -4
  28. package/lib/commands/ota-keys.js.map +1 -1
  29. package/lib/commands/ota-promote.d.ts +3 -0
  30. package/lib/commands/ota-promote.d.ts.map +1 -0
  31. package/lib/commands/ota-promote.js +194 -0
  32. package/lib/commands/ota-promote.js.map +1 -0
  33. package/lib/commands/ota-publish.d.ts.map +1 -1
  34. package/lib/commands/ota-publish.js +114 -38
  35. package/lib/commands/ota-publish.js.map +1 -1
  36. package/lib/commands/ota-rollout.d.ts.map +1 -1
  37. package/lib/commands/ota-rollout.js +201 -171
  38. package/lib/commands/ota-rollout.js.map +1 -1
  39. package/lib/commands/quickstart.d.ts.map +1 -1
  40. package/lib/commands/quickstart.js +110 -8
  41. package/lib/commands/quickstart.js.map +1 -1
  42. package/lib/commands/shell.d.ts +28 -0
  43. package/lib/commands/shell.d.ts.map +1 -0
  44. package/lib/commands/shell.js +512 -0
  45. package/lib/commands/shell.js.map +1 -0
  46. package/lib/index.js +140 -4
  47. package/lib/index.js.map +1 -1
  48. package/lib/ui/menu.d.ts +109 -0
  49. package/lib/ui/menu.d.ts.map +1 -0
  50. package/lib/ui/menu.js +314 -0
  51. package/lib/ui/menu.js.map +1 -0
  52. package/lib/utils/bundle.d.ts.map +1 -1
  53. package/lib/utils/bundle.js +24 -3
  54. package/lib/utils/bundle.js.map +1 -1
  55. package/lib/utils/context.d.ts +88 -0
  56. package/lib/utils/context.d.ts.map +1 -0
  57. package/lib/utils/context.js +247 -0
  58. package/lib/utils/context.js.map +1 -0
  59. package/lib/utils/credentials.d.ts +16 -0
  60. package/lib/utils/credentials.d.ts.map +1 -1
  61. package/lib/utils/credentials.js +14 -3
  62. package/lib/utils/credentials.js.map +1 -1
  63. package/lib/utils/guide.d.ts +63 -0
  64. package/lib/utils/guide.d.ts.map +1 -0
  65. package/lib/utils/guide.js +189 -0
  66. package/lib/utils/guide.js.map +1 -0
  67. package/lib/utils/output.d.ts +96 -0
  68. package/lib/utils/output.d.ts.map +1 -0
  69. package/lib/utils/output.js +135 -0
  70. package/lib/utils/output.js.map +1 -0
  71. package/lib/utils/prompt.d.ts +80 -6
  72. package/lib/utils/prompt.d.ts.map +1 -1
  73. package/lib/utils/prompt.js +184 -20
  74. package/lib/utils/prompt.js.map +1 -1
  75. package/lib/utils/provenance.d.ts +30 -0
  76. package/lib/utils/provenance.d.ts.map +1 -0
  77. package/lib/utils/provenance.js +157 -0
  78. package/lib/utils/provenance.js.map +1 -0
  79. package/lib/utils/resolve.d.ts +81 -0
  80. package/lib/utils/resolve.d.ts.map +1 -0
  81. package/lib/utils/resolve.js +139 -0
  82. package/lib/utils/resolve.js.map +1 -0
  83. package/lib/utils/sdkBin.d.ts +18 -0
  84. package/lib/utils/sdkBin.d.ts.map +1 -0
  85. package/lib/utils/sdkBin.js +76 -0
  86. package/lib/utils/sdkBin.js.map +1 -0
  87. package/lib/utils/ui.d.ts +10 -1
  88. package/lib/utils/ui.d.ts.map +1 -1
  89. package/lib/utils/ui.js +18 -57
  90. package/lib/utils/ui.js.map +1 -1
  91. package/lib/utils/validate.d.ts +62 -0
  92. package/lib/utils/validate.d.ts.map +1 -0
  93. package/lib/utils/validate.js +189 -0
  94. package/lib/utils/validate.js.map +1 -0
  95. package/package.json +3 -2
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GROUPS = void 0;
7
+ exports.commandLabel = commandLabel;
8
+ exports.nextStepHint = nextStepHint;
9
+ exports.renderNextStep = renderNextStep;
10
+ exports.renderAllCommands = renderAllCommands;
11
+ exports.renderGuide = renderGuide;
12
+ exports.contextLineText = contextLineText;
13
+ exports.renderContextLine = renderContextLine;
14
+ const chalk_1 = __importDefault(require("chalk"));
15
+ const context_1 = require("./context");
16
+ exports.GROUPS = [
17
+ {
18
+ title: 'SHIP',
19
+ summary: 'get a new bundle in front of devices',
20
+ commands: [
21
+ { argv: ['ota', 'publish'], desc: 'bundle, upload and publish a release' },
22
+ { argv: ['ota', 'promote'], desc: 'promote an uploaded bundle to a channel' },
23
+ { argv: ['ota', 'bundles'], desc: 'list uploaded bundles, newest first' },
24
+ { argv: ['ota', 'bundle', 'build'], desc: 'build a bundle file for manual upload' },
25
+ ],
26
+ },
27
+ {
28
+ title: 'CONTROL',
29
+ summary: 'change or stop what is already shipping',
30
+ commands: [
31
+ { argv: ['ota', 'rollout'], desc: 'set the rollout percentage' },
32
+ { argv: ['ota', 'pause'], desc: 'stop new devices receiving a release' },
33
+ { argv: ['ota', 'resume'], desc: 'resume a paused release' },
34
+ { argv: ['ota', 'rollback'], desc: 'revert a release for every device' },
35
+ ],
36
+ },
37
+ {
38
+ title: 'INSPECT',
39
+ summary: 'see what is live and what needs a decision',
40
+ commands: [
41
+ { argv: ['ota', 'status'], desc: 'what is live, and what needs a decision' },
42
+ { argv: ['ota', 'releases'], desc: 'list releases and their rollout state' },
43
+ { argv: ['apps'], desc: 'list your apps and their ids' },
44
+ ],
45
+ },
46
+ {
47
+ title: 'CONFIGURE',
48
+ summary: 'channels, signing keys, and deletions',
49
+ commands: [
50
+ { argv: ['ota', 'channels'], desc: 'list, create, rename or remove channels' },
51
+ { argv: ['ota', 'keys'], desc: 'list, register or revoke signing keys' },
52
+ { argv: ['ota', 'generate-key-pair'], desc: 'create an ed25519 signing key' },
53
+ { argv: ['ota', 'delete-bundle'], desc: 'delete a bundle permanently' },
54
+ ],
55
+ },
56
+ {
57
+ title: 'SETUP',
58
+ summary: 'sign in, bind the project, wire the native app',
59
+ commands: [
60
+ { argv: ['quickstart'], desc: 'guided first-release walkthrough' },
61
+ { argv: ['login'], desc: 'authenticate with an access token' },
62
+ { argv: ['logout'], desc: 'clear stored credentials' },
63
+ { argv: ['link'], desc: 'bind this project to an app' },
64
+ { argv: ['unlink'], desc: 'remove the app binding' },
65
+ { argv: ['init', 'android'], desc: 'wire the Android app for OTA' },
66
+ { argv: ['init', 'ios'], desc: 'wire the iOS app for OTA' },
67
+ { argv: ['doctor'], desc: 'check the project is OTA-ready' },
68
+ { argv: ['ci', 'init'], desc: 'generate a CI pipeline for publishing' },
69
+ { argv: ['migrate', 'codepush'], desc: 'migrate from Microsoft CodePush' },
70
+ ],
71
+ },
72
+ ];
73
+ /** How a command is written in prose and on the splash. */
74
+ function commandLabel(entry) {
75
+ return `scalebun ${entry.argv.join(' ')}`;
76
+ }
77
+ const cmd = (name, desc, pad = 32) => console.log(` ${chalk_1.default.cyan(name.padEnd(pad))} ${chalk_1.default.dim(desc)}`);
78
+ /**
79
+ * The stage hint as ONE line, for the interactive menu.
80
+ *
81
+ * The printed splash can afford a four-line NEXT block; a menu frame competing
82
+ * with the logo, the status box and five groups cannot. Losing it entirely
83
+ * would be worse — the ladder is what stops a newcomer from picking a command
84
+ * that cannot work yet — so it compresses rather than disappears.
85
+ */
86
+ function nextStepHint(ctx) {
87
+ const line = (lead, command, why) => chalk_1.default.dim(` ${lead} `) + chalk_1.default.green(command) + chalk_1.default.dim(` ${why}`);
88
+ switch (ctx.stage) {
89
+ case 0:
90
+ return line('start here', 'scalebun quickstart', 'sign in, pick an app, wire it, ship an update');
91
+ case 1:
92
+ return line('next', 'scalebun link', 'bind this project to an app');
93
+ case 2: {
94
+ const p = ctx.platforms.find((x) => ctx.wired[x] === false) ?? ctx.platforms[0] ?? 'android';
95
+ return line('next', `scalebun init ${p}`, 'updates install but never load until this is done');
96
+ }
97
+ case 3:
98
+ return line('next', 'scalebun ota publish', 'bundle, upload and ship your first update');
99
+ default:
100
+ return null;
101
+ }
102
+ }
103
+ /** The one thing to do next, given the stage. */
104
+ function renderNextStep(ctx) {
105
+ console.log('');
106
+ switch (ctx.stage) {
107
+ case 0:
108
+ console.log(` ${chalk_1.default.bold.white('START HERE')}`);
109
+ console.log(` ${chalk_1.default.green('scalebun quickstart')} ${chalk_1.default.dim('sign in, pick an app, wire it, ship an update')}`);
110
+ console.log('');
111
+ console.log(chalk_1.default.dim(' One guided flow. Everything else becomes relevant once it finishes.'));
112
+ break;
113
+ case 1:
114
+ console.log(` ${chalk_1.default.bold.white('NEXT')}`);
115
+ console.log(` ${chalk_1.default.green('scalebun link')} ${chalk_1.default.dim('bind this project to an app — then --app-id is never needed')}`);
116
+ console.log('');
117
+ cmd('scalebun quickstart', 'or continue the guided flow', 22);
118
+ cmd('scalebun apps', 'list the apps you can publish to', 22);
119
+ break;
120
+ case 2: {
121
+ const p = ctx.platforms.find((x) => ctx.wired[x] === false) ?? ctx.platforms[0] ?? 'android';
122
+ console.log(` ${chalk_1.default.bold.white('NEXT')}`);
123
+ console.log(` ${chalk_1.default.green('scalebun init ' + p)} ${chalk_1.default.dim('wire the native app for OTA, then rebuild')}`);
124
+ console.log('');
125
+ console.log(chalk_1.default.dim(' Updates install but never load until this is done.'));
126
+ cmd('scalebun doctor', 'check what else this project needs', 22);
127
+ break;
128
+ }
129
+ case 3:
130
+ console.log(` ${chalk_1.default.bold.white('NEXT')}`);
131
+ console.log(` ${chalk_1.default.green('scalebun ota publish')} ${chalk_1.default.dim('bundle, upload and ship your first update')}`);
132
+ console.log('');
133
+ // Pad to 32 — the same column renderAllCommands uses, because the
134
+ // grouped surface now prints directly beneath this block.
135
+ if (!ctx.hasSigningKey) {
136
+ cmd('scalebun ota generate-key-pair', 'sign your bundles (recommended)');
137
+ }
138
+ cmd('scalebun doctor', 'verify the project once more');
139
+ break;
140
+ case 4:
141
+ // No hand-holding at stage 4 — the full surface follows.
142
+ break;
143
+ }
144
+ }
145
+ /** Every command, grouped by task. Printed at stage 2+ and by `help --all`. */
146
+ function renderAllCommands() {
147
+ for (const g of exports.GROUPS) {
148
+ console.log('');
149
+ console.log(` ${chalk_1.default.bold.white(g.title)}`);
150
+ for (const c of g.commands)
151
+ cmd(commandLabel(c), c.desc);
152
+ }
153
+ console.log('');
154
+ console.log(chalk_1.default.dim(` Every flag: ${chalk_1.default.cyan('scalebun <command> --help')}`));
155
+ console.log('');
156
+ }
157
+ /** The stage-appropriate body of the splash. */
158
+ function renderGuide(ctx) {
159
+ renderNextStep(ctx);
160
+ // Stage 2 — linked — is where the map earns its space. Gating the grouped
161
+ // surface on stage 4 (a release already shipped) was too strict: it withheld
162
+ // the command list from exactly the person walking the setup path, who has
163
+ // committed to the tool and now wants to see what it does. Before stage 2
164
+ // there is no app bound, so most of the surface has nothing to act on and
165
+ // printing it buries the one step that does.
166
+ if (ctx.stage >= 2) {
167
+ renderAllCommands();
168
+ return;
169
+ }
170
+ console.log('');
171
+ console.log(chalk_1.default.dim(` All commands: ${chalk_1.default.cyan('scalebun help --all')}`));
172
+ console.log('');
173
+ }
174
+ /** Context line for the splash and the session header. */
175
+ function contextLineText(ctx) {
176
+ const label = (0, context_1.contextLabel)(ctx);
177
+ if (!label)
178
+ return null;
179
+ const wired = ctx.platforms
180
+ .map((p) => `${p} ${ctx.wired[p] ? chalk_1.default.green('wired') : chalk_1.default.yellow('not wired')}`)
181
+ .join(chalk_1.default.dim(' · '));
182
+ return chalk_1.default.dim(' project ') + chalk_1.default.bold(label) + (wired ? chalk_1.default.dim(' · ') + wired : '');
183
+ }
184
+ function renderContextLine(ctx) {
185
+ const line = contextLineText(ctx);
186
+ if (line)
187
+ console.log(line);
188
+ }
189
+ //# sourceMappingURL=guide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guide.js","sourceRoot":"","sources":["../../src/utils/guide.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Output mode and the exit-code contract.
3
+ *
4
+ * ── Why this exists ─────────────────────────────────────────────────────────
5
+ *
6
+ * Every command printed emoji-decorated chalk and exited 1 for everything.
7
+ * A pipeline could not read back a release id, and — worse — could not tell
8
+ * "object storage flaked, retry this" from "the server rejected the promotion,
9
+ * stop and page someone". Both were `exit(1)`.
10
+ *
11
+ * ── The contract ────────────────────────────────────────────────────────────
12
+ *
13
+ * 0 success
14
+ * 1 usage or local error — bad flag, not signed in, no project. Do not retry.
15
+ * 2 server rejected it — a 4xx. The request was understood and refused.
16
+ * 3 a deliberate gate — a check the operator asked for said no
17
+ * (--fail-on, size budget, preflight gate).
18
+ * 4 transient — network, timeout, 5xx. Retrying is reasonable.
19
+ *
20
+ * 3 is the one that earns its place: it lets CI distinguish "your bundle is
21
+ * 2 MB over budget" from "the API is down", which are the same colour of red
22
+ * today and want opposite responses.
23
+ */
24
+ export declare enum ExitCode {
25
+ Ok = 0,
26
+ Usage = 1,
27
+ Rejected = 2,
28
+ Gate = 3,
29
+ Network = 4
30
+ }
31
+ /** Enable machine output. Also set from SCALEBUN_JSON=1 for CI without flags. */
32
+ export declare function setJsonMode(on: boolean): void;
33
+ export declare function isJson(): boolean;
34
+ /**
35
+ * Print a result.
36
+ *
37
+ * `human` is a function rather than a string so the pretty path costs nothing
38
+ * when it will not run — several commands build tables and spinners that are
39
+ * pure waste under --json.
40
+ */
41
+ export declare function emit(human: () => void, data: unknown): void;
42
+ /**
43
+ * A command the user is likely to want next.
44
+ *
45
+ * ── Why these are data and not just printed lines ───────────────────────────
46
+ *
47
+ * Commands already ended with a block like
48
+ *
49
+ * Watch it: scalebun ota status
50
+ * Widen it: scalebun ota rollout --percent 100
51
+ * Undo it: scalebun ota rollback
52
+ *
53
+ * which is genuinely the right advice at exactly the right moment — and then
54
+ * asks the reader to retype it. Inside the interactive session that is
55
+ * absurd: the session already knows how to run commands, it just could not
56
+ * see these because they were prose.
57
+ *
58
+ * Recording them alongside the printed text lets the session offer them as a
59
+ * choice, while the plain CLI keeps printing exactly what it printed before.
60
+ * One declaration, so the list a person reads and the list they can select
61
+ * from cannot disagree.
62
+ */
63
+ export interface NextStep {
64
+ /** Why you would run it: "Watch it", "Undo it". */
65
+ label: string;
66
+ /** Argv, so the session can run it without parsing prose. */
67
+ argv: string[];
68
+ }
69
+ /**
70
+ * Print the follow-up block AND record it for the session.
71
+ *
72
+ * Silent under --json: the steps belong in the JSON body if a pipeline wants
73
+ * them, not as prose interleaved with a document it is trying to parse.
74
+ */
75
+ export declare function printNextSteps(steps: NextStep[], chalkish?: (s: string) => string): void;
76
+ /** Drain the recorded steps. The session calls this after each command. */
77
+ export declare function takeNextSteps(): NextStep[];
78
+ /**
79
+ * Fail with a typed exit code.
80
+ *
81
+ * Under --json the error is a JSON object on stdout as well, because a
82
+ * pipeline that has to parse stderr to find out what went wrong is not
83
+ * machine-readable in any useful sense.
84
+ */
85
+ export declare function fail(message: string, details?: string[], code?: ExitCode): never;
86
+ /**
87
+ * Map a thrown API error onto an exit code.
88
+ *
89
+ * ApiClient formats errors as `API error (HTTP 422): …`, so the status is
90
+ * recoverable from the message without changing every call site. 4xx is the
91
+ * server understanding and refusing; 5xx and socket errors are worth a retry.
92
+ */
93
+ export declare function codeForError(err: unknown): ExitCode;
94
+ /** Convenience: fail with the code implied by a thrown API error. */
95
+ export declare function failFromError(prefix: string, err: unknown, details?: string[]): never;
96
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,oBAAY,QAAQ;IAClB,EAAE,IAAI;IACN,KAAK,IAAI;IACT,QAAQ,IAAI;IACZ,IAAI,IAAI;IACR,OAAO,IAAI;CACZ;AAID,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAE7C;AAED,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAM3D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,QAAQ;IACvB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAUxF;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAI1C;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAAO,EAAE,IAAI,GAAE,QAAyB,GAAG,KAAK,CAUpG;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,CAYnD;AAED,qEAAqE;AACrE,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,KAAK,CAGzF"}
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ /**
3
+ * Output mode and the exit-code contract.
4
+ *
5
+ * ── Why this exists ─────────────────────────────────────────────────────────
6
+ *
7
+ * Every command printed emoji-decorated chalk and exited 1 for everything.
8
+ * A pipeline could not read back a release id, and — worse — could not tell
9
+ * "object storage flaked, retry this" from "the server rejected the promotion,
10
+ * stop and page someone". Both were `exit(1)`.
11
+ *
12
+ * ── The contract ────────────────────────────────────────────────────────────
13
+ *
14
+ * 0 success
15
+ * 1 usage or local error — bad flag, not signed in, no project. Do not retry.
16
+ * 2 server rejected it — a 4xx. The request was understood and refused.
17
+ * 3 a deliberate gate — a check the operator asked for said no
18
+ * (--fail-on, size budget, preflight gate).
19
+ * 4 transient — network, timeout, 5xx. Retrying is reasonable.
20
+ *
21
+ * 3 is the one that earns its place: it lets CI distinguish "your bundle is
22
+ * 2 MB over budget" from "the API is down", which are the same colour of red
23
+ * today and want opposite responses.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.ExitCode = void 0;
27
+ exports.setJsonMode = setJsonMode;
28
+ exports.isJson = isJson;
29
+ exports.emit = emit;
30
+ exports.printNextSteps = printNextSteps;
31
+ exports.takeNextSteps = takeNextSteps;
32
+ exports.fail = fail;
33
+ exports.codeForError = codeForError;
34
+ exports.failFromError = failFromError;
35
+ var ExitCode;
36
+ (function (ExitCode) {
37
+ ExitCode[ExitCode["Ok"] = 0] = "Ok";
38
+ ExitCode[ExitCode["Usage"] = 1] = "Usage";
39
+ ExitCode[ExitCode["Rejected"] = 2] = "Rejected";
40
+ ExitCode[ExitCode["Gate"] = 3] = "Gate";
41
+ ExitCode[ExitCode["Network"] = 4] = "Network";
42
+ })(ExitCode || (exports.ExitCode = ExitCode = {}));
43
+ let jsonMode = false;
44
+ /** Enable machine output. Also set from SCALEBUN_JSON=1 for CI without flags. */
45
+ function setJsonMode(on) {
46
+ jsonMode = on;
47
+ }
48
+ function isJson() {
49
+ return jsonMode || process.env.SCALEBUN_JSON === '1';
50
+ }
51
+ /**
52
+ * Print a result.
53
+ *
54
+ * `human` is a function rather than a string so the pretty path costs nothing
55
+ * when it will not run — several commands build tables and spinners that are
56
+ * pure waste under --json.
57
+ */
58
+ function emit(human, data) {
59
+ if (isJson()) {
60
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
61
+ return;
62
+ }
63
+ human();
64
+ }
65
+ let nextSteps = [];
66
+ /**
67
+ * Print the follow-up block AND record it for the session.
68
+ *
69
+ * Silent under --json: the steps belong in the JSON body if a pipeline wants
70
+ * them, not as prose interleaved with a document it is trying to parse.
71
+ */
72
+ function printNextSteps(steps, chalkish) {
73
+ nextSteps = steps;
74
+ if (isJson() || !steps.length)
75
+ return;
76
+ const width = steps.reduce((w, s) => Math.max(w, s.label.length), 0);
77
+ console.log('');
78
+ for (const s of steps) {
79
+ const cmd = `scalebun ${s.argv.join(' ')}`;
80
+ console.log(` ${(s.label + ':').padEnd(width + 2)} ${chalkish ? chalkish(cmd) : cmd}`);
81
+ }
82
+ console.log('');
83
+ }
84
+ /** Drain the recorded steps. The session calls this after each command. */
85
+ function takeNextSteps() {
86
+ const out = nextSteps;
87
+ nextSteps = [];
88
+ return out;
89
+ }
90
+ /**
91
+ * Fail with a typed exit code.
92
+ *
93
+ * Under --json the error is a JSON object on stdout as well, because a
94
+ * pipeline that has to parse stderr to find out what went wrong is not
95
+ * machine-readable in any useful sense.
96
+ */
97
+ function fail(message, details = [], code = ExitCode.Usage) {
98
+ if (isJson()) {
99
+ process.stdout.write(JSON.stringify({ ok: false, error: message, details, exitCode: code }, null, 2) + '\n');
100
+ }
101
+ else {
102
+ console.error(`❌ ${message}`);
103
+ for (const d of details)
104
+ console.error(` ${d}`);
105
+ }
106
+ process.exit(code);
107
+ }
108
+ /**
109
+ * Map a thrown API error onto an exit code.
110
+ *
111
+ * ApiClient formats errors as `API error (HTTP 422): …`, so the status is
112
+ * recoverable from the message without changing every call site. 4xx is the
113
+ * server understanding and refusing; 5xx and socket errors are worth a retry.
114
+ */
115
+ function codeForError(err) {
116
+ const msg = err instanceof Error ? err.message : String(err);
117
+ const m = /HTTP (\d{3})/.exec(msg);
118
+ if (m) {
119
+ const status = Number(m[1]);
120
+ if (status >= 500)
121
+ return ExitCode.Network;
122
+ if (status >= 400)
123
+ return ExitCode.Rejected;
124
+ }
125
+ if (/ETIMEDOUT|ECONNRESET|ENOTFOUND|EAI_AGAIN|socket hang up|network/i.test(msg)) {
126
+ return ExitCode.Network;
127
+ }
128
+ return ExitCode.Usage;
129
+ }
130
+ /** Convenience: fail with the code implied by a thrown API error. */
131
+ function failFromError(prefix, err, details = []) {
132
+ const msg = err instanceof Error ? err.message : String(err);
133
+ return fail(`${prefix}: ${msg}`, details, codeForError(err));
134
+ }
135
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
@@ -3,14 +3,88 @@ export declare function canPromptHidden(): boolean;
3
3
  export declare function promptHidden(query: string): Promise<string>;
4
4
  /** Close the shared prompt interface so the process can exit / move on. */
5
5
  export declare function closePrompts(): void;
6
- /** Free-text question with an optional default (shown in parentheses). */
7
- export declare function text(query: string, opts?: {
6
+ export interface TextOpts {
8
7
  default?: string;
9
- }): Promise<string>;
10
- /** Yes/no question. Empty input takes the default. */
11
- export declare function confirm(query: string, opts?: {
12
- default?: boolean;
8
+ /** Reject the answer and re-ask; return the reason, or null to accept. */
9
+ validate?: (value: string) => string | null;
10
+ /**
11
+ * Accept an empty answer and return ''. Only for genuinely optional fields —
12
+ * the release note is the one that means it.
13
+ */
14
+ allowEmpty?: boolean;
15
+ /** One line explaining what a good answer looks like, shown when re-asking. */
16
+ help?: string;
17
+ }
18
+ /**
19
+ * Free-text question with an optional default (shown in parentheses).
20
+ *
21
+ * ── Empty is a question, not an answer ──────────────────────────────────────
22
+ *
23
+ * This used to return '' the moment someone pressed Enter with nothing typed,
24
+ * and every caller then invented its own reaction. `text('App id')` in the
25
+ * publish wizard did `if (!id) process.exit(1)` — a silent exit 1, no message,
26
+ * from a prompt that had just told you it wanted an app id. Others carried the
27
+ * empty string onward until the API rejected a field the user never saw.
28
+ *
29
+ * Pressing Enter at a prompt with no default is almost never a decision; it is
30
+ * someone hurrying, or not knowing what is wanted. So empty now re-asks and
31
+ * explains, and callers with something sensible to fall back on pass a
32
+ * `default` — which is what "Enter means yes" should always have looked like.
33
+ * `allowEmpty` remains for fields that are honestly optional.
34
+ */
35
+ export declare function text(query: string, opts?: TextOpts): Promise<string>;
36
+ /**
37
+ * Type-this-exact-phrase confirmation, for the destructive commands.
38
+ *
39
+ * Empty and wrong are DIFFERENT mistakes and deserve different answers:
40
+ *
41
+ * empty — cancel. Enter must stay an escape from a prompt that is about to
42
+ * delete a channel or roll back a fleet; re-asking would trap
43
+ * someone whose actual intent was to back out. But say so, rather
44
+ * than printing "Cancelled" and leaving them to guess whether the
45
+ * keystroke registered.
46
+ * wrong — re-ask. A typo in a channel name is not a decision to cancel, and
47
+ * making the operator re-run the whole command mid-incident to fix
48
+ * one character is the worst moment to be pedantic.
49
+ */
50
+ export declare function confirmPhrase(query: string, expected: string, opts?: {
51
+ caseInsensitive?: boolean;
13
52
  }): Promise<boolean>;
53
+ export interface ConfirmOpts {
54
+ /**
55
+ * What Enter means. OMIT IT to require an explicit y or n.
56
+ *
57
+ * Omit it for the decision that commits a flow. `(y/N)` promises that Enter
58
+ * is a safe no, and for "Configure advanced options?" that promise is worth
59
+ * keeping. But at the end of a wizard the user has just chosen a bundle and
60
+ * a rollout percentage, and one stray Enter throws all of it away with
61
+ * "Cancelled — nothing was promoted". Safe, and infuriating: the work is
62
+ * gone and nothing says which keystroke did it.
63
+ */
64
+ default?: boolean;
65
+ /** Extra guidance shown when the answer is a number. */
66
+ numericHint?: string;
67
+ }
68
+ /**
69
+ * Yes/no question. Empty input takes the default; anything unrecognised
70
+ * RE-ASKS.
71
+ *
72
+ * ── Why it re-asks ──────────────────────────────────────────────────────────
73
+ *
74
+ * This used to be `return answer === 'y' || answer === 'yes'`, so every input
75
+ * that was not literally yes counted as no. Typing `50` at
76
+ * "Promote v14 to 100% of devices? (y/N)" — a completely reasonable thing to
77
+ * type when you wanted 50% — silently meant "no", and the CLI reported
78
+ * "Cancelled — nothing was promoted" as though the user had declined. The
79
+ * person did not decline. They answered a different question, and the tool
80
+ * discarded the answer and blamed them for it.
81
+ *
82
+ * The same swallow hid `yes please`, `Y ` with a stray space, and a mistyped
83
+ * `t`. Silently reading all of those as "no" is safe for the fleet and awful
84
+ * for the human: the destructive path is protected while the intent is lost
85
+ * without a word.
86
+ */
87
+ export declare function confirm(query: string, opts?: ConfirmOpts): Promise<boolean>;
14
88
  /** Integer question with optional default and min/max validation (loops until valid). */
15
89
  export declare function number(query: string, opts?: {
16
90
  default?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"AA4BA,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiD3D;AAyBD,2EAA2E;AAC3E,wBAAgB,YAAY,IAAI,IAAI,CAKnC;AAmBD,0EAA0E;AAC1E,wBAAsB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1F;AAED,sDAAsD;AACtD,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/F;AAED,yFAAyF;AACzF,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1D,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,CAAC,EAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EACpB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9B,OAAO,CAAC,CAAC,CAAC,CAKZ"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/utils/prompt.ts"],"names":[],"mappings":"AA4BA,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiD3D;AAiCD,2EAA2E;AAC3E,wBAAgB,YAAY,IAAI,IAAI,CAKnC;AAmBD,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC5C;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB9E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,OAAO,CAAC,CAclB;AA2BD,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCrF;AAED,yFAAyF;AACzF,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1D,OAAO,CAAC,MAAM,CAAC,CA4CjB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAAC,CAAC,EAC5B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EACpB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9B,OAAO,CAAC,CAAC,CAAC,CAKZ"}