@cimplify/sdk 0.44.32 → 0.44.34

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.
@@ -15,18 +15,50 @@ var CLI_ERROR_CODE = {
15
15
  INVALID_INPUT: "INVALID_INPUT",
16
16
  ALREADY_LINKED: "ALREADY_LINKED",
17
17
  SERVER_ERROR: "SERVER_ERROR",
18
- ABORTED: "ABORTED"};
18
+ ABORTED: "ABORTED",
19
+ /** Operation needs interactive confirmation and the shell is non-interactive. */
20
+ INTERACTIVE_REQUIRED: "INTERACTIVE_REQUIRED"
21
+ };
19
22
  var EXIT_CODE = {
20
23
  OK: 0,
21
24
  ERROR: 1,
22
- SUPERSEDED: 2
25
+ SUPERSEDED: 2,
26
+ ABORTED: 3,
27
+ NOT_LINKED: 4,
28
+ ALREADY_LINKED: 5,
29
+ GIT_ERROR: 6,
30
+ INTERACTIVE_REQUIRED: 7,
31
+ PROJECT_NOT_FOUND: 8,
32
+ NETWORK_ERROR: 10,
33
+ SERVER_ERROR: 11,
34
+ TIMEOUT: 12,
35
+ NOT_LOGGED_IN: 20,
36
+ AUTH_FAILED: 21,
37
+ UNAUTHORIZED: 22,
38
+ INVALID_INPUT: 30
39
+ };
40
+ var EXIT_CODE_FOR = {
41
+ NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
42
+ NOT_LINKED: EXIT_CODE.NOT_LINKED,
43
+ NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
44
+ AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
45
+ PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
46
+ GIT_ERROR: EXIT_CODE.GIT_ERROR,
47
+ INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
48
+ ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
49
+ SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
50
+ ABORTED: EXIT_CODE.ABORTED,
51
+ UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
52
+ TIMEOUT: EXIT_CODE.TIMEOUT,
53
+ INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
23
54
  };
