@amerged/ohmyhost-mcp 0.1.10

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 (72) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +11 -0
  4. package/THIRD_PARTY_NOTICES.md +474 -0
  5. package/dist/chunk-7MV2EVXV.js +30779 -0
  6. package/dist/index.js +9 -0
  7. package/dist/stdio-main.js +5607 -0
  8. package/package.json +32 -0
  9. package/src/apps/mcp/index.ts +30 -0
  10. package/src/apps/mcp/local-client.ts +891 -0
  11. package/src/apps/mcp/local-server.ts +1512 -0
  12. package/src/apps/mcp/stdio-main.ts +26 -0
  13. package/src/apps/product-cli/bin.ts +24 -0
  14. package/src/apps/product-cli/cli.ts +2271 -0
  15. package/src/apps/product-cli/command.ts +1811 -0
  16. package/src/apps/product-cli/composition.ts +823 -0
  17. package/src/apps/product-cli/credential-store.ts +149 -0
  18. package/src/apps/product-cli/device-token-expiry.ts +89 -0
  19. package/src/apps/product-cli/file-credential-store.ts +166 -0
  20. package/src/apps/product-cli/index.ts +23 -0
  21. package/src/apps/product-cli/product-api.ts +1082 -0
  22. package/src/apps/product-cli/project-link-store.ts +30 -0
  23. package/src/apps/product-cli/psql-session.ts +29 -0
  24. package/src/apps/product-cli/repository-init.ts +1622 -0
  25. package/src/apps/product-cli/runtime-contract.ts +1747 -0
  26. package/src/apps/product-cli/user-token-file.ts +214 -0
  27. package/src/apps/product-cli/workspace.ts +92 -0
  28. package/src/packages/agent-skills/generated-skill-resources.ts +147 -0
  29. package/src/packages/agent-skills/index.ts +37 -0
  30. package/src/packages/contracts/application-root.ts +80 -0
  31. package/src/packages/contracts/billing.ts +172 -0
  32. package/src/packages/contracts/credit-pricing.ts +214 -0
  33. package/src/packages/contracts/database-access.ts +257 -0
  34. package/src/packages/contracts/database-compute.ts +72 -0
  35. package/src/packages/contracts/database-migration-admission.ts +235 -0
  36. package/src/packages/contracts/feedback.ts +96 -0
  37. package/src/packages/contracts/framework-admission.ts +280 -0
  38. package/src/packages/contracts/framework-family.ts +18 -0
  39. package/src/packages/contracts/functions.ts +76 -0
  40. package/src/packages/contracts/index.ts +245 -0
  41. package/src/packages/contracts/operation-deployment-result.ts +165 -0
  42. package/src/packages/contracts/operation-failure.ts +150 -0
  43. package/src/packages/contracts/platform-origins.ts +5 -0
  44. package/src/packages/contracts/project-context.ts +59 -0
  45. package/src/packages/contracts/project-exports.ts +102 -0
  46. package/src/packages/contracts/user-api-keys.ts +98 -0
  47. package/src/packages/sdk-ts/generated/client/client.gen.ts +277 -0
  48. package/src/packages/sdk-ts/generated/client/index.ts +27 -0
  49. package/src/packages/sdk-ts/generated/client/types.gen.ts +217 -0
  50. package/src/packages/sdk-ts/generated/client/utils.gen.ts +316 -0
  51. package/src/packages/sdk-ts/generated/client.gen.ts +24 -0
  52. package/src/packages/sdk-ts/generated/core/auth.gen.ts +48 -0
  53. package/src/packages/sdk-ts/generated/core/bodySerializer.gen.ts +82 -0
  54. package/src/packages/sdk-ts/generated/core/params.gen.ts +178 -0
  55. package/src/packages/sdk-ts/generated/core/pathSerializer.gen.ts +171 -0
  56. package/src/packages/sdk-ts/generated/core/queryKeySerializer.gen.ts +117 -0
  57. package/src/packages/sdk-ts/generated/core/serverSentEvents.gen.ts +242 -0
  58. package/src/packages/sdk-ts/generated/core/types.gen.ts +114 -0
  59. package/src/packages/sdk-ts/generated/core/utils.gen.ts +140 -0
  60. package/src/packages/sdk-ts/generated/index.ts +578 -0
  61. package/src/packages/sdk-ts/generated/sdk.gen.ts +3440 -0
  62. package/src/packages/sdk-ts/generated/types.gen.ts +5790 -0
  63. package/src/packages/sdk-ts/index.ts +7 -0
  64. package/src/packages/workos-auth-contracts/device-flow.ts +271 -0
  65. package/src/packages/workos-auth-contracts/index.ts +8 -0
  66. package/src/packages/workos-auth-contracts/management-rpc.ts +379 -0
  67. package/src/packages/workos-auth-contracts/organizations.ts +113 -0
  68. package/src/packages/workos-auth-contracts/parsing.ts +75 -0
  69. package/src/packages/workos-auth-contracts/session-status.ts +53 -0
  70. package/src/packages/workos-auth-contracts/tokens.ts +216 -0
  71. package/src/packages/workos-auth-contracts/user-api-keys.ts +85 -0
  72. package/src/packages/workos-auth-contracts/webhooks.ts +127 -0
