@hs-x/cli 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +120 -0
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +38 -10
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/command-catalog.d.ts +2 -2
  6. package/dist/command-catalog.d.ts.map +1 -1
  7. package/dist/command-catalog.js +1 -0
  8. package/dist/command-catalog.js.map +1 -1
  9. package/dist/commands/completion.d.ts.map +1 -1
  10. package/dist/commands/completion.js +268 -16
  11. package/dist/commands/completion.js.map +1 -1
  12. package/dist/commands/connect.d.ts.map +1 -1
  13. package/dist/commands/connect.js +177 -20
  14. package/dist/commands/connect.js.map +1 -1
  15. package/dist/commands/control-plane-read.d.ts +28 -0
  16. package/dist/commands/control-plane-read.d.ts.map +1 -1
  17. package/dist/commands/control-plane-read.js +134 -28
  18. package/dist/commands/control-plane-read.js.map +1 -1
  19. package/dist/commands/deploy.d.ts +5 -0
  20. package/dist/commands/deploy.d.ts.map +1 -1
  21. package/dist/commands/deploy.js +605 -102
  22. package/dist/commands/deploy.js.map +1 -1
  23. package/dist/commands/dev.d.ts +19 -1
  24. package/dist/commands/dev.d.ts.map +1 -1
  25. package/dist/commands/dev.js +1335 -83
  26. package/dist/commands/dev.js.map +1 -1
  27. package/dist/commands/doctor.d.ts.map +1 -1
  28. package/dist/commands/doctor.js +27 -10
  29. package/dist/commands/doctor.js.map +1 -1
  30. package/dist/commands/help-command.d.ts.map +1 -1
  31. package/dist/commands/help-command.js +145 -35
  32. package/dist/commands/help-command.js.map +1 -1
  33. package/dist/commands/project.d.ts +107 -0
  34. package/dist/commands/project.d.ts.map +1 -0
  35. package/dist/commands/project.js +586 -0
  36. package/dist/commands/project.js.map +1 -0
  37. package/dist/commands/status.d.ts.map +1 -1
  38. package/dist/commands/status.js +15 -5
  39. package/dist/commands/status.js.map +1 -1
  40. package/dist/commands/validate.d.ts +19 -0
  41. package/dist/commands/validate.d.ts.map +1 -1
  42. package/dist/commands/validate.js +26 -7
  43. package/dist/commands/validate.js.map +1 -1
  44. package/dist/constants.d.ts +1 -1
  45. package/dist/constants.js +1 -1
  46. package/dist/dev/account-selection.d.ts +16 -0
  47. package/dist/dev/account-selection.d.ts.map +1 -0
  48. package/dist/dev/account-selection.js +22 -0
  49. package/dist/dev/account-selection.js.map +1 -0
  50. package/dist/dev/browser-log-bridge.d.ts +32 -0
  51. package/dist/dev/browser-log-bridge.d.ts.map +1 -0
  52. package/dist/dev/browser-log-bridge.js +156 -0
  53. package/dist/dev/browser-log-bridge.js.map +1 -0
  54. package/dist/dev/compat-shim.d.ts +3 -3
  55. package/dist/dev/compat-shim.d.ts.map +1 -1
  56. package/dist/dev/compat-shim.js +7 -6
  57. package/dist/dev/compat-shim.js.map +1 -1
  58. package/dist/dev/component-selection.d.ts +25 -0
  59. package/dist/dev/component-selection.d.ts.map +1 -0
  60. package/dist/dev/component-selection.js +79 -0
  61. package/dist/dev/component-selection.js.map +1 -0
  62. package/dist/dev/console-capture.d.ts +18 -0
  63. package/dist/dev/console-capture.d.ts.map +1 -0
  64. package/dist/dev/console-capture.js +57 -0
  65. package/dist/dev/console-capture.js.map +1 -0
  66. package/dist/dev/event-bus.d.ts +1 -1
  67. package/dist/dev/event-bus.js +1 -1
  68. package/dist/dev/frontend-log-relay.d.ts +27 -0
  69. package/dist/dev/frontend-log-relay.d.ts.map +1 -0
  70. package/dist/dev/frontend-log-relay.js +102 -0
  71. package/dist/dev/frontend-log-relay.js.map +1 -0
  72. package/dist/dev/keyboard.d.ts +1 -0
  73. package/dist/dev/keyboard.d.ts.map +1 -1
  74. package/dist/dev/keyboard.js +9 -3
  75. package/dist/dev/keyboard.js.map +1 -1
  76. package/dist/dev/log-server.js +3 -0
  77. package/dist/dev/log-server.js.map +1 -1
  78. package/dist/dev/managed-session.d.ts +124 -0
  79. package/dist/dev/managed-session.d.ts.map +1 -0
  80. package/dist/dev/managed-session.js +597 -0
  81. package/dist/dev/managed-session.js.map +1 -0
  82. package/dist/dev/port-process.d.ts +21 -0
  83. package/dist/dev/port-process.d.ts.map +1 -0
  84. package/dist/dev/port-process.js +145 -0
  85. package/dist/dev/port-process.js.map +1 -0
  86. package/dist/dev/session-manager.d.ts +11 -1
  87. package/dist/dev/session-manager.d.ts.map +1 -1
  88. package/dist/dev/session-manager.js +62 -12
  89. package/dist/dev/session-manager.js.map +1 -1
  90. package/dist/dev/stream-event-format.d.ts +14 -0
  91. package/dist/dev/stream-event-format.d.ts.map +1 -0
  92. package/dist/dev/stream-event-format.js +262 -0
  93. package/dist/dev/stream-event-format.js.map +1 -0
  94. package/dist/dev/stream-filter.d.ts.map +1 -1
  95. package/dist/dev/stream-filter.js +16 -1
  96. package/dist/dev/stream-filter.js.map +1 -1
  97. package/dist/dev/stream-renderer.d.ts +4 -2
  98. package/dist/dev/stream-renderer.d.ts.map +1 -1
  99. package/dist/dev/stream-renderer.js +224 -84
  100. package/dist/dev/stream-renderer.js.map +1 -1
  101. package/dist/errors-registry.d.ts.map +1 -1
  102. package/dist/errors-registry.js +213 -0
  103. package/dist/errors-registry.js.map +1 -1
  104. package/dist/hubspot-app-logs.d.ts +38 -5
  105. package/dist/hubspot-app-logs.d.ts.map +1 -1
  106. package/dist/hubspot-app-logs.js +88 -17
  107. package/dist/hubspot-app-logs.js.map +1 -1
  108. package/dist/hubspot-auth.d.ts +19 -1
  109. package/dist/hubspot-auth.d.ts.map +1 -1
  110. package/dist/hubspot-auth.js +228 -83
  111. package/dist/hubspot-auth.js.map +1 -1
  112. package/dist/hubspot-developer-client.d.ts +87 -0
  113. package/dist/hubspot-developer-client.d.ts.map +1 -1
  114. package/dist/hubspot-developer-client.js +232 -1
  115. package/dist/hubspot-developer-client.js.map +1 -1
  116. package/dist/hubspot-project-archive.d.ts +21 -0
  117. package/dist/hubspot-project-archive.d.ts.map +1 -0
  118. package/dist/hubspot-project-archive.js +281 -0
  119. package/dist/hubspot-project-archive.js.map +1 -0
  120. package/dist/hubspot-project.d.ts +32 -0
  121. package/dist/hubspot-project.d.ts.map +1 -0
  122. package/dist/hubspot-project.js +269 -0
  123. package/dist/hubspot-project.js.map +1 -0
  124. package/dist/local-project-picker.js +1 -1
  125. package/dist/local-project-picker.js.map +1 -1
  126. package/dist/output-context.js +1 -1
  127. package/dist/output-context.js.map +1 -1
  128. package/dist/services/hubspot-dev-client.js +3 -0
  129. package/dist/services/hubspot-dev-client.js.map +1 -1
  130. package/package.json +14 -12