24
55
  var CliError = class extends Error {
25
- constructor(code, message, exitCode = EXIT_CODE.ERROR) {
56
+ constructor(code, message, options = {}) {
26
57
  super(message);
27
58
  this.name = "CliError";
28
59
  this.code = code;
29
- this.exitCode = exitCode;
60
+ this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
61
+ this.remediation = options.remediation;
30
62
  }
31
63
  };
32
64
 
@@ -281,6 +313,7 @@ var ARGS_STATUS_PORCELAIN = ["status", "--porcelain=v1"];
281
313
  var ARGS_REV_PARSE_HEAD = ["rev-parse", "HEAD"];
282
314
  var ARGS_REV_PARSE_ABBREV_REF = ["rev-parse", "--abbrev-ref", "HEAD"];
283
315
  var ARGS_REV_PARSE_SHOW_TOPLEVEL = ["rev-parse", "--show-toplevel"];
316
+ var ARGS_REMOTE_GET_URL = ["remote", "get-url"];
284
317
  var ARGS_PUSH = ["push"];
285
318
  async function runGit(args, cwd) {
286
319
  return new Promise((resolve, reject) => {
@@ -325,10 +358,27 @@ async function gitDetectRoot(cwd) {
325
358
  if (code !== 0) return null;
326
359
  return stdout.trimEnd() || null;
327
360
  }
361
+ async function gitGetRemoteUrl(cwd, name = REMOTE_DEFAULT) {
362
+ const { code, stdout } = await runGit([...ARGS_REMOTE_GET_URL, name], cwd);
363
+ if (code !== 0) return null;
364
+ return stdout.trimEnd() || null;
365
+ }
328
366
  async function gitPush(cwd, branch) {
329
367
  const args = branch ? [...ARGS_PUSH, REMOTE_DEFAULT, branch] : [...ARGS_PUSH];
330
368
  await runGitOk(args, cwd);
331
369
  }
370
+ async function gitPushToUrl(cwd, url, branch) {
371
+ await runGitOk([...ARGS_PUSH, url, branch], cwd);
372
+ }
373
+ var FREESTYLE_GIT_HOST = "git.freestyle.sh";
374
+ function isFreestyleRemote(url) {
375
+ if (!url) return false;
376
+ try {
377
+ return new URL(url).hostname === FREESTYLE_GIT_HOST;
378
+ } catch {
379
+ return url.includes(FREESTYLE_GIT_HOST);
380
+ }
381
+ }
332
382
 
333
383
  // src/cli/output.ts
334
384
  var RESET = "\x1B[0m";
@@ -339,34 +389,68 @@ var CYAN_OPEN = "\x1B[36m";
339
389
  var PREFIX_STEP = "\u25B8";
340
390
  var PREFIX_SUCCESS = "\u2713";
341
391
  var PREFIX_FAILURE = "\u2717";
392
+ var ENV_JSON = "CIMPLIFY_JSON";
393
+ var ENV_YES = "CIMPLIFY_YES";
394
+ function envFlag(name) {
395
+ const v = process.env[name];
396
+ return v === "1" || v === "true";
397
+ }
398
+ function isJsonMode() {
399
+ return envFlag(ENV_JSON);
400
+ }
401
+ function isAutoYes() {
402
+ return envFlag(ENV_YES);
403
+ }
404
+ function isInteractive() {
405
+ return Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
406
+ }
342
407
  function wrap(open, value) {
343
408
  return `${open}${value}${RESET}`;
344
409
  }
345
410
  function dim(value) {
346
- return wrap(DIM_OPEN, value);
411
+ return isJsonMode() ? value : wrap(DIM_OPEN, value);
347
412
  }
348
413
  function red(value) {
349
- return wrap(RED_OPEN, value);
414
+ return isJsonMode() ? value : wrap(RED_OPEN, value);
350
415
  }
351
416
  function green(value) {
352
- return wrap(GREEN_OPEN, value);
417
+ return isJsonMode() ? value : wrap(GREEN_OPEN, value);
353
418
  }
354
419
  function cyan(value) {
355
- return wrap(CYAN_OPEN, value);
420
+ return isJsonMode() ? value : wrap(CYAN_OPEN, value);
356
421
  }
357
422
  function step(message) {
423
+ if (isJsonMode()) return;
358
424
  console.log(`${cyan(PREFIX_STEP)} ${message}`);
359
425
  }
360
426
  function success(message) {
427
+ if (isJsonMode()) return;
361
428
  console.log(`${green(PREFIX_SUCCESS)} ${message}`);
362
429
  }
363
430
  function failure(message) {
431
+ if (isJsonMode()) return;
364
432
  console.error(`${red(PREFIX_FAILURE)} ${message}`);
365
433
  }
366
434
  function info(message) {
435
+ if (isJsonMode()) return;
367
436
  console.log(message);
368
437
  }
438
+ var ENV_EMITTED = "__CIMPLIFY_RESULT_EMITTED";
439
+ function result(data) {
440
+ if (!isJsonMode()) return;
441
+ if (process.env[ENV_EMITTED] === "1") return;
442
+ process.env[ENV_EMITTED] = "1";
443
+ process.stdout.write(`${JSON.stringify({ ok: true, data })}
444
+ `);
445
+ }
369
446
  async function promptLine(question) {
447
+ if (!isInteractive()) {
448
+ throw new CliError(
449
+ CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
450
+ "this operation needs interactive input but stdin is not a TTY",
451
+ { remediation: "run interactively, or supply the value via a flag" }
452
+ );
453
+ }
370
454
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
371
455
  try {
372
456
  return await new Promise((resolve) => {
@@ -377,6 +461,14 @@ async function promptLine(question) {
377
461
  }
378
462
  }
379
463
  async function promptYesNo(question, defaultNo = true) {
464
+ if (isAutoYes()) return true;
465
+ if (!isInteractive()) {
466
+ throw new CliError(
467
+ CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
468
+ `this operation needs confirmation: ${question}`,
469
+ { remediation: "re-run with --yes to accept, or run interactively" }
470
+ );
471
+ }
380
472
  const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
381
473
  const answer = (await promptLine(`${question}${suffix}`)).trim().toLowerCase();
382
474
  if (answer === "") return !defaultNo;
@@ -402,6 +494,16 @@ var TERMINAL_DEPLOYMENT_STATUSES = /* @__PURE__ */ new Set([
402
494
  DEPLOYMENT_STATUS.CANCELLED,
403
495
  DEPLOYMENT_STATUS.SUPERSEDED
404
496
  ]);
497
+ var REPO_PROVIDER = {
498
+ FREESTYLE: "freestyle",
499
+ GITHUB: "github",
500
+ GITEA: "gitea",
501
+ EXTERNAL: "external"
502
+ };
503
+ new Set(Object.values(REPO_PROVIDER));
504
+ var TOKEN_PURPOSE = {
505
+ EDITOR: "editor"
506
+ };
405
507
 
406
508
  // src/cli/progress.ts
407
509
  var POLL_INTERVAL_MS = 1e3;
@@ -479,6 +581,19 @@ function reportTerminal(progress) {
479
581
  }
480
582
  }
481
583
 
584
+ // src/cli/commands/repo.ts
585
+ function repoEndpoint(businessId, projectId) {
586
+ return `/v1/businesses/${encodeURIComponent(businessId)}/projects/${encodeURIComponent(projectId)}/repo`;
587
+ }
588
+ function cloneTokenEndpoint(businessId, projectId) {
589
+ return `${repoEndpoint(businessId, projectId)}/clone-token`;
590
+ }
591
+ async function fetchCloneToken(client, businessId, projectId, purpose = TOKEN_PURPOSE.EDITOR) {
592
+ return client.post(cloneTokenEndpoint(businessId, projectId), {
593
+ purpose
594
+ });
595
+ }
596
+
482
597
  // src/cli/commands/deploy.ts
483
598
  var FLAG_PROD = "prod";
484
599
  var FLAG_REF = "ref";
@@ -492,6 +607,7 @@ async function run(argv) {
492
607
  const args = parseArgs(argv);
493
608
  const auth = await readAuth();
494
609
  const link = await readProjectLink();
610
+ const client = ApiClient.fromAuth(auth);
495
611
  const cwd = process.cwd();
496
612
  const root = await gitDetectRoot(cwd);
497
613
  if (!root) {
@@ -516,8 +632,20 @@ async function run(argv) {
516
632
  const localSha = await gitCurrentSha(root);
517
633
  const gitRef = explicitRef ?? localSha;
518
634
  if (!flagBool(args, FLAG_NO_PUSH)) {
519
- step(`Pushing ${branch} to origin`);
520
- await gitPush(root, branch);
635
+ const originUrl = await gitGetRemoteUrl(root);
636
+ if (isFreestyleRemote(originUrl)) {
637
+ step(`Pushing ${branch} to Freestyle (with minted clone token)`);
638
+ const token = await fetchCloneToken(
639
+ client,
640
+ link.businessId,
641
+ link.projectId,
642
+ TOKEN_PURPOSE.EDITOR
643
+ );
644
+ await gitPushToUrl(root, token.clone_url, branch);
645
+ } else {
646
+ step(`Pushing ${branch} to origin`);
647
+ await gitPush(root, branch);
648
+ }
521
649
  } else {
522
650
  info(dim("Skipping git push (--no-push)"));
523
651
  }
@@ -528,7 +656,6 @@ async function run(argv) {
528
656
  trigger: DEPLOY_TRIGGER.CLI
529
657
  };
530
658
  step(`Triggering ${envScope} deploy for ${gitRef.slice(0, 12)}`);
531
- const client = ApiClient.fromAuth(auth);
532
659
  const enqueued = await client.post(
533
660
  deployEndpoint(link.businessId, link.projectId),
534
661
  body
@@ -548,13 +675,34 @@ async function run(argv) {
548
675
  } else {
549
676
  success(`Enqueued deployment ${enqueued.deployment_id} (sha ${enqueued.git_sha.slice(0, 12)})`);
550
677
  }
551
- if (flagBool(args, FLAG_NO_POLL)) return;
678
+ if (flagBool(args, FLAG_NO_POLL)) {
679
+ result({
680
+ deployment: {
681
+ id: enqueued.deployment_id,
682
+ git_sha: enqueued.git_sha,
683
+ existing: enqueued.existing,
684
+ environment: envScope,
685
+ status: enqueued.existing ? "in_progress" : "queued"
686
+ }
687
+ });
688
+ return;
689
+ }
552
690
  const final = await pollDeployment(
553
691
  client,
554
692
  link.businessId,
555
693
  link.projectId,
556
694
  enqueued.deployment_id
557
695
  );
696
+ result({
697
+ deployment: {
698
+ id: enqueued.deployment_id,
699
+ git_sha: enqueued.git_sha,
700
+ environment: envScope,
701
+ status: final.status,
702
+ url: final.url ?? null,
703
+ error_message: final.error_message ?? null
704
+ }
705
+ });
558
706
  if (final.status === DEPLOYMENT_STATUS.ACTIVE) {
559
707
  process.exitCode = EXIT_CODE.OK;
560
708
  return;
@@ -16,13 +16,42 @@ var CLI_ERROR_CODE = {
16
16
  ALREADY_LINKED: "ALREADY_LINKED",
17
17
  SERVER_ERROR: "SERVER_ERROR"};
18
18
  var EXIT_CODE = {
19
- ERROR: 1};
19
+ ABORTED: 3,
20
+ NOT_LINKED: 4,
21
+ ALREADY_LINKED: 5,
22
+ GIT_ERROR: 6,
23
+ INTERACTIVE_REQUIRED: 7,
24
+ PROJECT_NOT_FOUND: 8,
25
+ NETWORK_ERROR: 10,
26
+ SERVER_ERROR: 11,
27
+ TIMEOUT: 12,
28
+ NOT_LOGGED_IN: 20,
29
+ AUTH_FAILED: 21,
30
+ UNAUTHORIZED: 22,
31
+ INVALID_INPUT: 30
32
+ };
33
+ var EXIT_CODE_FOR = {
34
+ NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
35
+ NOT_LINKED: EXIT_CODE.NOT_LINKED,
36
+ NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
37
+ AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
38
+ PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
39
+ GIT_ERROR: EXIT_CODE.GIT_ERROR,
40
+ INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
41
+ ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
42
+ SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
43
+ ABORTED: EXIT_CODE.ABORTED,
44
+ UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
45
+ TIMEOUT: EXIT_CODE.TIMEOUT,
46
+ INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
47
+ };
20
48
  var CliError = class extends Error {
21
- constructor(code, message, exitCode = EXIT_CODE.ERROR) {
49
+ constructor(code, message, options = {}) {
22
50
  super(message);
23
51
  this.name = "CliError";
24
52
  this.code = code;
25
- this.exitCode = exitCode;
53
+ this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
54
+ this.remediation = options.remediation;
26
55
  }
27
56
  };
28
57
 
@@ -296,13 +325,22 @@ function formatEnvFile(entries) {
296
325
  // src/cli/output.ts
297
326
  var RESET = "\x1B[0m";
298
327
  var DIM_OPEN = "\x1B[2m";
328
+ var ENV_JSON = "CIMPLIFY_JSON";
329
+ function envFlag(name) {
330
+ const v = process.env[name];
331
+ return v === "1" || v === "true";
332
+ }
333
+ function isJsonMode() {
334
+ return envFlag(ENV_JSON);
335
+ }
299
336
  function wrap(open, value) {
300
337
  return `${open}${value}${RESET}`;
301
338
  }
302
339
  function dim(value) {
303
- return wrap(DIM_OPEN, value);
340
+ return isJsonMode() ? value : wrap(DIM_OPEN, value);
304
341
  }
305
342
  function info(message) {
343
+ if (isJsonMode()) return;
306
344
  console.log(message);
307
345
  }
308
346
 
@@ -311,6 +349,13 @@ var ENV_SCOPE = {
311
349
  PRODUCTION: "production"
312
350
  };
313
351
  var PUBLIC_ENV_PREFIX = "NEXT_PUBLIC_";
352
+ var REPO_PROVIDER = {
353
+ FREESTYLE: "freestyle",
354
+ GITHUB: "github",
355
+ GITEA: "gitea",
356
+ EXTERNAL: "external"
357
+ };
358
+ new Set(Object.values(REPO_PROVIDER));
314
359
 
315
360
  // src/cli/commands/dev.ts
316
361
  var FLAG_REMOTE = "remote";
@@ -13,15 +13,47 @@ var CLI_ERROR_CODE = {
13
13
  INVALID_INPUT: "INVALID_INPUT",
14
14
  ALREADY_LINKED: "ALREADY_LINKED",
15
15
  SERVER_ERROR: "SERVER_ERROR",
16
- ABORTED: "ABORTED"};
16
+ ABORTED: "ABORTED",
17
+ /** Operation needs interactive confirmation and the shell is non-interactive. */
18
+ INTERACTIVE_REQUIRED: "INTERACTIVE_REQUIRED"
19
+ };
17
20
  var EXIT_CODE = {
18
- ERROR: 1};
21
+ ABORTED: 3,
22
+ NOT_LINKED: 4,
23
+ ALREADY_LINKED: 5,
24
+ GIT_ERROR: 6,
25
+ INTERACTIVE_REQUIRED: 7,
26
+ PROJECT_NOT_FOUND: 8,
27
+ NETWORK_ERROR: 10,
28
+ SERVER_ERROR: 11,
29
+ TIMEOUT: 12,
30
+ NOT_LOGGED_IN: 20,
31
+ AUTH_FAILED: 21,
32
+ UNAUTHORIZED: 22,
33
+ INVALID_INPUT: 30
34
+ };
35
+ var EXIT_CODE_FOR = {
36
+ NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
37
+ NOT_LINKED: EXIT_CODE.NOT_LINKED,
38
+ NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
39
+ AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
40
+ PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
41
+ GIT_ERROR: EXIT_CODE.GIT_ERROR,
42
+ INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
43
+ ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
44
+ SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
45
+ ABORTED: EXIT_CODE.ABORTED,
46
+ UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
47
+ TIMEOUT: EXIT_CODE.TIMEOUT,
48
+ INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
49
+ };
19
50
  var CliError = class extends Error {
20
- constructor(code, message, exitCode = EXIT_CODE.ERROR) {
51
+ constructor(code, message, options = {}) {
21
52
  super(message);
22
53
  this.name = "CliError";
23
54
  this.code = code;
24
- this.exitCode = exitCode;
55
+ this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
56
+ this.remediation = options.remediation;
25
57
  }
26
58
  };
27
59
 
@@ -262,25 +294,57 @@ var BOLD_OPEN = "\x1B[1m";
262
294
  var DIM_OPEN = "\x1B[2m";
263
295
  var GREEN_OPEN = "\x1B[32m";
264
296
  var PREFIX_SUCCESS = "\u2713";
297
+ var ENV_JSON = "CIMPLIFY_JSON";
298
+ var ENV_YES = "CIMPLIFY_YES";
299
+ function envFlag(name) {
300
+ const v = process.env[name];
301
+ return v === "1" || v === "true";
302
+ }
303
+ function isJsonMode() {
304
+ return envFlag(ENV_JSON);
305
+ }
306
+ function isAutoYes() {
307
+ return envFlag(ENV_YES);
308
+ }
309
+ function isInteractive() {
310
+ return Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
311
+ }
265
312
  function wrap(open, value) {
266
313
  return `${open}${value}${RESET}`;
267
314
  }
268
315
  function bold(value) {
269
- return wrap(BOLD_OPEN, value);
316
+ return isJsonMode() ? value : wrap(BOLD_OPEN, value);
270
317
  }
271
318
  function dim(value) {
272
- return wrap(DIM_OPEN, value);
319
+ return isJsonMode() ? value : wrap(DIM_OPEN, value);
273
320
  }
274
321
  function green(value) {
275
- return wrap(GREEN_OPEN, value);
322
+ return isJsonMode() ? value : wrap(GREEN_OPEN, value);
276
323
  }
277
324
  function success(message) {
325
+ if (isJsonMode()) return;
278
326
  console.log(`${green(PREFIX_SUCCESS)} ${message}`);
279
327
  }
280
328
  function info(message) {
329
+ if (isJsonMode()) return;
281
330
  console.log(message);
282
331
  }
332
+ var ENV_EMITTED = "__CIMPLIFY_RESULT_EMITTED";
333
+ function result(data) {
334
+ if (!isJsonMode()) return;
335
+ if (process.env[ENV_EMITTED] === "1") return;
336
+ process.env[ENV_EMITTED] = "1";
337
+ process.stdout.write(`${JSON.stringify({ ok: true, data })}
338
+ `);
339
+ }
283
340
  async function promptLine(question) {
341
+ if (!isInteractive()) {
342
+ throw new CliError(
343
+ CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
344
+ "this operation needs interactive input but stdin is not a TTY",
345
+ { remediation: "run interactively, or supply the value via a flag" }
346
+ );
347
+ }
284
348
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
285
349
  try {
286
350
  return await new Promise((resolve) => {
@@ -291,6 +355,14 @@ async function promptLine(question) {
291
355
  }
292
356
  }
293
357
  async function promptYesNo(question, defaultNo = true) {
358
+ if (isAutoYes()) return true;
359
+ if (!isInteractive()) {
360
+ throw new CliError(
361
+ CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
362
+ `this operation needs confirmation: ${question}`,
363
+ { remediation: "re-run with --yes to accept, or run interactively" }
364
+ );
365
+ }
294
366
  const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
295
367
  const answer = (await promptLine(`${question}${suffix}`)).trim().toLowerCase();
296
368
  if (answer === "") return !defaultNo;
@@ -308,6 +380,13 @@ var DOMAIN_TYPE = {
308
380
  CUSTOM: "custom",
309
381
  CIMPLIFY: "cimplify"
310
382
  };
383
+ var REPO_PROVIDER = {
384
+ FREESTYLE: "freestyle",
385
+ GITHUB: "github",
386
+ GITEA: "gitea",
387
+ EXTERNAL: "external"
388
+ };
389
+ new Set(Object.values(REPO_PROVIDER));
311
390
 
312
391
  // src/cli/commands/domains.ts
313
392
  var SUB_LS = "ls";
@@ -425,6 +504,7 @@ async function listDomains(client, businessId) {
425
504
  const domains = await client.get(domainsEndpoint(businessId));
426
505
  if (domains.length === 0) {
427
506
  info(dim("No domains. Add one: cimplify domains add <domain>"));
507
+ result({ domains: [] });
428
508
  return;
429
509
  }
430
510
  const domainWidth = Math.max(COL_DOMAIN.length, ...domains.map((d) => d.domain.length));
@@ -453,6 +533,16 @@ async function listDomains(client, businessId) {
453
533
  ].join(COL_GAP)
454
534
  );
455
535
  }
536
+ result({
537
+ domains: domains.map((d) => ({
538
+ id: d.id,
539
+ domain: d.domain,
540
+ domain_type: d.domain_type,
541
+ is_verified: d.is_verified,
542
+ is_primary: d.is_primary,
543
+ created_at: d.created_at ?? null
544
+ }))
545
+ });
456
546
  }
457
547
  async function addDomain(client, businessId, args) {
458
548
  const domain = args.positional[1];
@@ -466,15 +556,25 @@ async function addDomain(client, businessId, args) {
466
556
  const body = { domain, domain_type: domainType };
467
557
  const created = await client.post(domainsEndpoint(businessId), body);
468
558
  success(`Added ${created.domain} (${created.domain_type})`);
559
+ let dnsRecords = null;
469
560
  if (domainType === DOMAIN_TYPE.CUSTOM) {
470
561
  try {
471
562
  const dns = await client.get(dnsRecordsEndpoint(domain));
472
- const records = Array.isArray(dns) ? dns : dns.records;
473
- printDnsRecords(domain, records);
563
+ dnsRecords = Array.isArray(dns) ? dns : dns.records;
564
+ printDnsRecords(domain, dnsRecords);
474
565
  } catch (err) {
475
566
  info(dim(`(Could not fetch DNS records: ${err instanceof Error ? err.message : String(err)})`));
476
567
  }
477
568
  }
569
+ result({
570
+ domain: {
571
+ id: created.id,
572
+ domain: created.domain,
573
+ domain_type: created.domain_type,
574
+ is_verified: created.is_verified
575
+ },
576
+ dns_records: dnsRecords
577
+ });
478
578
  }
479
579
  function printDnsRecords(domain, records) {
480
580
  if (!records || records.length === 0) {
@@ -508,12 +608,13 @@ async function verifyDomain(client, businessId, args) {
508
608
  );
509
609
  }
510
610
  const target = await findDomain(client, businessId, domain);
511
- const result = await client.post(domainVerifyEndpoint(businessId, target.id));
512
- if (result.is_verified) {
513
- success(`Verified ${result.domain}`);
611
+ const verified = await client.post(domainVerifyEndpoint(businessId, target.id));
612
+ if (verified.is_verified) {
613
+ success(`Verified ${verified.domain}`);
514
614
  } else {
515
615
  info(dim(`Not yet verified \u2014 DNS may still be propagating. Try again in a few minutes.`));
516
616
  }
617
+ result({ domain: verified.domain, verified: verified.is_verified });
517
618
  }
518
619
  async function removeDomain(client, businessId, args) {
519
620
  const domain = args.positional[1];
@@ -532,6 +633,7 @@ async function removeDomain(client, businessId, args) {
532
633
  }
533
634
  await client.delete(domainItemEndpoint(businessId, target.id));
534
635
  success(`Removed ${domain}`);
636
+ result({ removed: true, domain });
535
637
  }
536
638
  async function findDomain(client, businessId, domain) {
537
639
  const domains = await client.get(domainsEndpoint(businessId));
@@ -573,6 +675,7 @@ async function attachDomain(client, businessId, args) {
573
675
  );
574
676
  success(`Attached ${domain} to project for ${envScope}${isPrimary ? " (primary)" : ""}`);
575
677
  info(dim("Next deploy will bind this domain. Run: cimplify deploy"));
678
+ result({ attached: true, domain, env: envScope, primary: isPrimary });
576
679
  }
577
680
  async function detachDomain(client, businessId, args) {
578
681
  const domain = args.positional[1];
@@ -594,6 +697,7 @@ async function detachDomain(client, businessId, args) {
594
697
  await client.delete(projectDomainItemEndpoint(businessId, link.projectId, attachment.id));
595
698
  success(`Detached ${domain} (${envScope})`);
596
699
  info(dim("Next deploy will not bind this domain. Existing deployments continue serving until redeploy."));
700
+ result({ detached: true, domain, env: envScope });
597
701
  }
598
702
  async function setPrimaryAttachment(client, businessId, args) {
599
703
  const domain = args.positional[1];
@@ -610,6 +714,7 @@ async function setPrimaryAttachment(client, businessId, args) {
610
714
  projectDomainPrimaryEndpoint(businessId, link.projectId, attachment.id)
611
715
  );
612
716
  success(`Set ${domain} as primary for ${envScope}`);
717
+ result({ primary: true, domain, env: envScope });
613
718
  }
614
719
  async function listAttachedForCurrentProject(client, businessId) {
615
720
  const link = await readProjectLink();
@@ -618,6 +723,7 @@ async function listAttachedForCurrentProject(client, businessId) {
618
723
  );
619
724
  if (attachments.length === 0) {
620
725
  info(dim("No domains attached to this project. Attach one: cimplify domains attach <domain>"));
726
+ result({ attached: [] });
621
727
  return;
622
728
  }
623
729
  const domains = await client.get(domainsEndpoint(businessId));
@@ -634,6 +740,16 @@ async function listAttachedForCurrentProject(client, businessId) {
634
740
  ].join(COL_GAP)
635
741
  );
636
742
  }
743
+ result({
744
+ attached: attachments.map((a) => ({
745
+ attachment_id: a.id,
746
+ business_domain_id: a.business_domain_id,
747
+ domain: domainById.get(a.business_domain_id)?.domain ?? null,
748
+ env: a.env_scope,
749
+ primary: a.is_primary,
750
+ attached_at: a.attached_at
751
+ }))
752
+ });
637
753
  }
638
754
  async function findAttachmentForCurrentProject(client, businessId, projectId, domain, envScope) {
639
755
  const target = await findDomain(client, businessId, domain);