@@ -0,0 +1,1811 @@
1
+ import { USER_API_KEY_ID } from "@ohmyhost/contracts/user-api-keys";
2
+ export type ProductCliCommand =
3
+ | {
4
+ readonly kind: "github-connect";
5
+ readonly organizationId: string;
6
+ readonly idempotencyKey: string;
7
+ readonly credentialStore: "native";
8
+ }
9
+ | {
10
+ readonly kind: "github-status";
11
+ readonly organizationId: string;
12
+ readonly credentialStore: "native";
13
+ }
14
+ | {
15
+ readonly kind: "database-compute-set";
16
+ readonly projectId: string;
17
+ readonly environment: "dev" | "prod";
18
+ readonly profile: "standard" | "performance";
19
+ readonly idempotencyKey: string;
20
+ readonly wait: boolean;
21
+ readonly credentialStore: "native";
22
+ }
23
+ | {
24
+ readonly kind: "database-compute-get";
25
+ readonly projectId: string;
26
+ readonly environment: "dev" | "prod";
27
+ readonly credentialStore: "native";
28
+ }
29
+ | {
30
+ readonly kind: "project-context";
31
+ readonly projectId: string;
32
+ readonly credentialStore: "native";
33
+ }
34
+ | {
35
+ readonly kind: "project-notes-set";
36
+ readonly projectId: string;
37
+ readonly markdown: string;
38
+ readonly expectedVersion: number;
39
+ readonly idempotencyKey: string;
40
+ readonly credentialStore: "native";
41
+ }
42
+ | {
43
+ readonly kind: "export-create";
44
+ readonly projectId: string;
45
+ readonly idempotencyKey: string;
46
+ readonly stdin: true;
47
+ readonly credentialStore: "native";
48
+ }
49
+ | {
50
+ readonly kind: "export-get";
51
+ readonly projectId: string;
52
+ readonly exportId: string;
53
+ readonly credentialStore: "native";
54
+ }
55
+ | {
56
+ readonly kind: "token-create";
57
+ readonly organizationId: string;
58
+ readonly name: string;
59
+ readonly idempotencyKey: string;
60
+ readonly outputPath: string;
61
+ readonly credentialStore: "native";
62
+ }
63
+ | {
64
+ readonly kind: "token-list";
65
+ readonly organizationId: string;
66
+ readonly after?: string;
67
+ readonly credentialStore: "native";
68
+ }
69
+ | {
70
+ readonly kind: "token-revoke";
71
+ readonly organizationId: string;
72
+ readonly keyId: string;
73
+ readonly credentialStore: "native";
74
+ }
75
+ | {
76
+ readonly kind: "feedback-submit";
77
+ readonly report: import("@ohmyhost/sdk-ts").FeedbackSubmission;
78
+ readonly idempotencyKey: string;
79
+ readonly credentialStore: "native";
80
+ }
81
+ | { kind: "billing-recharge-get"; organizationId: string; credentialStore: "native" }
82
+ | ({ kind: "billing-recharge-set"; credentialStore: "native" } & {
83
+ organizationId: string;
84
+ enabled: boolean;
85
+ monthlyLimitMinor: number;
86
+ consent: "off_session_v1" | null;
87
+ revision: number;
88
+ idempotencyKey: string;
89
+ })
90
+ | {
91
+ readonly kind: "billing-checkout";
92
+ readonly organizationId: string;
93
+ readonly offer: "topup" | "paid";
94
+ readonly packs: number;
95
+ readonly idempotencyKey: string;
96
+ readonly credentialStore: "native";
97
+ }
98
+ | {
99
+ readonly kind: "billing-status";
100
+ readonly organizationId: string;
101
+ readonly checkoutId: string;
102
+ readonly credentialStore: "native";
103
+ }
104
+ | {
105
+ readonly kind: "billing-portal";
106
+ readonly organizationId: string;
107
+ readonly credentialStore: "native";
108
+ }
109
+ | {
110
+ readonly kind: "credits-usage";
111
+ readonly organizationId: string;
112
+ readonly month: string;
113
+ readonly cursor?: string;
114
+ readonly credentialStore: "native";
115
+ }
116
+ | {
117
+ readonly kind: "credits-account";
118
+ readonly organizationId: string;
119
+ readonly credentialStore: "native";
120
+ }
121
+ | {
122
+ readonly kind: "credits-balance";
123
+ readonly organizationId: string;
124
+ readonly credentialStore: "native";
125
+ }
126
+ | { readonly kind: "budget-get"; readonly projectId: string; readonly credentialStore: "native" }
127
+ | {
128
+ readonly kind: "budget-set";
129
+ readonly projectId: string;
130
+ readonly amountMicros: string | null;
131
+ readonly mode: "continue" | "stop";
132
+ readonly idempotencyKey: string;
133
+ readonly credentialStore: "native";
134
+ }
135
+ | { readonly kind: "help"; readonly topic?: string }
136
+ | { readonly kind: "version" }
137
+ | { readonly kind: "whoami"; readonly credentialStore: "native" }
138
+ | {
139
+ readonly kind: "organization-create";
140
+ readonly signupSource?: string;
141
+ readonly name: string;
142
+ readonly idempotencyKey: string;
143
+ readonly credentialStore: "native";
144
+ }
145
+ | { readonly kind: "organization-list"; readonly credentialStore: "native" }
146
+ | {
147
+ readonly kind: "organization-use";
148
+ readonly organizationId: string;
149
+ readonly credentialStore: "native";
150
+ }
151
+ | {
152
+ readonly kind: "init";
153
+ readonly directory: string;
154
+ readonly project?: string;
155
+ readonly root?: string;
156
+ /** The hosting region of the project the repository deploys to; `us` when unknown. */
157
+ readonly region?: "us" | "eu";
158
+ readonly dryRun: boolean;
159
+ }
160
+ | {
161
+ readonly kind: "login";
162
+ readonly organizationId?: string;
163
+ readonly credentialStore: "native";
164
+ }
165
+ | {
166
+ readonly kind: "logout";
167
+ readonly revoke: boolean;
168
+ readonly credentialStore: "native";
169
+ }
170
+ | {
171
+ readonly kind: "operation-get";
172
+ readonly operationId: string;
173
+ readonly credentialStore: "native";
174
+ }
175
+ | {
176
+ readonly kind: "operation-reconcile";
177
+ readonly operationId: string;
178
+ readonly idempotencyKey: string;
179
+ readonly yes: true;
180
+ readonly credentialStore: "native";
181
+ }
182
+ | {
183
+ readonly kind: "project-create";
184
+ readonly organizationId: string;
185
+ readonly name: string;
186
+ readonly dataMode?: "shared" | "isolated";
187
+ readonly region?: "us" | "eu";
188
+ readonly idempotencyKey: string;
189
+ readonly credentialStore: "native";
190
+ }
191
+ | {
192
+ readonly kind: "project-list";
193
+ readonly cursor?: string;
194
+ readonly limit: number;
195
+ readonly credentialStore: "native";
196
+ }
197
+ | {
198
+ readonly kind: "project-status";
199
+ readonly projectId: string;
200
+ readonly credentialStore: "native";
201
+ }
202
+ | {
203
+ readonly kind: "project-dev-access-create";
204
+ readonly projectId: string;
205
+ readonly credentialStore: "native";
206
+ }
207
+ | {
208
+ readonly kind: "database-write";
209
+ readonly projectId: string;
210
+ readonly environment: "dev" | "prod";
211
+ readonly statementFile: string;
212
+ readonly parameters: readonly import("@ohmyhost/contracts/database-access").DatabaseJson[];
213
+ readonly idempotencyKey: string;
214
+ readonly yes: true;
215
+ readonly credentialStore: "native";
216
+ }
217
+ | {
218
+ readonly kind: "database-query";
219
+ readonly environment: "dev" | "prod";
220
+ readonly projectId: string;
221
+ readonly statement: string;
222
+ readonly parameters: readonly (string | number | boolean | null)[];
223
+ readonly credentialStore: "native";
224
+ }
225
+ | {
226
+ readonly kind: "database-access-create";
227
+ readonly projectId: string;
228
+ readonly environment: "dev" | "prod";
229
+ readonly mode: "read" | "write";
230
+ readonly ttlSeconds: number;
231
+ readonly label: string | null;
232
+ readonly yes: true;
233
+ readonly credentialStore: "native";
234
+ }
235
+ | {
236
+ readonly kind: "database-access-list";
237
+ readonly projectId: string;
238
+ readonly environment?: "dev" | "prod";
239
+ readonly credentialStore: "native";
240
+ }
241
+ | {
242
+ readonly kind: "database-access-revoke";
243
+ readonly projectId: string;
244
+ readonly accessId: string;
245
+ readonly yes: true;
246
+ readonly credentialStore: "native";
247
+ }
248
+ | {
249
+ readonly kind: "database-psql";
250
+ readonly projectId: string;
251
+ readonly environment: "dev" | "prod";
252
+ readonly mode: "read" | "write";
253
+ readonly ttlSeconds: number;
254
+ readonly credentialStore: "native";
255
+ }
256
+ | {
257
+ readonly kind: "link";
258
+ readonly projectId: string;
259
+ readonly repositoryOwner: string;
260
+ readonly repositoryName: string;
261
+ readonly idempotencyKey: string;
262
+ readonly credentialStore: "native";
263
+ }
264
+ | {
265
+ readonly kind: "source-auto-deploy-set";
266
+ readonly projectId: string;
267
+ readonly branch: string;
268
+ readonly enabled: boolean;
269
+ readonly idempotencyKey: string;
270
+ readonly credentialStore: "native";
271
+ }
272
+ | {
273
+ readonly kind: "source-auto-deploy-status";
274
+ readonly projectId: string;
275
+ readonly credentialStore: "native";
276
+ }
277
+ | {
278
+ readonly kind: "plan";
279
+ readonly projectId: string;
280
+ readonly commitSha: string;
281
+ readonly credentialStore: "native";
282
+ }
283
+ | {
284
+ readonly kind: "deploy";
285
+ readonly projectId: string;
286
+ readonly planId: string;
287
+ readonly idempotencyKey: string;
288
+ readonly yes: true;
289
+ readonly wait: boolean;
290
+ readonly credentialStore: "native";
291
+ }
292
+ | {
293
+ readonly kind: "logs";
294
+ readonly operationId: string;
295
+ readonly follow: true;
296
+ readonly credentialStore: "native";
297
+ }
298
+ | {
299
+ readonly kind: "deployment-logs";
300
+ readonly projectId: string;
301
+ readonly deploymentId: string;
302
+ readonly follow: true;
303
+ readonly credentialStore: "native";
304
+ }
305
+ | {
306
+ readonly kind: "rollback-plan";
307
+ readonly projectId: string;
308
+ readonly deploymentId: string;
309
+ readonly credentialStore: "native";
310
+ }
311
+ | {
312
+ readonly kind: "rollback";
313
+ readonly projectId: string;
314
+ readonly deploymentId: string;
315
+ readonly ifMatch: string;
316
+ readonly confirmationToken: string;
317
+ readonly idempotencyKey: string;
318
+ readonly yes: true;
319
+ readonly credentialStore: "native";
320
+ }
321
+ | {
322
+ readonly kind: "promotion-plan";
323
+ readonly projectId: string;
324
+ readonly sourceDeploymentId: string;
325
+ readonly credentialStore: "native";
326
+ }
327
+ | {
328
+ readonly kind: "promote";
329
+ readonly projectId: string;
330
+ readonly sourceDeploymentId: string;
331
+ readonly ifMatch: string;
332
+ readonly confirmationToken: string;
333
+ readonly idempotencyKey: string;
334
+ readonly yes: true;
335
+ readonly wait: boolean;
336
+ readonly credentialStore: "native";
337
+ }
338
+ | {
339
+ readonly kind: "delete-plan";
340
+ readonly projectId: string;
341
+ readonly credentialStore: "native";
342
+ }
343
+ | {
344
+ readonly kind: "delete";
345
+ readonly projectId: string;
346
+ readonly ifMatch: string;
347
+ readonly confirmationToken: string;
348
+ readonly idempotencyKey: string;
349
+ readonly yes: true;
350
+ readonly credentialStore: "native";
351
+ }
352
+ | {
353
+ readonly kind: "secret-list";
354
+ readonly projectId: string;
355
+ readonly environmentId: string;
356
+ readonly credentialStore: "native";
357
+ }
358
+ | {
359
+ readonly kind: "function-runs";
360
+ readonly projectId: string;
361
+ readonly environmentId: string;
362
+ readonly limit: number;
363
+ readonly credentialStore: "native";
364
+ }
365
+ | {
366
+ readonly kind: "secret-set";
367
+ readonly projectId: string;
368
+ readonly environmentId: string;
369
+ readonly name: string;
370
+ readonly idempotencyKey: string;
371
+ readonly stdin: true;
372
+ readonly wait: boolean;
373
+ readonly credentialStore: "native";
374
+ }
375
+ | {
376
+ readonly kind: "secret-delete";
377
+ readonly projectId: string;
378
+ readonly environmentId: string;
379
+ readonly name: string;
380
+ readonly idempotencyKey: string;
381
+ readonly wait: boolean;
382
+ readonly credentialStore: "native";
383
+ }
384
+ | {
385
+ readonly kind: "mail-domain-set";
386
+ readonly projectId: string;
387
+ readonly domain: string;
388
+ readonly idempotencyKey: string;
389
+ readonly credentialStore: "native";
390
+ }
391
+ | {
392
+ readonly kind: "mail-domain-status";
393
+ readonly projectId: string;
394
+ readonly credentialStore: "native";
395
+ }
396
+ | {
397
+ readonly kind: "domain-cloudflare-authorize";
398
+ readonly projectId: string;
399
+ readonly zone: string;
400
+ readonly idempotencyKey: string;
401
+ readonly credentialStore: "native";
402
+ }
403
+ | {
404
+ readonly kind: "domain-cloudflare-status";
405
+ readonly projectId: string;
406
+ readonly credentialStore: "native";
407
+ }
408
+ | {
409
+ readonly kind: "domain-cloudflare-apply";
410
+ readonly projectId: string;
411
+ readonly idempotencyKey: string;
412
+ readonly yes: true;
413
+ readonly wait: true;
414
+ readonly credentialStore: "native";
415
+ }
416
+ | {
417
+ readonly kind: "paid-domain-plan";
418
+ readonly projectId: string;
419
+ readonly hostname: string;
420
+ readonly credentialStore: "native";
421
+ }
422
+ | {
423
+ readonly kind: "paid-domain-status";
424
+ readonly projectId: string;
425
+ readonly credentialStore: "native";
426
+ }
427
+ | {
428
+ readonly kind: "paid-domain-apply";
429
+ readonly projectId: string;
430
+ readonly hostname: string;
431
+ readonly idempotencyKey: string;
432
+ readonly yes: true;
433
+ readonly credentialStore: "native";
434
+ }
435
+ | {
436
+ readonly kind: "paid-domain-delete";
437
+ readonly projectId: string;
438
+ readonly hostname: string;
439
+ readonly idempotencyKey: string;
440
+ readonly yes: true;
441
+ readonly credentialStore: "native";
442
+ };
443
+
444
+ export class InvalidCommandError extends Error {
445
+ public constructor() {
446
+ super("The command arguments are invalid.");
447
+ this.name = "InvalidCommandError";
448
+ }
449
+ }
450
+
451
+ export type RecognizedCommandLabel =
452
+ | "database compute set"
453
+ | "database compute get"
454
+ | "project context"
455
+ | "project notes set"
456
+ | "export create"
457
+ | "export get"
458
+ | "billing recharge get"
459
+ | "billing recharge set"
460
+ | "billing checkout"
461
+ | "billing status"
462
+ | "billing portal"
463
+ | "unknown"
464
+ | "help"
465
+ | "version"
466
+ | "whoami"
467
+ | "credits account"
468
+ | "credits balance"
469
+ | "credits usage"
470
+ | "budget get"
471
+ | "budget set"
472
+ | "init"
473
+ | "login"
474
+ | "logout"
475
+ | "operation get"
476
+ | "operation reconcile"
477
+ | "token create"
478
+ | "token list"
479
+ | "token revoke"
480
+ | "feedback submit"
481
+ | "project create"
482
+ | "organization create"
483
+ | "organization list"
484
+ | "organization use"
485
+ | "github connect"
486
+ | "github status"
487
+ | "project list"
488
+ | "project status"
489
+ | "project dev-access create"
490
+ | "database write"
491
+ | "database query"
492
+ | "database access create"
493
+ | "database access list"
494
+ | "database access revoke"
495
+ | "database psql"
496
+ | "link"
497
+ | "source auto-deploy set"
498
+ | "source auto-deploy status"
499
+ | "domain cloudflare authorize"
500
+ | "domain cloudflare status"
501
+ | "domain cloudflare apply"
502
+ | "domain paid plan"
503
+ | "domain paid apply"
504
+ | "domain paid status"
505
+ | "domain paid delete"
506
+ | "plan"
507
+ | "deploy"
508
+ | "logs"
509
+ | "deployment logs"
510
+ | "rollback plan"
511
+ | "rollback"
512
+ | "deployment promote plan"
513
+ | "deployment promote"
514
+ | "delete plan"
515
+ | "delete"
516
+ | "secret list"
517
+ | "function runs"
518
+ | "secret set"
519
+ | "secret delete"
520
+ | "mail domain set"
521
+ | "mail domain status";
522
+
523
+ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedCommandLabel => {
524
+ const values = [...argv];
525
+ if (values.includes("--help")) return "help";
526
+ if (values.includes("--version")) return "version";
527
+ while (values.length > 0) {
528
+ const value = values.shift();
529
+ if (
530
+ value === "--json" ||
531
+ value === "--dry-run" ||
532
+ value === "--revoke" ||
533
+ value === "--yes" ||
534
+ value === "--wait" ||
535
+ value === "--follow" ||
536
+ value === "--stdin"
537
+ ) {
538
+ continue;
539
+ }
540
+ if (
541
+ value === "help" ||
542
+ value === "version" ||
543
+ value === "whoami" ||
544
+ value === "init" ||
545
+ value === "login" ||
546
+ value === "logout"
547
+ )
548
+ return value;
549
+ if (value === "github" && values[0] === "connect") return "github connect";
550
+ if (value === "github" && values[0] === "status") return "github status";
551
+ if (value === "operation" && values[0] === "get") return "operation get";
552
+ if (value === "credits" && values[0] === "account") return "credits account";
553
+ if (value === "credits" && values[0] === "balance") return "credits balance";
554
+ if (value === "billing" && values[0] === "recharge" && values[1] === "get")
555
+ return "billing recharge get";
556
+ if (value === "billing" && values[0] === "recharge" && values[1] === "set")
557
+ return "billing recharge set";
558
+ if (value === "billing" && values[0] === "checkout") return "billing checkout";
559
+ if (value === "billing" && values[0] === "status") return "billing status";
560
+ if (value === "billing" && values[0] === "portal") return "billing portal";
561
+ if (value === "credits" && values[0] === "usage") return "credits usage";
562
+ if (value === "budget" && values[0] === "get") return "budget get";
563
+ if (value === "budget" && values[0] === "set") return "budget set";
564
+ if (value === "operation" && values[0] === "reconcile") return "operation reconcile";
565
+ if (value === "token" && ["create", "list", "revoke"].includes(values[0] ?? ""))
566
+ return `token ${values[0]}` as RecognizedCommandLabel;
567
+ if (value === "export" && (values[0] === "create" || values[0] === "get"))
568
+ return `export ${values[0]}`;
569
+ if (value === "feedback" && values[0] === "submit") return "feedback submit";
570
+ if (value === "project" && values[0] === "create") return "project create";
571
+ if (value === "organization" && values[0] === "create") return "organization create";
572
+ if (value === "organization" && values[0] === "list") return "organization list";
573
+ if (value === "organization" && values[0] === "use") return "organization use";
574
+ if (value === "project" && values[0] === "list") return "project list";
575
+ if (value === "project" && values[0] === "context") return "project context";
576
+ if (value === "project" && values[0] === "notes" && values[1] === "set")
577
+ return "project notes set";
578
+ if (value === "project" && values[0] === "status") return "project status";
579
+ if (value === "project" && values[0] === "dev-access" && values[1] === "create")
580
+ return "project dev-access create";
581
+ if (value === "database" && values[0] === "compute" && values[1] === "set")
582
+ return "database compute set";
583
+ if (value === "database" && values[0] === "compute" && values[1] === "get")
584
+ return "database compute get";
585
+ if (value === "database" && values[0] === "write") return "database write";
586
+ if (value === "database" && values[0] === "query") return "database query";
587
+ if (value === "database" && values[0] === "access") {
588
+ if (values[1] === "create") return "database access create";
589
+ if (values[1] === "list") return "database access list";
590
+ if (values[1] === "revoke") return "database access revoke";
591
+ }
592
+ if (value === "database" && values[0] === "psql") return "database psql";
593
+ if (value === "link" || value === "plan" || value === "deploy" || value === "logs")
594
+ return value;
595
+ if (value === "source" && values[0] === "auto-deploy") {
596
+ if (values[1] === "set") return "source auto-deploy set";
597
+ if (values[1] === "status") return "source auto-deploy status";
598
+ }
599
+ if (value === "domain" && values[0] === "cloudflare") {
600
+ if (values[1] === "authorize") return "domain cloudflare authorize";
601
+ if (values[1] === "status") return "domain cloudflare status";
602
+ if (values[1] === "apply") return "domain cloudflare apply";
603
+ }
604
+ if (value === "domain" && values[0] === "paid") {
605
+ if (values[1] === "plan") return "domain paid plan";
606
+ if (values[1] === "apply") return "domain paid apply";
607
+ if (values[1] === "status") return "domain paid status";
608
+ if (values[1] === "delete") return "domain paid delete";
609
+ }
610
+ if (value === "rollback") return values[0] === "plan" ? "rollback plan" : "rollback";
611
+ if (value === "deployment" && values[0] === "promote")
612
+ return values[1] === "plan" ? "deployment promote plan" : "deployment promote";
613
+ if (value === "deployment" && values[0] === "logs") return "deployment logs";
614
+ if (value === "delete") return values[0] === "plan" ? "delete plan" : "delete";
615
+ if (value === "secret") {
616
+ if (values[0] === "list") return "secret list";
617
+ if (values[0] === "set") return "secret set";
618
+ if (values[0] === "delete") return "secret delete";
619
+ }
620
+ if (value === "function" && values[0] === "runs") return "function runs";
621
+ if (value === "mail" && values[0] === "domain") {
622
+ if (values[1] === "set") return "mail domain set";
623
+ if (values[1] === "status") return "mail domain status";
624
+ }
625
+ return "unknown";
626
+ }
627
+ return "unknown";
628
+ };
629
+
630
+ export const parseProductCliCommand = (argv: readonly string[]): ProductCliCommand => {
631
+ const values = [...argv];
632
+ removeFlag(values, "--json");
633
+ const help = removeFlag(values, "--help");
634
+ if ((values.length === 1 && values[0] === "help") || values.length === 0) {
635
+ return { kind: "help" };
636
+ }
637
+ if (help) {
638
+ const topic = recognizedCommandLabel(values);
639
+ if (topic === "unknown" || topic === "version" || topic !== values.join(" "))
640
+ throw new InvalidCommandError();
641
+ return { kind: "help", topic };
642
+ }
643
+ if (values.length === 1 && (values[0] === "--version" || values[0] === "version")) {
644
+ return { kind: "version" };
645
+ }
646
+ const dryRun = removeFlag(values, "--dry-run");
647
+ const credentialStore = "native" as const;
648
+ const revoke = removeFlag(values, "--revoke");
649
+ const yes = removeFlag(values, "--yes");
650
+ const wait = removeFlag(values, "--wait");
651
+ const follow = removeFlag(values, "--follow");
652
+ const stdin = removeFlag(values, "--stdin");
653
+ const first = values.shift();
654
+ if (
655
+ first === "whoami" &&
656
+ !dryRun &&
657
+ !revoke &&
658
+ !yes &&
659
+ !wait &&
660
+ !follow &&
661
+ !stdin &&
662
+ values.length === 0
663
+ ) {
664
+ return { kind: "whoami", credentialStore };
665
+ }
666
+ if (first === "init") {
667
+ if (revoke || yes || wait || follow || stdin) throw new InvalidCommandError();
668
+ const options = parseOptionalOptions(values, [
669
+ "--directory",
670
+ "--project",
671
+ "--root",
672
+ "--region",
673
+ ]);
674
+ const region = options["--region"];
675
+ if (region !== undefined && region !== "us" && region !== "eu") throw new InvalidCommandError();
676
+ return {
677
+ kind: "init",
678
+ directory: options["--directory"] ?? ".",
679
+ project: options["--project"],
680
+ root: options["--root"],
681
+ ...(region === undefined ? {} : { region }),
682
+ dryRun,
683
+ };
684
+ }
685
+ if (dryRun) throw new InvalidCommandError();
686
+ if (first === "login" && !revoke && !yes && !wait && !follow && !stdin) {
687
+ const options = parseOptionalOptions(values, ["--organization"]);
688
+ const organizationId = options["--organization"];
689
+ if (organizationId !== undefined && !ORGANIZATION_ULID.test(organizationId))
690
+ throw new InvalidCommandError();
691
+ return {
692
+ kind: "login",
693
+ ...(organizationId === undefined ? {} : { organizationId }),
694
+ credentialStore,
695
+ };
696
+ }
697
+ if (first === "logout") {
698
+ if (!yes && !wait && !follow && !stdin && values.length === 0)
699
+ return { kind: "logout", revoke, credentialStore };
700
+ }
701
+ if (revoke) throw new InvalidCommandError();
702
+ if (first === "token") {
703
+ if (wait || follow || stdin) throw new InvalidCommandError();
704
+ const sub = values.shift();
705
+ const options = parseOptionalOptions(
706
+ values,
707
+ sub === "create"
708
+ ? ["--organization", "--name", "--idempotency-key", "--out"]
709
+ : sub === "list"
710
+ ? ["--organization", "--after"]
711
+ : ["--organization", "--key"],
712
+ );
713
+ const organizationId = options["--organization"];
714
+ if (!organizationId || !/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(organizationId))
715
+ throw new InvalidCommandError();
716
+ if (sub === "create" && !yes) {
717
+ const name = options["--name"],
718
+ idempotencyKey = options["--idempotency-key"],
719
+ outputPath = options["--out"];
720
+ if (
721
+ !name ||
722
+ name.trim() !== name ||
723
+ name.length > 64 ||
724
+ /\p{Cc}/u.test(name) ||
725
+ !idempotencyKey ||
726
+ !/^[A-Za-z0-9._:-]{1,128}$/u.test(idempotencyKey) ||
727
+ !outputPath
728
+ )
729
+ throw new InvalidCommandError();
730
+ return {
731
+ kind: "token-create",
732
+ organizationId,
733
+ name,
734
+ idempotencyKey,
735
+ outputPath,
736
+ credentialStore,
737
+ };
738
+ }
739
+ if (sub === "list" && !yes) {
740
+ const after = options["--after"];
741
+ if (after !== undefined && !USER_API_KEY_ID.test(after)) throw new InvalidCommandError();
742
+ return {
743
+ kind: "token-list",
744
+ organizationId,
745
+ ...(after === undefined ? {} : { after }),
746
+ credentialStore,
747
+ };
748
+ }
749
+ if (sub === "revoke" && yes && options["--key"] && USER_API_KEY_ID.test(options["--key"]))
750
+ return { kind: "token-revoke", organizationId, keyId: options["--key"], credentialStore };
751
+ throw new InvalidCommandError();
752
+ }
753
+ if (first === "feedback" && values.shift() === "submit") {
754
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
755
+ const options = parseOptionalOptions(values, [
756
+ "--organization",
757
+ "--kind",
758
+ "--title",
759
+ "--description",
760
+ "--project",
761
+ "--environment",
762
+ "--operation",
763
+ "--error-code",
764
+ "--client-version",
765
+ "--idempotency-key",
766
+ ]);
767
+ const organization = options["--organization"],
768
+ kind = options["--kind"],
769
+ title = options["--title"],
770
+ description = options["--description"],
771
+ key = options["--idempotency-key"];
772
+ if (
773
+ !organization ||
774
+ !title ||
775
+ !description ||
776
+ !key ||
777
+ (kind !== "bug" && kind !== "issue" && kind !== "feature_request")
778
+ )
779
+ throw new InvalidCommandError();
780
+ return {
781
+ kind: "feedback-submit",
782
+ credentialStore,
783
+ idempotencyKey: key,
784
+ report: {
785
+ organization_id: organization,
786
+ kind,
787
+ title,
788
+ description,
789
+ ...(options["--project"] === undefined ? {} : { project_id: options["--project"] }),
790
+ ...(options["--environment"] === undefined
791
+ ? {}
792
+ : { environment_id: options["--environment"] }),
793
+ ...(options["--operation"] === undefined ? {} : { operation_id: options["--operation"] }),
794
+ ...(options["--error-code"] === undefined ? {} : { error_code: options["--error-code"] }),
795
+ ...(options["--client-version"] === undefined
796
+ ? {}
797
+ : { client_version: options["--client-version"] }),
798
+ },
799
+ };
800
+ }
801
+ if (first === "billing") {
802
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
803
+ const sub = values.shift();
804
+ if (sub === "recharge") {
805
+ const action = values.shift();
806
+ if (action === "get") {
807
+ const o = parseOptions(values, ["--organization"]);
808
+ return {
809
+ kind: "billing-recharge-get",
810
+ organizationId: o["--organization"],
811
+ credentialStore,
812
+ };
813
+ }
814
+ if (action !== "set") throw new InvalidCommandError();
815
+ const o = parseOptionalOptions(values, [
816
+ "--organization",
817
+ "--enabled",
818
+ "--monthly-limit-minor",
819
+ "--consent",
820
+ "--revision",
821
+ "--idempotency-key",
822
+ ]);
823
+ const limit = Number(o["--monthly-limit-minor"]),
824
+ revision = Number(o["--revision"]);
825
+ if (
826
+ !o["--organization"] ||
827
+ !o["--idempotency-key"] ||
828
+ !["true", "false"].includes(o["--enabled"] ?? "") ||
829
+ !Number.isSafeInteger(limit) ||
830
+ limit < 1000 ||
831
+ limit > 100000 ||
832
+ !o["--revision"] ||
833
+ !Number.isSafeInteger(revision) ||
834
+ revision < 0 ||
835
+ (o["--enabled"] === "true"
836
+ ? o["--consent"] !== "off_session_v1"
837
+ : o["--consent"] !== undefined)
838
+ )
839
+ throw new InvalidCommandError();
840
+ return {
841
+ kind: "billing-recharge-set",
842
+ organizationId: o["--organization"],
843
+ enabled: o["--enabled"] === "true",
844
+ monthlyLimitMinor: limit,
845
+ consent: o["--enabled"] === "true" ? "off_session_v1" : null,
846
+ revision,
847
+ idempotencyKey: o["--idempotency-key"],
848
+ credentialStore,
849
+ };
850
+ }
851
+ if (sub === "portal") {
852
+ const options = parseOptions(values, ["--organization"]);
853
+ return { kind: "billing-portal", organizationId: options["--organization"], credentialStore };
854
+ }
855
+ if (sub === "status") {
856
+ const options = parseOptions(values, ["--organization", "--checkout"]);
857
+ return {
858
+ kind: "billing-status",
859
+ organizationId: options["--organization"],
860
+ checkoutId: options["--checkout"],
861
+ credentialStore,
862
+ };
863
+ }
864
+ if (sub === "checkout") {
865
+ const options = parseOptionalOptions(values, [
866
+ "--organization",
867
+ "--offer",
868
+ "--packs",
869
+ "--idempotency-key",
870
+ ]),
871
+ offer = options["--offer"],
872
+ packs = Number(options["--packs"] ?? "1");
873
+ if (
874
+ !options["--organization"] ||
875
+ !options["--idempotency-key"] ||
876
+ (offer !== "topup" && offer !== "paid") ||
877
+ !/^[1-9][0-9]{0,2}$/u.test(options["--packs"] ?? "1") ||
878
+ packs > 100 ||
879
+ (offer === "paid" && packs !== 1)
880
+ )
881
+ throw new InvalidCommandError();
882
+ return {
883
+ kind: "billing-checkout",
884
+ organizationId: options["--organization"],
885
+ offer,
886
+ packs,
887
+ idempotencyKey: options["--idempotency-key"],
888
+ credentialStore,
889
+ };
890
+ }
891
+ throw new InvalidCommandError();
892
+ }
893
+ if (first === "credits" && values[0] === "usage") {
894
+ values.shift();
895
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
896
+ const options = parseOptionalOptions(values, ["--organization", "--month", "--cursor"]);
897
+ if (
898
+ !options["--organization"] ||
899
+ !options["--month"] ||
900
+ !/^20[0-9]{2}-(0[1-9]|1[0-2])$/u.test(options["--month"])
901
+ )
902
+ throw new InvalidCommandError();
903
+ return {
904
+ kind: "credits-usage",
905
+ organizationId: options["--organization"],
906
+ month: options["--month"],
907
+ ...(options["--cursor"] ? { cursor: options["--cursor"] } : {}),
908
+ credentialStore,
909
+ };
910
+ }
911
+ if (first === "credits" && values[0] === "account") {
912
+ values.shift();
913
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
914
+ const options = parseOptions(values, ["--organization"]);
915
+ return { kind: "credits-account", organizationId: options["--organization"], credentialStore };
916
+ }
917
+ if (first === "credits" && values.shift() === "balance") {
918
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
919
+ const options = parseOptions(values, ["--organization"]);
920
+ return { kind: "credits-balance", organizationId: options["--organization"], credentialStore };
921
+ }
922
+ if (first === "budget") {
923
+ const subcommand = values.shift();
924
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
925
+ if (subcommand === "get")
926
+ return {
927
+ kind: "budget-get",
928
+ projectId: parseOptions(values, ["--project"])["--project"],
929
+ credentialStore,
930
+ };
931
+ if (subcommand !== "set") throw new InvalidCommandError();
932
+ const options = parseOptionalOptions(values, [
933
+ "--project",
934
+ "--credits",
935
+ "--mode",
936
+ "--idempotency-key",
937
+ ]);
938
+ const projectId = options["--project"];
939
+ const value = options["--credits"];
940
+ const mode = options["--mode"] ?? "continue";
941
+ const idempotencyKey = options["--idempotency-key"];
942
+ if (
943
+ !isValue(projectId) ||
944
+ !isValue(value) ||
945
+ !isValue(idempotencyKey) ||
946
+ (mode !== "continue" && mode !== "stop")
947
+ )
948
+ throw new InvalidCommandError();
949
+ if (value === "none") {
950
+ if (mode !== "continue") throw new InvalidCommandError();
951
+ return {
952
+ kind: "budget-set",
953
+ projectId,
954
+ amountMicros: null,
955
+ mode,
956
+ idempotencyKey,
957
+ credentialStore,
958
+ };
959
+ }
960
+ if (!/^(0|[1-9][0-9]{0,12})(\.[0-9]{1,6})?$/u.test(value)) throw new InvalidCommandError();
961
+ const [whole, fraction = ""] = value.split(".");
962
+ const amount = BigInt(whole ?? "0") * 1_000_000n + BigInt(fraction.padEnd(6, "0"));
963
+ if (amount > 9223372036854775807n) throw new InvalidCommandError();
964
+ return {
965
+ kind: "budget-set",
966
+ projectId,
967
+ amountMicros: amount.toString(),
968
+ mode,
969
+ idempotencyKey,
970
+ credentialStore,
971
+ };
972
+ }
973
+ if (first === "operation") {
974
+ const subcommand = values.shift();
975
+ if (subcommand === "get") {
976
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
977
+ const operationId = values.shift();
978
+ if (isValue(operationId) && values.length === 0) {
979
+ return { kind: "operation-get", operationId, credentialStore };
980
+ }
981
+ }
982
+ if (subcommand === "reconcile") {
983
+ if (!yes || wait || follow || stdin) throw new InvalidCommandError();
984
+ const operationId = values.shift();
985
+ if (!isValue(operationId)) throw new InvalidCommandError();
986
+ const options = parseOptions(values, ["--idempotency-key"]);
987
+ return {
988
+ kind: "operation-reconcile",
989
+ operationId,
990
+ idempotencyKey: options["--idempotency-key"],
991
+ yes,
992
+ credentialStore,
993
+ };
994
+ }
995
+ throw new InvalidCommandError();
996
+ }
997
+ if (first === "github") {
998
+ const subcommand = values.shift();
999
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1000
+ if (subcommand !== "connect" && subcommand !== "status") throw new InvalidCommandError();
1001
+ const options = parseOptions(
1002
+ values,
1003
+ subcommand === "connect" ? ["--organization", "--idempotency-key"] : ["--organization"],
1004
+ );
1005
+ const organizationId = options["--organization"];
1006
+ if (!ORGANIZATION_ULID.test(organizationId)) throw new InvalidCommandError();
1007
+ return subcommand === "connect"
1008
+ ? {
1009
+ kind: "github-connect",
1010
+ organizationId,
1011
+ idempotencyKey: options["--idempotency-key"],
1012
+ credentialStore,
1013
+ }
1014
+ : { kind: "github-status", organizationId, credentialStore };
1015
+ }
1016
+ if (first === "organization") {
1017
+ const subcommand = values.shift();
1018
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1019
+ if (subcommand === "create") {
1020
+ const options = parseOptionalOptions(values, ["--name", "--source", "--idempotency-key"]);
1021
+ const name = options["--name"];
1022
+ const idempotencyKey = options["--idempotency-key"];
1023
+ // Signup is open: the source is optional attribution, the other two are required.
1024
+ if (!isValue(name) || !isValue(idempotencyKey)) throw new InvalidCommandError();
1025
+ const signupSource = options["--source"];
1026
+ if (signupSource !== undefined && !isValue(signupSource)) throw new InvalidCommandError();
1027
+ return {
1028
+ kind: "organization-create",
1029
+ ...(signupSource === undefined ? {} : { signupSource }),
1030
+ name,
1031
+ idempotencyKey,
1032
+ credentialStore,
1033
+ };
1034
+ }
1035
+ if (subcommand === "list") {
1036
+ if (values.length > 0) throw new InvalidCommandError();
1037
+ return { kind: "organization-list", credentialStore };
1038
+ }
1039
+ if (subcommand === "use") {
1040
+ const organizationId = parseOptions(values, ["--organization"])["--organization"];
1041
+ if (!ORGANIZATION_ULID.test(organizationId)) throw new InvalidCommandError();
1042
+ return { kind: "organization-use", organizationId, credentialStore };
1043
+ }
1044
+ throw new InvalidCommandError();
1045
+ }
1046
+ if (first === "project") {
1047
+ const subcommand = values.shift();
1048
+ if (subcommand === "dev-access" && values.shift() === "create") {
1049
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1050
+ const options = parseOptions(values, ["--project"]);
1051
+ return {
1052
+ kind: "project-dev-access-create",
1053
+ projectId: options["--project"],
1054
+ credentialStore,
1055
+ };
1056
+ }
1057
+ if (subcommand === "list") {
1058
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1059
+ const options = parseOptionalOptions(values, ["--cursor", "--limit"]);
1060
+ const limit = options["--limit"] === undefined ? 50 : Number(options["--limit"]);
1061
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
1062
+ throw new InvalidCommandError();
1063
+ }
1064
+ return {
1065
+ kind: "project-list",
1066
+ cursor: options["--cursor"],
1067
+ limit,
1068
+ credentialStore,
1069
+ };
1070
+ }
1071
+ if (subcommand === "context") {
1072
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1073
+ const options = parseOptions(values, ["--project"]);
1074
+ return { kind: "project-context", projectId: options["--project"], credentialStore };
1075
+ }
1076
+ if (subcommand === "notes" && values.shift() === "set") {
1077
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1078
+ const textIndex = values.indexOf("--markdown");
1079
+ const markdown = values[textIndex + 1];
1080
+ if (textIndex < 0 || markdown === undefined) throw new InvalidCommandError();
1081
+ values.splice(textIndex, 2);
1082
+ const options = parseOptions(values, ["--project", "--version", "--idempotency-key"]);
1083
+ if (
1084
+ !/^(?:0|[1-9][0-9]*)$/u.test(options["--version"]) ||
1085
+ Number(options["--version"]) >= 2147483647
1086
+ )
1087
+ throw new InvalidCommandError();
1088
+ return {
1089
+ kind: "project-notes-set",
1090
+ projectId: options["--project"],
1091
+ markdown,
1092
+ expectedVersion: Number(options["--version"]),
1093
+ idempotencyKey: options["--idempotency-key"],
1094
+ credentialStore,
1095
+ };
1096
+ }
1097
+ if (subcommand === "status") {
1098
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1099
+ const options = parseOptions(values, ["--project"]);
1100
+ return { kind: "project-status", projectId: options["--project"], credentialStore };
1101
+ }
1102
+ if (subcommand !== "create") throw new InvalidCommandError();
1103
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1104
+ const options = parseOptionalOptions(values, [
1105
+ "--organization",
1106
+ "--name",
1107
+ "--idempotency-key",
1108
+ "--data-mode",
1109
+ "--region",
1110
+ ]);
1111
+ const dataMode = options["--data-mode"];
1112
+ const region = options["--region"];
1113
+ if (
1114
+ !options["--organization"] ||
1115
+ !options["--name"] ||
1116
+ !options["--idempotency-key"] ||
1117
+ (dataMode !== undefined && dataMode !== "shared" && dataMode !== "isolated") ||
1118
+ (region !== undefined && region !== "us" && region !== "eu")
1119
+ )
1120
+ throw new InvalidCommandError();
1121
+ return {
1122
+ kind: "project-create",
1123
+ organizationId: options["--organization"],
1124
+ name: options["--name"],
1125
+ ...(dataMode === undefined ? {} : { dataMode }),
1126
+ ...(region === undefined ? {} : { region }),
1127
+ idempotencyKey: options["--idempotency-key"],
1128
+ credentialStore,
1129
+ };
1130
+ }
1131
+ if (first === "database" && values[0] === "compute" && values[1] === "set") {
1132
+ values.splice(0, 2);
1133
+ if (!yes || follow || stdin || revoke) throw new InvalidCommandError();
1134
+ const options = parseOptionalOptions(values, [
1135
+ "--project",
1136
+ "--environment",
1137
+ "--profile",
1138
+ "--idempotency-key",
1139
+ ]);
1140
+ if (
1141
+ !options["--project"] ||
1142
+ !options["--idempotency-key"] ||
1143
+ (options["--profile"] !== "standard" && options["--profile"] !== "performance") ||
1144
+ (options["--environment"] !== "dev" && options["--environment"] !== "prod")
1145
+ )
1146
+ throw new InvalidCommandError();
1147
+ return {
1148
+ kind: "database-compute-set",
1149
+ projectId: options["--project"],
1150
+ environment: options["--environment"],
1151
+ profile: options["--profile"],
1152
+ idempotencyKey: options["--idempotency-key"],
1153
+ wait,
1154
+ credentialStore,
1155
+ };
1156
+ }
1157
+ if (first === "database" && values[0] === "compute" && values[1] === "get") {
1158
+ values.splice(0, 2);
1159
+ if (yes || wait || follow || stdin || revoke) throw new InvalidCommandError();
1160
+ const options = parseOptionalOptions(values, ["--project", "--environment"]);
1161
+ if (!options["--project"]) throw new InvalidCommandError();
1162
+ const environment = options["--environment"] ?? "dev";
1163
+ if (environment !== "dev" && environment !== "prod") throw new InvalidCommandError();
1164
+ return {
1165
+ kind: "database-compute-get",
1166
+ projectId: options["--project"],
1167
+ environment,
1168
+ credentialStore,
1169
+ };
1170
+ }
1171
+ if (first === "database" && values[0] === "write") {
1172
+ values.shift();
1173
+ if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
1174
+ const options = parseOptionalOptions(values, [
1175
+ "--project",
1176
+ "--environment",
1177
+ "--statement-file",
1178
+ "--parameters-json",
1179
+ "--idempotency-key",
1180
+ ]);
1181
+ const projectId = options["--project"],
1182
+ environment = options["--environment"],
1183
+ statementFile = options["--statement-file"],
1184
+ idempotencyKey = options["--idempotency-key"];
1185
+ if (
1186
+ !isValue(projectId) ||
1187
+ !isValue(statementFile) ||
1188
+ !isValue(idempotencyKey) ||
1189
+ (environment !== "dev" && environment !== "prod")
1190
+ )
1191
+ throw new InvalidCommandError();
1192
+ let parameters: unknown;
1193
+ try {
1194
+ parameters = JSON.parse(options["--parameters-json"] ?? "[]");
1195
+ } catch {
1196
+ throw new InvalidCommandError();
1197
+ }
1198
+ if (!Array.isArray(parameters) || parameters.length > 100) throw new InvalidCommandError();
1199
+ return {
1200
+ kind: "database-write",
1201
+ projectId,
1202
+ environment,
1203
+ statementFile,
1204
+ parameters,
1205
+ idempotencyKey,
1206
+ yes: true,
1207
+ credentialStore,
1208
+ };
1209
+ }
1210
+ if (first === "database" && values[0] === "access") {
1211
+ values.shift();
1212
+ const subcommand = values.shift();
1213
+ if (subcommand === "create") {
1214
+ if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
1215
+ const options = parseOptionalOptions(values, [
1216
+ "--project",
1217
+ "--environment",
1218
+ "--mode",
1219
+ "--ttl",
1220
+ "--label",
1221
+ ]);
1222
+ const projectId = options["--project"],
1223
+ environment = options["--environment"],
1224
+ mode = options["--mode"] ?? "read";
1225
+ if (
1226
+ !isValue(projectId) ||
1227
+ (environment !== "dev" && environment !== "prod") ||
1228
+ (mode !== "read" && mode !== "write")
1229
+ )
1230
+ throw new InvalidCommandError();
1231
+ const label = options["--label"];
1232
+ if (label !== undefined && (label.trim() !== label || label.length < 1 || label.length > 64))
1233
+ throw new InvalidCommandError();
1234
+ return {
1235
+ kind: "database-access-create",
1236
+ projectId,
1237
+ environment,
1238
+ mode,
1239
+ ttlSeconds: accessTtlSeconds(options["--ttl"]),
1240
+ label: label ?? null,
1241
+ yes: true,
1242
+ credentialStore,
1243
+ };
1244
+ }
1245
+ if (subcommand === "list") {
1246
+ if (yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
1247
+ const options = parseOptionalOptions(values, ["--project", "--environment"]);
1248
+ const projectId = options["--project"],
1249
+ environment = options["--environment"];
1250
+ if (
1251
+ !isValue(projectId) ||
1252
+ (environment !== undefined && environment !== "dev" && environment !== "prod")
1253
+ )
1254
+ throw new InvalidCommandError();
1255
+ return {
1256
+ kind: "database-access-list",
1257
+ projectId,
1258
+ ...(environment === undefined ? {} : { environment }),
1259
+ credentialStore,
1260
+ };
1261
+ }
1262
+ if (subcommand === "revoke") {
1263
+ if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
1264
+ const options = parseOptions(values, ["--project", "--access"]);
1265
+ return {
1266
+ kind: "database-access-revoke",
1267
+ projectId: options["--project"],
1268
+ accessId: options["--access"],
1269
+ yes: true,
1270
+ credentialStore,
1271
+ };
1272
+ }
1273
+ throw new InvalidCommandError();
1274
+ }
1275
+ if (first === "database" && values[0] === "psql") {
1276
+ values.shift();
1277
+ if (yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
1278
+ const options = parseOptionalOptions(values, ["--project", "--environment", "--mode", "--ttl"]);
1279
+ const projectId = options["--project"],
1280
+ environment = options["--environment"],
1281
+ mode = options["--mode"] ?? "read";
1282
+ if (
1283
+ !isValue(projectId) ||
1284
+ (environment !== "dev" && environment !== "prod") ||
1285
+ (mode !== "read" && mode !== "write")
1286
+ )
1287
+ throw new InvalidCommandError();
1288
+ return {
1289
+ kind: "database-psql",
1290
+ projectId,
1291
+ environment,
1292
+ mode,
1293
+ ttlSeconds: accessTtlSeconds(options["--ttl"]),
1294
+ credentialStore,
1295
+ };
1296
+ }
1297
+ if (first === "database" && values.shift() === "query") {
1298
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1299
+ const options = parseOptionalOptions(values, [
1300
+ "--project",
1301
+ "--environment",
1302
+ "--statement",
1303
+ "--parameters-json",
1304
+ ]);
1305
+ const environment = options["--environment"];
1306
+ if (environment !== "dev" && environment !== "prod") throw new InvalidCommandError();
1307
+ const projectId = options["--project"];
1308
+ const statement = options["--statement"];
1309
+ if (
1310
+ !isValue(projectId) ||
1311
+ !isValue(statement) ||
1312
+ !/^(?:select|with)\b/iu.test(statement.trimStart()) ||
1313
+ new TextEncoder().encode(statement).byteLength > 4_096
1314
+ ) {
1315
+ throw new InvalidCommandError();
1316
+ }
1317
+ let parameters: unknown = [];
1318
+ try {
1319
+ parameters = JSON.parse(options["--parameters-json"] ?? "[]") as unknown;
1320
+ } catch {
1321
+ throw new InvalidCommandError();
1322
+ }
1323
+ if (
1324
+ !Array.isArray(parameters) ||
1325
+ parameters.length > 32 ||
1326
+ !parameters.every(
1327
+ (parameter) =>
1328
+ parameter === null ||
1329
+ typeof parameter === "string" ||
1330
+ typeof parameter === "boolean" ||
1331
+ (typeof parameter === "number" && Number.isFinite(parameter)),
1332
+ )
1333
+ ) {
1334
+ throw new InvalidCommandError();
1335
+ }
1336
+ return {
1337
+ kind: "database-query",
1338
+ projectId,
1339
+ environment,
1340
+ statement,
1341
+ parameters: Object.freeze([...parameters]) as readonly (string | number | boolean | null)[],
1342
+ credentialStore,
1343
+ };
1344
+ }
1345
+ if (first === "link") {
1346
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1347
+ const options = parseOptions(values, [
1348
+ "--project",
1349
+ "--repository-owner",
1350
+ "--repository-name",
1351
+ "--idempotency-key",
1352
+ ]);
1353
+ return {
1354
+ kind: "link",
1355
+ projectId: options["--project"],
1356
+ repositoryOwner: options["--repository-owner"],
1357
+ repositoryName: options["--repository-name"],
1358
+ idempotencyKey: options["--idempotency-key"],
1359
+ credentialStore,
1360
+ };
1361
+ }
1362
+ if (first === "source" && values.shift() === "auto-deploy") {
1363
+ const subcommand = values.shift();
1364
+ if (subcommand === "set") {
1365
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1366
+ const options = parseOptions(values, [
1367
+ "--project",
1368
+ "--branch",
1369
+ "--enabled",
1370
+ "--idempotency-key",
1371
+ ]);
1372
+ if (!validGithubBranchName(options["--branch"])) throw new InvalidCommandError();
1373
+ if (options["--enabled"] !== "true" && options["--enabled"] !== "false")
1374
+ throw new InvalidCommandError();
1375
+ return {
1376
+ kind: "source-auto-deploy-set",
1377
+ projectId: options["--project"],
1378
+ branch: options["--branch"],
1379
+ enabled: options["--enabled"] === "true",
1380
+ idempotencyKey: options["--idempotency-key"],
1381
+ credentialStore,
1382
+ };
1383
+ }
1384
+ if (subcommand === "status") {
1385
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1386
+ const options = parseOptions(values, ["--project"]);
1387
+ return {
1388
+ kind: "source-auto-deploy-status",
1389
+ projectId: options["--project"],
1390
+ credentialStore,
1391
+ };
1392
+ }
1393
+ throw new InvalidCommandError();
1394
+ }
1395
+ if (first === "plan") {
1396
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1397
+ const options = parseOptions(values, ["--project", "--commit"]);
1398
+ return {
1399
+ kind: "plan",
1400
+ projectId: options["--project"],
1401
+ commitSha: options["--commit"],
1402
+ credentialStore,
1403
+ };
1404
+ }
1405
+ if (first === "deploy") {
1406
+ if (!yes || follow || stdin) throw new InvalidCommandError();
1407
+ const options = parseOptions(values, ["--project", "--plan-id", "--idempotency-key"]);
1408
+ return {
1409
+ kind: "deploy",
1410
+ projectId: options["--project"],
1411
+ planId: options["--plan-id"],
1412
+ idempotencyKey: options["--idempotency-key"],
1413
+ yes,
1414
+ wait,
1415
+ credentialStore,
1416
+ };
1417
+ }
1418
+ if (first === "logs") {
1419
+ if (!follow || yes || wait || stdin) throw new InvalidCommandError();
1420
+ const operationId = values.shift();
1421
+ if (isValue(operationId) && values.length === 0)
1422
+ return { kind: "logs", operationId, follow, credentialStore };
1423
+ }
1424
+ if (first === "deployment" && values[0] === "logs") {
1425
+ values.shift();
1426
+ if (!follow || yes || wait || stdin) throw new InvalidCommandError();
1427
+ const options = parseOptions(values, ["--project", "--deployment"]);
1428
+ return {
1429
+ kind: "deployment-logs",
1430
+ projectId: options["--project"],
1431
+ deploymentId: options["--deployment"],
1432
+ follow: true,
1433
+ credentialStore,
1434
+ };
1435
+ }
1436
+ if (first === "rollback" && values[0] === "plan") {
1437
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1438
+ values.shift();
1439
+ const options = parseOptions(values, ["--project", "--deployment"]);
1440
+ return {
1441
+ kind: "rollback-plan",
1442
+ projectId: options["--project"],
1443
+ deploymentId: options["--deployment"],
1444
+ credentialStore,
1445
+ };
1446
+ }
1447
+ if (first === "rollback") {
1448
+ if (!yes || wait || follow || stdin) throw new InvalidCommandError();
1449
+ const options = parseOptions(values, [
1450
+ "--project",
1451
+ "--deployment",
1452
+ "--if-match",
1453
+ "--confirmation-token",
1454
+ "--idempotency-key",
1455
+ ]);
1456
+ return {
1457
+ kind: "rollback",
1458
+ projectId: options["--project"],
1459
+ deploymentId: options["--deployment"],
1460
+ ifMatch: options["--if-match"],
1461
+ confirmationToken: options["--confirmation-token"],
1462
+ idempotencyKey: options["--idempotency-key"],
1463
+ yes,
1464
+ credentialStore,
1465
+ };
1466
+ }
1467
+ if (first === "deployment" && values[0] === "promote" && values[1] === "plan") {
1468
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1469
+ values.splice(0, 2);
1470
+ const options = parseOptions(values, ["--project", "--deployment"]);
1471
+ return {
1472
+ kind: "promotion-plan",
1473
+ projectId: options["--project"],
1474
+ sourceDeploymentId: options["--deployment"],
1475
+ credentialStore,
1476
+ };
1477
+ }
1478
+ if (first === "deployment" && values[0] === "promote") {
1479
+ if (!yes || follow || stdin) throw new InvalidCommandError();
1480
+ values.shift();
1481
+ const options = parseOptions(values, [
1482
+ "--project",
1483
+ "--deployment",
1484
+ "--if-match",
1485
+ "--confirmation-token",
1486
+ "--idempotency-key",
1487
+ ]);
1488
+ return {
1489
+ kind: "promote",
1490
+ projectId: options["--project"],
1491
+ sourceDeploymentId: options["--deployment"],
1492
+ ifMatch: options["--if-match"],
1493
+ confirmationToken: options["--confirmation-token"],
1494
+ idempotencyKey: options["--idempotency-key"],
1495
+ yes,
1496
+ wait,
1497
+ credentialStore,
1498
+ };
1499
+ }
1500
+ if (first === "delete" && values[0] === "plan") {
1501
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1502
+ values.shift();
1503
+ const options = parseOptions(values, ["--project"]);
1504
+ return { kind: "delete-plan", projectId: options["--project"], credentialStore };
1505
+ }
1506
+ if (first === "delete") {
1507
+ if (!yes || wait || follow || stdin) throw new InvalidCommandError();
1508
+ const options = parseOptions(values, [
1509
+ "--project",
1510
+ "--if-match",
1511
+ "--confirmation-token",
1512
+ "--idempotency-key",
1513
+ ]);
1514
+ return {
1515
+ kind: "delete",
1516
+ projectId: options["--project"],
1517
+ ifMatch: options["--if-match"],
1518
+ confirmationToken: options["--confirmation-token"],
1519
+ idempotencyKey: options["--idempotency-key"],
1520
+ yes,
1521
+ credentialStore,
1522
+ };
1523
+ }
1524
+ if (first === "export" && values[0] === "create") {
1525
+ if (yes || wait || follow || !stdin) throw new InvalidCommandError();
1526
+ values.shift();
1527
+ const options = parseOptions(values, ["--project", "--idempotency-key"]);
1528
+ return {
1529
+ kind: "export-create",
1530
+ projectId: options["--project"],
1531
+ idempotencyKey: options["--idempotency-key"],
1532
+ stdin,
1533
+ credentialStore,
1534
+ };
1535
+ }
1536
+ if (first === "export" && values[0] === "get") {
1537
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1538
+ values.shift();
1539
+ const exportId = values.shift();
1540
+ if (!exportId) throw new InvalidCommandError();
1541
+ const options = parseOptions(values, ["--project"]);
1542
+ return { kind: "export-get", projectId: options["--project"], exportId, credentialStore };
1543
+ }
1544
+ if (first === "secret" && values[0] === "list") {
1545
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1546
+ values.shift();
1547
+ const options = parseOptions(values, ["--project", "--environment"]);
1548
+ return {
1549
+ kind: "secret-list",
1550
+ projectId: options["--project"],
1551
+ environmentId: options["--environment"],
1552
+ credentialStore,
1553
+ };
1554
+ }
1555
+ if (first === "function" && values[0] === "runs") {
1556
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1557
+ values.shift();
1558
+ // --limit is the only optional option: one to one hundred runs, fifty by default.
1559
+ const limitIndex = values.indexOf("--limit");
1560
+ let limit = 50;
1561
+ if (limitIndex !== -1) {
1562
+ const raw = values[limitIndex + 1];
1563
+ if (typeof raw !== "string" || !/^[1-9][0-9]{0,2}$/u.test(raw) || Number(raw) > 100) {
1564
+ throw new InvalidCommandError();
1565
+ }
1566
+ limit = Number(raw);
1567
+ values.splice(limitIndex, 2);
1568
+ }
1569
+ const options = parseOptions(values, ["--project", "--environment"]);
1570
+ return {
1571
+ kind: "function-runs",
1572
+ projectId: options["--project"],
1573
+ environmentId: options["--environment"],
1574
+ limit,
1575
+ credentialStore,
1576
+ };
1577
+ }
1578
+ if (first === "secret" && values[0] === "set") {
1579
+ if (yes || follow || !stdin) throw new InvalidCommandError();
1580
+ values.shift();
1581
+ const name = values.shift();
1582
+ if (!isUserSecretName(name)) throw new InvalidCommandError();
1583
+ const options = parseOptions(values, ["--project", "--environment", "--idempotency-key"]);
1584
+ return {
1585
+ kind: "secret-set",
1586
+ projectId: options["--project"],
1587
+ environmentId: options["--environment"],
1588
+ name,
1589
+ idempotencyKey: options["--idempotency-key"],
1590
+ stdin,
1591
+ wait,
1592
+ credentialStore,
1593
+ };
1594
+ }
1595
+ if (first === "secret" && values[0] === "delete") {
1596
+ if (yes || follow || stdin) throw new InvalidCommandError();
1597
+ values.shift();
1598
+ const name = values.shift();
1599
+ if (!isUserSecretName(name)) throw new InvalidCommandError();
1600
+ const options = parseOptions(values, ["--project", "--environment", "--idempotency-key"]);
1601
+ return {
1602
+ kind: "secret-delete",
1603
+ projectId: options["--project"],
1604
+ environmentId: options["--environment"],
1605
+ name,
1606
+ idempotencyKey: options["--idempotency-key"],
1607
+ wait,
1608
+ credentialStore,
1609
+ };
1610
+ }
1611
+ if (first === "domain" && values[0] === "paid") {
1612
+ const subcommand = values[1];
1613
+ values.splice(0, 2);
1614
+ if (subcommand === "plan") {
1615
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1616
+ const options = parseOptions(values, ["--project", "--hostname"]);
1617
+ if (!validPaidHostname(options["--hostname"])) throw new InvalidCommandError();
1618
+ return {
1619
+ kind: "paid-domain-plan",
1620
+ projectId: options["--project"],
1621
+ hostname: options["--hostname"],
1622
+ credentialStore,
1623
+ };
1624
+ }
1625
+ if (subcommand === "status") {
1626
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1627
+ const options = parseOptions(values, ["--project"]);
1628
+ return { kind: "paid-domain-status", projectId: options["--project"], credentialStore };
1629
+ }
1630
+ if (subcommand === "apply" || subcommand === "delete") {
1631
+ if (!yes || wait || follow || stdin) throw new InvalidCommandError();
1632
+ const options = parseOptions(values, ["--project", "--hostname", "--idempotency-key"]);
1633
+ if (!validPaidHostname(options["--hostname"])) throw new InvalidCommandError();
1634
+ return {
1635
+ kind: subcommand === "apply" ? "paid-domain-apply" : "paid-domain-delete",
1636
+ projectId: options["--project"],
1637
+ hostname: options["--hostname"],
1638
+ idempotencyKey: options["--idempotency-key"],
1639
+ yes,
1640
+ credentialStore,
1641
+ };
1642
+ }
1643
+ throw new InvalidCommandError();
1644
+ }
1645
+ if (first === "domain" && values[0] === "cloudflare") {
1646
+ const subcommand = values[1];
1647
+ values.splice(0, 2);
1648
+ if (subcommand === "authorize") {
1649
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1650
+ const options = parseOptions(values, ["--project", "--zone", "--idempotency-key"]);
1651
+ if (!validPaidHostname(options["--zone"])) throw new InvalidCommandError();
1652
+ return {
1653
+ kind: "domain-cloudflare-authorize",
1654
+ projectId: options["--project"],
1655
+ zone: options["--zone"],
1656
+ idempotencyKey: options["--idempotency-key"],
1657
+ credentialStore,
1658
+ };
1659
+ }
1660
+ if (subcommand === "status") {
1661
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1662
+ const options = parseOptions(values, ["--project"]);
1663
+ return {
1664
+ kind: "domain-cloudflare-status",
1665
+ projectId: options["--project"],
1666
+ credentialStore,
1667
+ };
1668
+ }
1669
+ if (subcommand === "apply") {
1670
+ if (!yes || !wait || follow || stdin) throw new InvalidCommandError();
1671
+ const options = parseOptions(values, ["--project", "--idempotency-key"]);
1672
+ return {
1673
+ kind: "domain-cloudflare-apply",
1674
+ projectId: options["--project"],
1675
+ idempotencyKey: options["--idempotency-key"],
1676
+ yes,
1677
+ wait,
1678
+ credentialStore,
1679
+ };
1680
+ }
1681
+ throw new InvalidCommandError();
1682
+ }
1683
+ if (first === "mail" && values[0] === "domain" && values[1] === "set") {
1684
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1685
+ values.splice(0, 2);
1686
+ const options = parseOptions(values, ["--project", "--domain", "--idempotency-key"]);
1687
+ if (
1688
+ !/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(
1689
+ options["--domain"],
1690
+ )
1691
+ ) {
1692
+ throw new InvalidCommandError();
1693
+ }
1694
+ return {
1695
+ kind: "mail-domain-set",
1696
+ projectId: options["--project"],
1697
+ domain: options["--domain"],
1698
+ idempotencyKey: options["--idempotency-key"],
1699
+ credentialStore,
1700
+ };
1701
+ }
1702
+ if (first === "mail" && values[0] === "domain" && values[1] === "status") {
1703
+ if (yes || wait || follow || stdin) throw new InvalidCommandError();
1704
+ values.splice(0, 2);
1705
+ const options = parseOptions(values, ["--project"]);
1706
+ return {
1707
+ kind: "mail-domain-status",
1708
+ projectId: options["--project"],
1709
+ credentialStore,
1710
+ };
1711
+ }
1712
+ throw new InvalidCommandError();
1713
+ };
1714
+
1715
+ const removeFlag = (values: Array<string>, flag: string): boolean => {
1716
+ const index = values.indexOf(flag);
1717
+ if (index < 0) return false;
1718
+ if (values.indexOf(flag, index + 1) >= 0) throw new InvalidCommandError();
1719
+ values.splice(index, 1);
1720
+ return true;
1721
+ };
1722
+
1723
+ const parseOptions = <T extends string>(
1724
+ values: readonly string[],
1725
+ allowed: readonly T[],
1726
+ ): Record<T, string> => {
1727
+ const result = {} as Record<T, string>;
1728
+ for (let index = 0; index < values.length; index += 2) {
1729
+ const option = values[index];
1730
+ const value = values[index + 1];
1731
+ if (option === undefined || !allowed.includes(option as T) || !isValue(value)) {
1732
+ throw new InvalidCommandError();
1733
+ }
1734
+ if (option in result) throw new InvalidCommandError();
1735
+ result[option as T] = value;
1736
+ }
1737
+ if (Object.keys(result).length !== allowed.length) throw new InvalidCommandError();
1738
+ return result;
1739
+ };
1740
+
1741
+ const parseOptionalOptions = <T extends string>(
1742
+ values: readonly string[],
1743
+ allowed: readonly T[],
1744
+ ): Partial<Record<T, string>> => {
1745
+ const result: Partial<Record<T, string>> = {};
1746
+ for (let index = 0; index < values.length; index += 2) {
1747
+ const option = values[index];
1748
+ const value = values[index + 1];
1749
+ if (option === undefined || !allowed.includes(option as T) || !isValue(value)) {
1750
+ throw new InvalidCommandError();
1751
+ }
1752
+ if (option in result) throw new InvalidCommandError();
1753
+ result[option as T] = value;
1754
+ }
1755
+ return result;
1756
+ };
1757
+
1758
+ /** A credential lifetime is minutes, hours or plain seconds, always within 5 minutes and 24 hours. */
1759
+ const accessTtlSeconds = (value: string | undefined): number => {
1760
+ if (value === undefined) return 3_600;
1761
+ const match = /^(\d{1,6})(m|h)?$/u.exec(value);
1762
+ if (!match) throw new InvalidCommandError();
1763
+ const amount = Number(match[1]);
1764
+ const seconds = match[2] === "m" ? amount * 60 : match[2] === "h" ? amount * 3_600 : amount;
1765
+ if (!Number.isSafeInteger(seconds) || seconds < 300 || seconds > 86_400)
1766
+ throw new InvalidCommandError();
1767
+ return seconds;
1768
+ };
1769
+
1770
+ /** A platform organization identifier as the API returns it. */
1771
+ const ORGANIZATION_ULID = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u;
1772
+
1773
+ const isValue = (value: string | undefined): value is string =>
1774
+ value !== undefined && value.length > 0 && !value.startsWith("--");
1775
+
1776
+ const validGithubBranchName = (value: string): boolean =>
1777
+ !(
1778
+ value.length < 1 ||
1779
+ value.length > 1_024 ||
1780
+ !/^[A-Za-z0-9]/u.test(value) ||
1781
+ value.startsWith("-") ||
1782
+ value.startsWith("/") ||
1783
+ value.endsWith("/") ||
1784
+ value.endsWith(".") ||
1785
+ value.includes("//") ||
1786
+ value.includes("..") ||
1787
+ value.includes("@{") ||
1788
+ [...value].some((character) => {
1789
+ const code = character.codePointAt(0) ?? 0;
1790
+ return code <= 0x20 || code === 0x7f || "\\~^:?*[]".includes(character);
1791
+ }) ||
1792
+ value.split("/").some((component) => component.startsWith(".") || component.endsWith(".lock"))
1793
+ );
1794
+
1795
+ const validPaidHostname = (value: string): boolean =>
1796
+ /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/u.test(value) &&
1797
+ !value.endsWith(".poc.aixyte.com") &&
1798
+ value !== "omh.st" &&
1799
+ !value.endsWith(".omh.st") &&
1800
+ value !== "ohmyho.st" &&
1801
+ !value.endsWith(".ohmyho.st");
1802
+
1803
+ const isUserSecretName = (value: string | undefined): value is string =>
1804
+ isValue(value) &&
1805
+ /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) &&
1806
+ value !== "DATABASE_URL" &&
1807
+ value !== "HYPERDRIVE" &&
1808
+ value !== "BETTER_AUTH_SECRET" &&
1809
+ value !== "BETTER_AUTH_URL" &&
1810
+ value !== "OHMYHOST_MAIL_KEY" &&
1811
+ value !== "OHMYHOST_MAIL_GATEWAY_URL";