@@ -1,8 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { createHash, randomBytes } from 'node:crypto';
3
- import { cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rmdir, stat, unlink, writeFile, } from 'node:fs/promises';
3
+ import { lstat, mkdir, readFile, readdir, realpath, rename, rmdir, stat, unlink, writeFile, } from 'node:fs/promises';
4
4
  import { createServer } from 'node:http';
5
- import { tmpdir } from 'node:os';
6
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
7
6
  import { pathToFileURL } from 'node:url';
8
7
  import * as Command from '@effect/cli/Command';
@@ -25,7 +24,9 @@ import { createControlPlaneTenantProvisioningAuthority, } from '../control-plane
25
24
  import { captureDeployRevision } from '../deploy-revision-capture.js';
26
25
  import { resolveDeployTargets } from '../deploy-targets.js';
27
26
  import { executeCliHttp } from '../effect-http.js';
28
- import { createDeployHubSpotDeveloperClient } from '../hubspot-developer-client.js';
27
+ import { DirectHubSpotAccountResolutionError, createDeployHubSpotDeveloperClient, resolveDirectHubSpotContext, } from '../hubspot-developer-client.js';
28
+ import { HubSpotProjectArchiveSourceError, fingerprintHubSpotArchiveSource, writeLocalHubSpotProjectArchive, } from '../hubspot-project-archive.js';
29
+ import { detectLocalProjectMode, readHubSpotProjectDescriptor, validateHubSpotProject, } from '../hubspot-project.js';
29
30
  import { hydrateAncestorEnv } from '../load-env.js';
30
31
  import { LocalProjectPickerCancelled, resolveLocalProject } from '../local-project-picker.js';
31
32
  import { getMachineId, getMachineSecret, previewMachineId } from '../machine-id.js';
@@ -541,6 +542,447 @@ function workerSourceSummaries(workers) {
541
542
  function workerSyncSchemaModes(workers) {
542
543
  return workers.flatMap((worker) => worker.capabilities.flatMap((capability) => capability.kind === 'sync' ? [`${capability.id}: ${capability.manageSchema ?? false}`] : []));
543
544
  }
545
+ function includesCliFlag(argv, flag) {
546
+ return argv.some((argument) => argument === flag || argument.startsWith(`${flag}=`));
547
+ }
548
+ function directDeployAccountView(account) {
549
+ return {
550
+ id: account.id,
551
+ ...(account.name ? { name: account.name } : {}),
552
+ source: account.source,
553
+ selectedBy: account.selectedBy,
554
+ authReady: account.authReady,
555
+ ...(account.configSource ? { configSource: account.configSource } : {}),
556
+ };
557
+ }
558
+ function directDeployAccountLabel(account) {
559
+ if (!account)
560
+ return 'unresolved HubSpot account';
561
+ return account.name ? `${account.name} (${account.id})` : account.id;
562
+ }
563
+ function directDeployAccountFailure(cause) {
564
+ if (cause instanceof DirectHubSpotAccountResolutionError) {
565
+ const suffix = ` ${cause.hint}`;
566
+ return {
567
+ reasonCode: cause.code,
568
+ message: cause.message.endsWith(suffix)
569
+ ? cause.message.slice(0, -suffix.length)
570
+ : cause.message,
571
+ hint: cause.hint,
572
+ };
573
+ }
574
+ return {
575
+ reasonCode: 'HSX_E_DIRECT_HUBSPOT_ACCOUNT_CONFIG',
576
+ message: cause instanceof Error ? cause.message : String(cause),
577
+ hint: 'Fix the HubSpot CLI account configuration and retry.',
578
+ };
579
+ }
580
+ function describeHubSpotDeployFailure(cause) {
581
+ const record = isRecord(cause) ? cause : undefined;
582
+ const data = isRecord(record?.data) ? record.data : undefined;
583
+ const body = isRecord(record?.body) ? record.body : undefined;
584
+ const validationErrors = Array.isArray(record?.validationErrors)
585
+ ? record.validationErrors.filter((item) => typeof item === 'string')
586
+ : [];
587
+ const values = [
588
+ cause instanceof Error ? cause.message : String(cause),
589
+ typeof record?.status === 'number' ? `HTTP ${record.status}` : undefined,
590
+ typeof data?.message === 'string' ? data.message : undefined,
591
+ typeof body?.message === 'string' ? body.message : undefined,
592
+ typeof record?.category === 'string' ? record.category : undefined,
593
+ typeof data?.category === 'string' ? data.category : undefined,
594
+ ...validationErrors,
595
+ typeof record?.correlationId === 'string'
596
+ ? `correlation ${record.correlationId}`
597
+ : typeof data?.correlationId === 'string'
598
+ ? `correlation ${data.correlationId}`
599
+ : typeof body?.correlationId === 'string'
600
+ ? `correlation ${body.correlationId}`
601
+ : undefined,
602
+ ].filter((value) => Boolean(value));
603
+ return [...new Set(values)].join(' · ');
604
+ }
605
+ function directDeployDoneMessage(upload) {
606
+ if (upload.deploySkipped && !upload.autoDeployExpected) {
607
+ return 'Uploaded and built in HubSpot; deployment skipped';
608
+ }
609
+ if (upload.autoDeployExpected)
610
+ return 'Uploaded, built, and auto-deployed in HubSpot';
611
+ return 'Uploaded, built, and deployed in HubSpot';
612
+ }
613
+ async function hubSpotDirectDeployCommand(input) {
614
+ try {
615
+ return await executeHubSpotDirectDeployCommand(input);
616
+ }
617
+ catch (cause) {
618
+ if (!(cause instanceof HubSpotProjectArchiveSourceError))
619
+ throw cause;
620
+ return inputValidationError({
621
+ argv: input.argv,
622
+ command: 'deploy',
623
+ json: input.json,
624
+ code: cause.code,
625
+ message: cause.message,
626
+ hint: cause.hint,
627
+ detail: 'No archive was retained and no upload or deploy request was made.',
628
+ });
629
+ }
630
+ }
631
+ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
632
+ const reporter = createReporter({ command: 'deploy', argv, entry: !json });
633
+ const machineOutput = json || reporter.mode === 'ndjson';
634
+ const unsupported = [
635
+ '--account-id',
636
+ '--project-id',
637
+ '--control-plane-url',
638
+ '--build-only',
639
+ '--cloudflare-deploy',
640
+ '--cloudflare-dry-run',
641
+ '--local-control-plane',
642
+ '--record-local',
643
+ '--promote-when-healthy',
644
+ '--no-record',
645
+ '--heartbeat',
646
+ '--no-heartbeat',
647
+ '--apply-schema',
648
+ '--portal-schema-live',
649
+ '--portal-schema-fixture',
650
+ '--local-hubspot-schema',
651
+ ].find((flag) => includesCliFlag(argv, flag));
652
+ if (unsupported) {
653
+ return inputValidationError({
654
+ argv,
655
+ command: 'deploy',
656
+ json,
657
+ code: 'HSX_E_INPUT_INVALID',
658
+ message: `${unsupported} is an HS-X project feature.`,
659
+ hint: 'Remove the flag to deploy this native HubSpot project directly to HubSpot.',
660
+ detail: 'HubSpot-direct mode never contacts the HS-X control plane or Cloudflare.',
661
+ });
662
+ }
663
+ const preparationFingerprint = await fingerprintHubSpotArchiveSource(root, false);
664
+ const [descriptor, validation, projectName, intermediateRepresentation] = await Promise.all([
665
+ readHubSpotProjectDescriptor(root),
666
+ validateHubSpotProject({ root }),
667
+ resolveHubSpotProjectName(argv, root),
668
+ readHubSpotProjectIntermediateRepresentation(root),
669
+ ]);
670
+ const appUid = readAppUidFromIntermediateRepresentation(intermediateRepresentation) ??
671
+ (await readAppUidFromProjectSource(root));
672
+ const sourceFingerprint = validation.ok
673
+ ? await fingerprintHubSpotArchiveSource(root, intermediateRepresentation !== undefined)
674
+ : preparationFingerprint;
675
+ // Enclose every source-derived preparation read (validation, descriptor,
676
+ // IR, app uid, and scoped fingerprint) in one full-tree stability check.
677
+ // A later edit is still caught against sourceFingerprint in the staged copy.
678
+ if ((await fingerprintHubSpotArchiveSource(root, false)) !== preparationFingerprint) {
679
+ return inputValidationError({
680
+ argv,
681
+ command: 'deploy',
682
+ json,
683
+ code: 'HSX_E_INPUT_INVALID',
684
+ message: 'HubSpot project sources changed while preparing the deploy plan.',
685
+ hint: 'Wait for local edits or generators to settle, then review a fresh plan.',
686
+ detail: 'No upload or deploy request was made.',
687
+ });
688
+ }
689
+ const uploadOnly = argv.includes('--hubspot-upload-only') || argv.includes('--skip-deploy');
690
+ const planOnly = argv.includes('--plan') || argv.includes('--dry-run');
691
+ const localHubSpotAdapter = argv.includes('--local-hubspot-upload');
692
+ const platformVersion = resolveFlag(argv, '--platform-version') ?? descriptor.platformVersion ?? '2026.03';
693
+ const buildMessage = resolveFlag(argv, '--message') ?? 'HS-X HubSpot-direct deploy';
694
+ const buildTimeoutMs = Number(resolveFlag(argv, '--hubspot-build-timeout-ms') ?? '60000');
695
+ const force = argv.includes('--force');
696
+ let account;
697
+ let accountFailure;
698
+ let preparedUpload;
699
+ if (localHubSpotAdapter) {
700
+ account = {
701
+ id: resolveFlag(argv, '--developer-account-id') ?? 'local',
702
+ name: 'local HubSpot adapter',
703
+ source: 'local-adapter',
704
+ selectedBy: 'local-adapter',
705
+ authReady: true,
706
+ };
707
+ preparedUpload = {
708
+ projectName,
709
+ intermediateRepresentation,
710
+ appUid,
711
+ developerAccountId: resolveFlag(argv, '--developer-account-id') ?? process.env.HSX_HUBSPOT_DEVELOPER_ACCOUNT_ID,
712
+ platformVersion,
713
+ buildMessage,
714
+ buildTimeoutMs,
715
+ force,
716
+ sourceFingerprint,
717
+ createClient: () => createDeployHubSpotDeveloperClient({
718
+ argv,
719
+ local: true,
720
+ allowedOperations: ['hubspot-project-upload'],
721
+ cwd: root,
722
+ }),
723
+ };
724
+ }
725
+ else {
726
+ try {
727
+ const context = await resolveDirectHubSpotContext({ argv, cwd: root });
728
+ account = directDeployAccountView(context.account);
729
+ if (!account.authReady) {
730
+ accountFailure = {
731
+ reasonCode: 'HSX_E_DIRECT_HUBSPOT_AUTH_REQUIRED',
732
+ message: `HubSpot account ${directDeployAccountLabel(account)} does not have deploy authentication available.`,
733
+ hint: 'Run `hs account auth` for that account, or pass --pak for this run.',
734
+ };
735
+ }
736
+ else {
737
+ preparedUpload = {
738
+ projectName,
739
+ intermediateRepresentation,
740
+ appUid,
741
+ developerAccountId: context.account.id,
742
+ platformVersion,
743
+ buildMessage,
744
+ buildTimeoutMs,
745
+ force,
746
+ sourceFingerprint,
747
+ createClient: async () => context.createClient(),
748
+ };
749
+ }
750
+ }
751
+ catch (cause) {
752
+ accountFailure = directDeployAccountFailure(cause);
753
+ }
754
+ }
755
+ const ready = validation.ok &&
756
+ account !== undefined &&
757
+ accountFailure === undefined &&
758
+ preparedUpload !== undefined;
759
+ const plan = {
760
+ schema_version: 1,
761
+ ok: ready,
762
+ command: 'deploy',
763
+ mode: 'hubspot-direct',
764
+ root,
765
+ project: {
766
+ name: projectName,
767
+ ...(projectName !== descriptor.name ? { configuredName: descriptor.name } : {}),
768
+ platformVersion,
769
+ srcDir: descriptor.srcDir,
770
+ sourceFingerprint,
771
+ ...(descriptor.appUid ? { appUid: descriptor.appUid } : {}),
772
+ },
773
+ account: account
774
+ ? { ...account, ...(accountFailure ? { error: accountFailure } : {}) }
775
+ : {
776
+ authReady: false,
777
+ error: accountFailure,
778
+ },
779
+ targets: { hubspot: true, cloudflare: false, controlPlane: false },
780
+ action: uploadOnly ? 'upload-and-build' : 'upload-build-and-deploy',
781
+ options: {
782
+ force,
783
+ buildMessage,
784
+ buildTimeoutMs,
785
+ skipAutoDeploy: uploadOnly,
786
+ },
787
+ validation,
788
+ };
789
+ const rows = [
790
+ { key: 'mode', value: 'HubSpot direct' },
791
+ { key: 'source', value: root },
792
+ { key: 'project', value: projectName },
793
+ ...(account
794
+ ? [
795
+ {
796
+ key: 'account',
797
+ value: directDeployAccountLabel(account),
798
+ status: account.authReady ? 'ok' : 'fail',
799
+ detail: account.configSource ?? account.selectedBy,
800
+ ...(account.authReady
801
+ ? {}
802
+ : { hint: 'Run `hs account auth` for this account, then retry.' }),
803
+ },
804
+ ]
805
+ : [
806
+ {
807
+ key: 'account',
808
+ value: 'not ready',
809
+ status: 'fail',
810
+ ...(accountFailure?.reasonCode ? { detail: accountFailure.reasonCode } : {}),
811
+ ...(accountFailure?.hint ? { hint: accountFailure.hint } : {}),
812
+ },
813
+ ]),
814
+ { key: 'platform', value: platformVersion },
815
+ { key: 'build message', value: buildMessage },
816
+ { key: 'build timeout', value: `${buildTimeoutMs}ms` },
817
+ {
818
+ key: 'HubSpot',
819
+ value: uploadOnly ? 'upload + build only' : 'upload + build + deploy',
820
+ status: 'ok',
821
+ },
822
+ { key: 'auto-deploy', value: uploadOnly ? 'explicitly skipped' : 'project setting' },
823
+ { key: 'force', value: force ? 'yes' : 'no', ...(force ? { status: 'warn' } : {}) },
824
+ { key: 'Cloudflare', value: 'not used' },
825
+ { key: 'HS-X control plane', value: 'not used' },
826
+ {
827
+ key: 'validation',
828
+ value: validation.ok ? 'pass' : 'fail',
829
+ status: validation.ok ? 'ok' : 'fail',
830
+ },
831
+ ...validation.diagnostics.map((diagnostic) => ({
832
+ key: diagnostic.code,
833
+ value: diagnostic.message,
834
+ status: diagnostic.severity === 'error' ? 'fail' : 'warn',
835
+ ...(diagnostic.file ? { detail: diagnostic.file } : {}),
836
+ })),
837
+ ];
838
+ if (planOnly || !ready) {
839
+ if (json) {
840
+ write(`${JSON.stringify(plan, null, 2)}\n`);
841
+ }
842
+ else if (reporter.mode === 'ndjson') {
843
+ reporter.data(plan);
844
+ reporter.done(ready ? 'Plan ready' : 'Plan not ready', ready ? 0 : 10);
845
+ }
846
+ else {
847
+ reporter.header(projectName);
848
+ reporter.rows(rows);
849
+ reporter.done(ready ? 'Plan ready' : 'Plan not ready', ready ? 0 : 10);
850
+ }
851
+ return { exitCode: ready ? 0 : validation.ok ? 10 : 1 };
852
+ }
853
+ const summary = [
854
+ 'HubSpot-direct deploy plan',
855
+ `Project: ${projectName}`,
856
+ `Account: ${directDeployAccountLabel(account)}`,
857
+ `Source: ${root}`,
858
+ `Action: ${uploadOnly ? 'upload and build only (skip auto-deploy)' : 'upload, wait for build, and deploy'}`,
859
+ `Platform: ${platformVersion}`,
860
+ `Force: ${force ? 'yes' : 'no'}`,
861
+ `Build message: ${buildMessage}`,
862
+ `Build timeout: ${buildTimeoutMs}ms`,
863
+ 'Cloudflare: not used',
864
+ 'HS-X control plane: not used',
865
+ ].join('\n');
866
+ // MutationConsent requires the exact read-only preview to be visible before
867
+ // it asks. In machine modes the same plan is available via --plan; --yes is
868
+ // mandatory and no interactive output is emitted.
869
+ if (!machineOutput) {
870
+ reporter.header(projectName);
871
+ reporter.rows(rows);
872
+ }
873
+ const consent = await requestMutationConsent({
874
+ yes: argv.includes('--yes') || argv.includes('-y'),
875
+ machineOutput,
876
+ interactive: isInteractive(),
877
+ message: `Deploy ${projectName} to ${directDeployAccountLabel(account)}?`,
878
+ confirm: promptConfirm,
879
+ });
880
+ if (consent === 'require-yes') {
881
+ return inputValidationError({
882
+ argv,
883
+ command: 'deploy',
884
+ json,
885
+ code: 'HSX_E_INPUT_INVALID',
886
+ message: 'Deploy requires explicit consent in machine or non-interactive mode.',
887
+ hint: 'Review with `hs-x deploy --plan`, then re-run with --yes.',
888
+ detail: summary,
889
+ });
890
+ }
891
+ if (consent === 'cancelled')
892
+ return cancelledResult(argv, 'deploy');
893
+ if (!preparedUpload) {
894
+ return inputValidationError({
895
+ argv,
896
+ command: 'deploy',
897
+ json,
898
+ code: 'HSX_E_DIRECT_HUBSPOT_ACCOUNT_CONFIG',
899
+ message: 'The HubSpot deploy target changed while preparing the execution plan.',
900
+ hint: 'Review a fresh plan and retry.',
901
+ detail: 'No upload or deploy request was made.',
902
+ });
903
+ }
904
+ try {
905
+ const step = machineOutput ? undefined : reporter.step('Uploading to HubSpot');
906
+ const uploaded = await executeHubSpotOnlyUpload({
907
+ argv,
908
+ root,
909
+ local: localHubSpotAdapter,
910
+ persistBinding: false,
911
+ uploadOnly,
912
+ prepared: preparedUpload,
913
+ });
914
+ const buildState = readStatus(uploaded.buildStatus) ?? 'UNKNOWN';
915
+ const deployState = uploaded.deployStatus
916
+ ? (readStatus(uploaded.deployStatus) ?? 'UNKNOWN')
917
+ : undefined;
918
+ const deployUnverified = uploaded.autoDeployExpected && deployState === undefined;
919
+ const ok = buildState === 'SUCCESS' && !deployUnverified && (!deployState || deployState === 'SUCCESS');
920
+ step?.[ok ? 'ok' : 'fail'](`build #${uploaded.buildId} ${buildState}${deployState ? ` · deploy ${deployState}` : deployUnverified ? ' · deploy unverified' : ''}`);
921
+ const result = {
922
+ schema_version: 1,
923
+ ok,
924
+ command: 'deploy',
925
+ mode: 'hubspot-direct',
926
+ account,
927
+ projectName: uploaded.projectName,
928
+ appId: uploaded.appId || undefined,
929
+ buildId: uploaded.buildId,
930
+ buildStatus: buildState,
931
+ ...(uploaded.deployId !== undefined ? { deployId: uploaded.deployId } : {}),
932
+ ...(deployState ? { deployStatus: deployState } : {}),
933
+ deploySkipped: uploaded.deploySkipped,
934
+ deployVerification: deployUnverified ? 'unverified' : deployState ? 'verified' : 'skipped',
935
+ controlPlaneUsed: false,
936
+ cloudflareUsed: false,
937
+ };
938
+ if (json) {
939
+ write(`${JSON.stringify(result, null, 2)}\n`);
940
+ }
941
+ else if (reporter.mode === 'ndjson') {
942
+ reporter.data(result);
943
+ reporter.done(ok ? directDeployDoneMessage(uploaded) : 'HubSpot deploy failed', ok ? 0 : 1);
944
+ }
945
+ else {
946
+ streamHubSpotUploadOutcome(reporter, uploaded);
947
+ if (deployUnverified) {
948
+ reporter.error('HSX_E_DEPLOY_HUBSPOT', 'HubSpot auto-deploy could not be verified before the timeout.', { hint: 'Check the project deploy status in HubSpot, then retry if it did not ship.' });
949
+ }
950
+ reporter.done(ok ? directDeployDoneMessage(uploaded) : 'HubSpot deploy failed', ok ? 0 : 1);
951
+ }
952
+ return { exitCode: ok ? 0 : 1 };
953
+ }
954
+ catch (cause) {
955
+ if (cause instanceof HubSpotProjectArchiveSourceError)
956
+ throw cause;
957
+ const code = cause instanceof DirectHubSpotAccountResolutionError
958
+ ? 'HSX_E_DEPLOY_HUBSPOT_AUTH'
959
+ : 'HSX_E_DEPLOY_HUBSPOT';
960
+ const message = describeHubSpotDeployFailure(cause);
961
+ const hint = cause instanceof DirectHubSpotAccountResolutionError
962
+ ? cause.hint
963
+ : 'Review the HubSpot status and correlation id above, fix the reported issue, and retry.';
964
+ if (json) {
965
+ write(`${JSON.stringify({
966
+ schema_version: 1,
967
+ command: 'deploy',
968
+ ok: false,
969
+ error: { code, message, hint },
970
+ }, null, 2)}\n`);
971
+ }
972
+ else if (reporter.mode === 'ndjson') {
973
+ reporter.error(code, message, { hint });
974
+ reporter.done('HubSpot deploy failed', 1);
975
+ }
976
+ else {
977
+ reporter.error(code, message, {
978
+ hint,
979
+ docs_url: `https://hs-x.dev/errors/${code}`,
980
+ });
981
+ reporter.done('HubSpot deploy failed', 1);
982
+ }
983
+ return { exitCode: 1 };
984
+ }
985
+ }
544
986
  export async function deployCommand({ argv, root, json, }) {
545
987
  // Stamp the whole invocation up front: the human summary reporter is created
546
988
  // late (render time), so without this "Deployed … in 1ms" measured only the
@@ -567,6 +1009,9 @@ export async function deployCommand({ argv, root, json, }) {
567
1009
  throw error;
568
1010
  }
569
1011
  }
1012
+ if ((await detectLocalProjectMode(root)) === 'hubspot-direct') {
1013
+ return hubSpotDirectDeployCommand({ argv, root, json });
1014
+ }
570
1015
  // bun only auto-loads .env/.env.local from the CWD; when deploying from a
571
1016
  // nested app dir, pull in a repo-root .env.local (CLOUDFLARE_ACCOUNT_ID, etc.)
572
1017
  // so the deploy doesn't fail for vars that are set at the root.
@@ -3773,44 +4218,56 @@ function runCloudflareCommand(command, options) {
3773
4218
  },
3774
4219
  })));
3775
4220
  }
3776
- async function executeHubSpotOnlyUpload({ argv, root, local, uploadOnly, }) {
4221
+ async function executeHubSpotOnlyUpload({ argv, root, local, persistBinding = true, uploadOnly, prepared, }) {
3777
4222
  // The HubSpot project name must match the name baked into the generated
3778
4223
  // bundle (hsproject.json + app-hsmeta uid), not the on-disk directory name.
3779
4224
  // Using basename(root) breaks when the project lives in a generic dir (e.g.
3780
4225
  // `app/`): the upload targets a project named "app" while the bundle declares
3781
4226
  // the real project id, and HubSpot rejects the mismatch with an opaque 400.
3782
- const projectName = await resolveHubSpotProjectName(argv, root);
3783
- const intermediateRepresentation = await readHubSpotProjectIntermediateRepresentation(root);
3784
- const appUid = readAppUidFromIntermediateRepresentation(intermediateRepresentation) ??
4227
+ const projectName = prepared?.projectName ?? (await resolveHubSpotProjectName(argv, root));
4228
+ const intermediateRepresentation = prepared?.intermediateRepresentation ??
4229
+ (await readHubSpotProjectIntermediateRepresentation(root));
4230
+ const appUid = prepared?.appUid ??
4231
+ readAppUidFromIntermediateRepresentation(intermediateRepresentation) ??
3785
4232
  (await readAppUidFromProjectSource(root));
3786
4233
  // For the post-deploy app Auth URL (unlinked installs). Resolved from
3787
4234
  // flag/env here so the URL is correct on the unlinked path; on a
3788
4235
  // control-plane lease path it stays undefined and the URL is skipped.
3789
- const developerAccountId = resolveFlag(argv, '--developer-account-id') ?? process.env.HSX_HUBSPOT_DEVELOPER_ACCOUNT_ID;
4236
+ const developerAccountId = prepared?.developerAccountId ??
4237
+ resolveFlag(argv, '--developer-account-id') ??
4238
+ process.env.HSX_HUBSPOT_DEVELOPER_ACCOUNT_ID;
3790
4239
  const archivePath = await writeLocalHubSpotProjectArchive(root, projectName, {
3791
4240
  sourceOnly: intermediateRepresentation !== undefined,
4241
+ ...(prepared ? { expectedSourceFingerprint: prepared.sourceFingerprint } : {}),
3792
4242
  });
3793
- const client = await createDeployHubSpotDeveloperClient({
3794
- argv,
3795
- local,
3796
- allowedOperations: ['hubspot-project-upload'],
3797
- cwd: root,
3798
- });
4243
+ const client = prepared
4244
+ ? await prepared.createClient()
4245
+ : await createDeployHubSpotDeveloperClient({
4246
+ argv,
4247
+ local,
4248
+ allowedOperations: ['hubspot-project-upload'],
4249
+ cwd: root,
4250
+ });
3799
4251
  await client.projects.ensureProject({ projectName });
3800
4252
  const hsproject = await readHubSpotProjectConfig(root);
3801
4253
  const configuredPlatformVersion = typeof hsproject.platformVersion === 'string' ? hsproject.platformVersion : undefined;
3802
4254
  const upload = await client.projects.upload({
3803
4255
  projectName,
3804
4256
  archivePath,
3805
- message: resolveFlag(argv, '--message') ?? 'HS-X HubSpot-only deploy',
3806
- platformVersion: resolveFlag(argv, '--platform-version') ?? configuredPlatformVersion ?? '2026.03',
4257
+ message: prepared?.buildMessage ?? resolveFlag(argv, '--message') ?? 'HS-X HubSpot-only deploy',
4258
+ platformVersion: prepared?.platformVersion ??
4259
+ resolveFlag(argv, '--platform-version') ??
4260
+ configuredPlatformVersion ??
4261
+ '2026.03',
3807
4262
  intermediateRepresentation,
4263
+ skipAutoDeploy: uploadOnly,
3808
4264
  });
4265
+ const timeoutMs = prepared?.buildTimeoutMs ?? Number(resolveFlag(argv, '--hubspot-build-timeout-ms') ?? '60000');
3809
4266
  const buildStatus = await waitForHubSpotBuildStatus({
3810
4267
  client,
3811
4268
  projectName,
3812
4269
  buildId: upload.buildId,
3813
- timeoutMs: Number(resolveFlag(argv, '--hubspot-build-timeout-ms') ?? '60000'),
4270
+ timeoutMs,
3814
4271
  intervalMs: 1000,
3815
4272
  });
3816
4273
  const autoDeployEnabled = readBooleanProperty(buildStatus, 'isAutoDeployEnabled');
@@ -3818,7 +4275,7 @@ async function executeHubSpotOnlyUpload({ argv, root, local, uploadOnly, }) {
3818
4275
  const appId = readStatus(buildStatus) === 'SUCCESS'
3819
4276
  ? await optionalHubSpotAppId(client, projectName, appUid)
3820
4277
  : 0;
3821
- if (appId)
4278
+ if (persistBinding && appId)
3822
4279
  await persistHubSpotAppBinding(root, {
3823
4280
  appId,
3824
4281
  projectName,
@@ -3827,10 +4284,17 @@ async function executeHubSpotOnlyUpload({ argv, root, local, uploadOnly, }) {
3827
4284
  // Build SUCCESS does NOT mean the app shipped: HubSpot's auto-deploy can
3828
4285
  // still fail (found live: "Apps are not allowed to have 0 redirect URLs"
3829
4286
  // failed deploy #4 while the CLI reported green). Verify the deploy too.
3830
- // Auto-deploy fires on uploads too verify it regardless of uploadOnly
3831
- // (the f1-data 0-redirect failure rode exactly this path).
3832
- const autoDeployStatus = autoDeployEnabled && readStatus(buildStatus) === 'SUCCESS'
3833
- ? await waitForHubSpotAutoDeploy({ client, projectName, buildStatus })
4287
+ // Normal uploads can auto-deploy; uploadOnly sends skipAutoDeploy and is
4288
+ // therefore a strict build-only result.
4289
+ const autoDeployExpected = !uploadOnly && autoDeployEnabled === true && readStatus(buildStatus) === 'SUCCESS';
4290
+ const autoDeployStatus = autoDeployExpected
4291
+ ? await waitForHubSpotAutoDeploy({
4292
+ client,
4293
+ projectName,
4294
+ buildId: upload.buildId,
4295
+ buildStatus,
4296
+ timeoutMs,
4297
+ })
3834
4298
  : undefined;
3835
4299
  return {
3836
4300
  projectName,
@@ -3843,27 +4307,37 @@ async function executeHubSpotOnlyUpload({ argv, root, local, uploadOnly, }) {
3843
4307
  buildStatus,
3844
4308
  deployStatus: autoDeployStatus,
3845
4309
  deploySkipped: Boolean(uploadOnly || autoDeployEnabled),
4310
+ autoDeployExpected,
3846
4311
  local,
3847
4312
  };
3848
4313
  }
3849
4314
  const deploy = await withTransientDeployRetry(() => client.projects.deployBuild({
3850
4315
  projectName,
3851
4316
  buildId: upload.buildId,
3852
- force: argv.includes('--force'),
4317
+ force: prepared?.force ?? argv.includes('--force'),
3853
4318
  }));
3854
- const deployId = readNumericProperty(deploy, 'deployId');
3855
- const deployStatus = deployId === undefined
3856
- ? deploy
3857
- : await client.projects.getDeployStatus({
3858
- projectName,
3859
- deployId,
3860
- });
3861
- const appId = await optionalHubSpotAppId(client, projectName, appUid);
3862
- await persistHubSpotAppBinding(root, {
3863
- appId,
4319
+ const deployResultType = readStringProperty(deploy, 'buildResultType');
4320
+ if (deployResultType === 'DEPLOY_BLOCKED') {
4321
+ throw new Error(describeBlockedHubSpotDeploy(deploy));
4322
+ }
4323
+ const deployId = readNumericProperty(deploy, 'deployId') ?? readNumericProperty(deploy, 'id');
4324
+ if (deployId === undefined) {
4325
+ throw new Error(`HubSpot returned ${deployResultType ?? 'an unrecognized response'} without a deploy id.`);
4326
+ }
4327
+ const deployStatus = await waitForHubSpotDeployStatus({
4328
+ client,
3864
4329
  projectName,
3865
- developerAccountId,
4330
+ deployId,
4331
+ timeoutMs,
3866
4332
  });
4333
+ const appId = await optionalHubSpotAppId(client, projectName, appUid);
4334
+ if (persistBinding) {
4335
+ await persistHubSpotAppBinding(root, {
4336
+ appId,
4337
+ projectName,
4338
+ developerAccountId,
4339
+ });
4340
+ }
3867
4341
  return {
3868
4342
  projectName,
3869
4343
  archivePath,
@@ -3875,6 +4349,7 @@ async function executeHubSpotOnlyUpload({ argv, root, local, uploadOnly, }) {
3875
4349
  buildStatus,
3876
4350
  deployStatus,
3877
4351
  deploySkipped: false,
4352
+ autoDeployExpected: false,
3878
4353
  local,
3879
4354
  };
3880
4355
  }
@@ -3892,20 +4367,26 @@ async function optionalHubSpotAppId(client, projectName, appUid) {
3892
4367
  * (deployStatusTaskLocator.id); absent/transient lookups resolve undefined —
3893
4368
  * the caller treats that as "could not verify", never as success.
3894
4369
  */
3895
- async function waitForHubSpotAutoDeploy({ client, projectName, buildStatus, timeoutMs = 60_000, intervalMs = 2_000, }) {
3896
- const locator = buildStatus && typeof buildStatus === 'object'
3897
- ? buildStatus.deployStatusTaskLocator
3898
- : undefined;
3899
- const deployId = locator && typeof locator.id === 'number'
3900
- ? locator.id
3901
- : locator && typeof locator.id === 'string'
3902
- ? Number.parseInt(locator.id, 10)
3903
- : undefined;
3904
- if (deployId === undefined || Number.isNaN(deployId))
3905
- return undefined;
4370
+ async function waitForHubSpotAutoDeploy({ client, projectName, buildId, buildStatus, timeoutMs = 60_000, intervalMs = 2_000, }) {
3906
4371
  const deadline = Date.now() + timeoutMs;
4372
+ let latestBuildStatus = buildStatus;
4373
+ let deployId = readHubSpotDeployLocator(latestBuildStatus);
3907
4374
  let last;
3908
4375
  while (Date.now() < deadline) {
4376
+ if (deployId === undefined) {
4377
+ try {
4378
+ latestBuildStatus = await client.projects.getBuildStatus({ projectName, buildId });
4379
+ deployId = readHubSpotDeployLocator(latestBuildStatus);
4380
+ }
4381
+ catch {
4382
+ // The build may be visible before its auto-deploy locator. Keep trying
4383
+ // until the same bounded timeout instead of declaring success.
4384
+ }
4385
+ if (deployId === undefined) {
4386
+ await sleep(intervalMs);
4387
+ continue;
4388
+ }
4389
+ }
3909
4390
  try {
3910
4391
  last = await client.projects.getDeployStatus({ projectName, deployId });
3911
4392
  }
@@ -3913,13 +4394,47 @@ async function waitForHubSpotAutoDeploy({ client, projectName, buildStatus, time
3913
4394
  last = undefined;
3914
4395
  }
3915
4396
  const status = readStatus(last);
3916
- if (status && status !== 'PENDING' && status !== 'BUILDING' && status !== 'DEPLOYING') {
4397
+ if (status && !isHubSpotDeployInProgress(status)) {
3917
4398
  return last;
3918
4399
  }
3919
4400
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
3920
4401
  }
3921
4402
  return last;
3922
4403
  }
4404
+ async function waitForHubSpotDeployStatus({ client, projectName, deployId, timeoutMs, intervalMs = 1_000, }) {
4405
+ const deadline = Date.now() + timeoutMs;
4406
+ let latest;
4407
+ do {
4408
+ latest = await client.projects.getDeployStatus({ projectName, deployId });
4409
+ const status = readStatus(latest);
4410
+ if (status && !isHubSpotDeployInProgress(status))
4411
+ return latest;
4412
+ await sleep(intervalMs);
4413
+ } while (Date.now() < deadline);
4414
+ return latest;
4415
+ }
4416
+ function isHubSpotDeployInProgress(status) {
4417
+ return new Set([
4418
+ 'PENDING',
4419
+ 'QUEUED',
4420
+ 'BUILDING',
4421
+ 'DEPLOYING',
4422
+ 'PROCESSING',
4423
+ 'STARTED',
4424
+ 'IN_PROGRESS',
4425
+ ]).has(status.toUpperCase());
4426
+ }
4427
+ function readHubSpotDeployLocator(value) {
4428
+ if (!isRecord(value) || !isRecord(value.deployStatusTaskLocator))
4429
+ return undefined;
4430
+ const id = value.deployStatusTaskLocator.id;
4431
+ if (typeof id === 'number')
4432
+ return id;
4433
+ if (typeof id !== 'string')
4434
+ return undefined;
4435
+ const parsed = Number.parseInt(id, 10);
4436
+ return Number.isNaN(parsed) ? undefined : parsed;
4437
+ }
3923
4438
  async function waitForHubSpotBuildStatus({ client, projectName, buildId, timeoutMs, intervalMs, }) {
3924
4439
  const startedAt = Date.now();
3925
4440
  let latest;
@@ -3936,38 +4451,6 @@ async function waitForHubSpotBuildStatus({ client, projectName, buildId, timeout
3936
4451
  function sleep(ms) {
3937
4452
  return new Promise((resolve) => setTimeout(resolve, ms));
3938
4453
  }
3939
- async function writeLocalHubSpotProjectArchive(root, projectName, options = { sourceOnly: false }) {
3940
- const outDir = await mkdtemp(join(tmpdir(), 'hs-x-hubspot-project-'));
3941
- const archivePath = join(outDir, `${projectName.replace(/[^a-zA-Z0-9._-]+/g, '-')}-hubspot-project.zip`);
3942
- const stageDir = join(outDir, 'project');
3943
- await cp(root, stageDir, {
3944
- recursive: true,
3945
- filter(source) {
3946
- return (!source.includes(`${join(root, 'node_modules')}/`) &&
3947
- !source.includes(`${join(root, '.hs-x')}/`));
3948
- },
3949
- });
3950
- await patchHubSpotProjectName(stageDir, projectName);
3951
- await runZip(options.sourceOnly ? await hubSpotProjectSourceRoot(stageDir) : stageDir, archivePath);
3952
- return archivePath;
3953
- }
3954
- async function hubSpotProjectSourceRoot(root) {
3955
- const hsproject = await readHubSpotProjectConfig(root);
3956
- const srcDir = typeof hsproject.srcDir === 'string' ? hsproject.srcDir : 'src';
3957
- return join(root, srcDir);
3958
- }
3959
- async function patchHubSpotProjectName(root, projectName) {
3960
- const path = join(root, 'hsproject.json');
3961
- const raw = await readFile(path, 'utf8').catch(() => undefined);
3962
- if (!raw) {
3963
- return;
3964
- }
3965
- const parsed = JSON.parse(raw);
3966
- if (!isRecord(parsed)) {
3967
- return;
3968
- }
3969
- await writeFile(path, `${JSON.stringify({ ...parsed, name: projectName }, null, 2)}\n`);
3970
- }
3971
4454
  async function readHubSpotProjectIntermediateRepresentation(root) {
3972
4455
  const hsproject = await readHubSpotProjectConfig(root);
3973
4456
  const srcDir = typeof hsproject.srcDir === 'string' ? hsproject.srcDir : 'src';
@@ -4078,30 +4561,40 @@ async function collectHubSpotMetaFiles(root) {
4078
4561
  await walk(root);
4079
4562
  return files.sort();
4080
4563
  }
4081
- function runZip(root, archivePath) {
4082
- return new Promise((resolve, reject) => {
4083
- const child = spawn('zip', ['-qr', archivePath, '.', '-x', 'node_modules/*', '*/node_modules/*', '.hs-x/*'], {
4084
- cwd: root,
4085
- stdio: ['ignore', 'ignore', 'pipe'],
4086
- });
4087
- let stderr = '';
4088
- child.stderr?.setEncoding('utf8');
4089
- child.stderr?.on('data', (chunk) => {
4090
- stderr += chunk;
4091
- });
4092
- child.on('error', reject);
4093
- child.on('close', (code) => {
4094
- if (code === 0) {
4095
- resolve();
4096
- }
4097
- else {
4098
- reject(new Error(`Unable to create HubSpot project zip archive: ${stderr.trim()}`));
4099
- }
4564
+ function readNumericProperty(value, key) {
4565
+ if (!isRecord(value))
4566
+ return undefined;
4567
+ const property = value[key];
4568
+ if (typeof property === 'number')
4569
+ return property;
4570
+ if (typeof property !== 'string')
4571
+ return undefined;
4572
+ const parsed = Number.parseInt(property, 10);
4573
+ return Number.isNaN(parsed) ? undefined : parsed;
4574
+ }
4575
+ function readStringProperty(value, key) {
4576
+ return isRecord(value) && typeof value[key] === 'string' ? value[key] : undefined;
4577
+ }
4578
+ function describeBlockedHubSpotDeploy(value) {
4579
+ if (!isRecord(value))
4580
+ return 'HubSpot blocked the deploy.';
4581
+ const candidates = [value.issues, value.blockingIssues, value.errors, value.message];
4582
+ const details = candidates.flatMap((candidate) => {
4583
+ if (typeof candidate === 'string')
4584
+ return [candidate];
4585
+ if (!Array.isArray(candidate))
4586
+ return [];
4587
+ return candidate.map((item) => {
4588
+ if (typeof item === 'string')
4589
+ return item;
4590
+ if (!isRecord(item))
4591
+ return String(item);
4592
+ return String(item.message ?? item.reason ?? item.description ?? JSON.stringify(item));
4100
4593
  });
4101
4594
  });
4102
- }
4103
- function readNumericProperty(value, key) {
4104
- return isRecord(value) && typeof value[key] === 'number' ? value[key] : undefined;
4595
+ return details.length > 0
4596
+ ? `HubSpot blocked the deploy: ${details.join('; ')}`
4597
+ : 'HubSpot blocked the deploy. Re-run with --force only after reviewing its removal warnings.';
4105
4598
  }
4106
4599
  function readBooleanProperty(value, key) {
4107
4600
  return isRecord(value) && typeof value[key] === 'boolean' ? value[key] : undefined;
@@ -4844,8 +5337,12 @@ const deployOptions = {
4844
5337
  hubspotClientId: Options.text('hubspot-client-id').pipe(Options.optional), // --hubspot-client-id
4845
5338
  hubspotClientSecret: Options.text('hubspot-client-secret').pipe(Options.optional), // --hubspot-client-secret
4846
5339
  developerAccountId: Options.text('developer-account-id').pipe(Options.optional), // --developer-account-id
5340
+ developerApiKey: Options.text('developer-api-key').pipe(Options.optional), // --developer-api-key
5341
+ pak: Options.text('pak').pipe(Options.optional), // --pak
5342
+ account: Options.text('account').pipe(Options.withAlias('a'), Options.optional), // --account / -a
5343
+ config: Options.text('config').pipe(Options.withAlias('c'), Options.optional), // --config / -c
4847
5344
  hubspotBuildTimeoutMs: Options.text('hubspot-build-timeout-ms').pipe(Options.optional), // --hubspot-build-timeout-ms
4848
- message: Options.text('message').pipe(Options.optional), // --message
5345
+ message: Options.text('message').pipe(Options.withAlias('m'), Options.optional), // --message / -m
4849
5346
  platformVersion: Options.text('platform-version').pipe(Options.optional), // --platform-version
4850
5347
  hubspotProjectName: Options.text('hubspot-project-name').pipe(Options.optional), // --hubspot-project-name
4851
5348
  // ── boolean flags (argv.includes) ──
@@ -4866,13 +5363,14 @@ const deployOptions = {
4866
5363
  applySchema: Options.boolean('apply-schema').pipe(Options.withDefault(false)), // --apply-schema
4867
5364
  heartbeat: Options.boolean('heartbeat').pipe(Options.withDefault(false)), // --heartbeat
4868
5365
  noHeartbeat: Options.boolean('no-heartbeat').pipe(Options.withDefault(false)), // --no-heartbeat
5366
+ useEnv: Options.boolean('use-env').pipe(Options.withDefault(false)), // --use-env
4869
5367
  // Declared explicitly: without it @effect/cli treats `--no-record` as the
4870
5368
  // negation of an undeclared `record` boolean and fails routing with a
4871
5369
  // confusing root-level "Invalid subcommand" error (the flag is documented).
4872
5370
  noRecord: Options.boolean('no-record').pipe(Options.withDefault(false)), // --no-record
4873
5371
  portalSchemaLive: Options.boolean('portal-schema-live').pipe(Options.withDefault(false)), // --portal-schema-live
4874
5372
  localHubspotSchema: Options.boolean('local-hubspot-schema').pipe(Options.withDefault(false)), // --local-hubspot-schema
4875
- force: Options.boolean('force').pipe(Options.withDefault(false)), // --force
5373
+ force: Options.boolean('force').pipe(Options.withAlias('f'), Options.withDefault(false)), // --force / -f
4876
5374
  yes: Options.boolean('yes').pipe(Options.withAlias('y'), Options.withDefault(false)), // --yes / -y
4877
5375
  };
4878
5376
  const deployBaseCmd = Command.make('deploy', deployOptions, (opts) => runHandler('deploy', { json: opts.json }, Effect.gen(function* () {
@@ -4904,6 +5402,10 @@ const deployBaseCmd = Command.make('deploy', deployOptions, (opts) => runHandler
4904
5402
  push('--hubspot-client-id', opts.hubspotClientId);
4905
5403
  push('--hubspot-client-secret', opts.hubspotClientSecret);
4906
5404
  push('--developer-account-id', opts.developerAccountId);
5405
+ push('--developer-api-key', opts.developerApiKey);
5406
+ push('--pak', opts.pak);
5407
+ push('--account', opts.account);
5408
+ push('--config', opts.config);
4907
5409
  push('--hubspot-build-timeout-ms', opts.hubspotBuildTimeoutMs);
4908
5410
  push('--message', opts.message);
4909
5411
  push('--platform-version', opts.platformVersion);
@@ -4925,6 +5427,7 @@ const deployBaseCmd = Command.make('deploy', deployOptions, (opts) => runHandler
4925
5427
  flag('--apply-schema', opts.applySchema);
4926
5428
  flag('--heartbeat', opts.heartbeat);
4927
5429
  flag('--no-heartbeat', opts.noHeartbeat);
5430
+ flag('--use-env', opts.useEnv);
4928
5431
  flag('--no-record', opts.noRecord);
4929
5432
  flag('--portal-schema-live', opts.portalSchemaLive);
4930
5433
  flag('--local-hubspot-schema', opts.localHubspotSchema);