@hs-x/cli 0.4.6 → 0.4.7
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.
- package/README.md +6 -5
- package/dist/account-store.d.ts +9 -0
- package/dist/account-store.d.ts.map +1 -1
- package/dist/account-store.js +11 -0
- package/dist/account-store.js.map +1 -1
- package/dist/cloudflare-kv.d.ts +9 -0
- package/dist/cloudflare-kv.d.ts.map +1 -1
- package/dist/cloudflare-kv.js +16 -7
- package/dist/cloudflare-kv.js.map +1 -1
- package/dist/cloudflare-oauth.js +2 -2
- package/dist/cloudflare-oauth.js.map +1 -1
- package/dist/cloudflare-scoped-resource.d.ts +17 -2
- package/dist/cloudflare-scoped-resource.d.ts.map +1 -1
- package/dist/cloudflare-scoped-resource.js +46 -6
- package/dist/cloudflare-scoped-resource.js.map +1 -1
- package/dist/commands/api.d.ts.map +1 -1
- package/dist/commands/api.js +1 -1
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/connect.js +3 -3
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/control-plane-read.d.ts +2 -2
- package/dist/commands/control-plane-read.d.ts.map +1 -1
- package/dist/commands/control-plane-read.js +35 -22
- package/dist/commands/control-plane-read.js.map +1 -1
- package/dist/commands/deploy-promote.js +3 -3
- package/dist/commands/deploy-promote.js.map +1 -1
- package/dist/commands/deploy.d.ts +17 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +265 -30
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +7 -9
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/link.d.ts.map +1 -1
- package/dist/commands/link.js +41 -7
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/rollback.d.ts +23 -0
- package/dist/commands/rollback.d.ts.map +1 -1
- package/dist/commands/rollback.js +60 -13
- package/dist/commands/rollback.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +4 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +24 -9
- package/dist/commands/sync.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/control-plane-fetch.d.ts +3 -1
- package/dist/control-plane-fetch.d.ts.map +1 -1
- package/dist/control-plane-fetch.js +9 -7
- package/dist/control-plane-fetch.js.map +1 -1
- package/dist/deploy-targets.d.ts +0 -1
- package/dist/deploy-targets.d.ts.map +1 -1
- package/dist/deploy-targets.js +2 -5
- package/dist/deploy-targets.js.map +1 -1
- package/dist/dev/invoke.d.ts +7 -0
- package/dist/dev/invoke.d.ts.map +1 -1
- package/dist/dev/invoke.js +18 -5
- package/dist/dev/invoke.js.map +1 -1
- package/dist/errors-registry.js +8 -8
- package/dist/errors-registry.js.map +1 -1
- package/dist/hubspot-developer-client.js +2 -2
- package/dist/hubspot-developer-client.js.map +1 -1
- package/dist/services/control-plane.d.ts +3 -1
- package/dist/services/control-plane.d.ts.map +1 -1
- package/dist/services/control-plane.js +8 -6
- package/dist/services/control-plane.js.map +1 -1
- package/dist/services/hubspot-dev-client.js +1 -1
- package/dist/services/hubspot-dev-client.js.map +1 -1
- package/dist/tenant-state.d.ts +12 -2
- package/dist/tenant-state.d.ts.map +1 -1
- package/dist/tenant-state.js +42 -18
- package/dist/tenant-state.js.map +1 -1
- package/package.json +7 -7
package/dist/commands/deploy.js
CHANGED
|
@@ -16,9 +16,9 @@ import { isLinked } from '../account-store.js';
|
|
|
16
16
|
import { exitWith } from '../cli-error.js';
|
|
17
17
|
import { accountIdOption, controlPlaneUrlOption, cwdOption, deployIdOption, forceOption, jsonOption, projectIdOption, runHandler, runQuarantined, userIdOption, yesOption, } from '../cli/kit.js';
|
|
18
18
|
import { classifyCloudflareCommandFailure, preflightCloudflareAnalyticsEngine, validateCloudflareResourceForAccount, } from '../cloudflare-account-preflight.js';
|
|
19
|
-
import { resolveCloudflareCredentials } from '../cloudflare-kv.js';
|
|
19
|
+
import { applyLeasedCloudflareCredentials, resolveCloudflareCredentials, } from '../cloudflare-kv.js';
|
|
20
20
|
import { loadCloudflarePointer } from '../cloudflare-pointer.js';
|
|
21
|
-
import { resolveOrProvisionCloudflareScopedResource } from '../cloudflare-scoped-resource.js';
|
|
21
|
+
import { parseWranglerJsonArray, resolveOrProvisionCloudflareScopedResource, } from '../cloudflare-scoped-resource.js';
|
|
22
22
|
import { DEFAULT_CONTROL_PLANE_URL, formatConfigUrl, loadCliConfig, resolveControlPlaneUrl, } from '../config.js';
|
|
23
23
|
import { CLI_VERSION } from '../constants.js';
|
|
24
24
|
import { controlPlaneAuthHeaders } from '../control-plane-fetch.js';
|
|
@@ -31,6 +31,7 @@ import { DirectHubSpotAccountResolutionError, createDeployHubSpotDeveloperClient
|
|
|
31
31
|
import { HubSpotProjectArchiveSourceError, fingerprintHubSpotArchiveSource, writeLocalHubSpotProjectArchive, } from '../hubspot-project-archive.js';
|
|
32
32
|
import { deployHubSpotProjectBuild, readHubSpotSubbuildStatuses, waitForHubSpotAutoDeploy, waitForHubSpotBuildStatus, } from '../hubspot-project-lifecycle.js';
|
|
33
33
|
import { detectLocalProjectMode, readHubSpotProjectDescriptor, validateHubSpotProject, } from '../hubspot-project.js';
|
|
34
|
+
import { linkMigrationJournalId, loadLinkMigrationJournal } from '../link-migration-journal.js';
|
|
34
35
|
import { hydrateAncestorEnv } from '../load-env.js';
|
|
35
36
|
import { LocalProjectPickerCancelled, resolveLocalProject } from '../local-project-picker.js';
|
|
36
37
|
import { getMachineId, getMachineSecret, previewMachineId } from '../machine-id.js';
|
|
@@ -42,7 +43,7 @@ import { createReporter } from '../reporter/index.js';
|
|
|
42
43
|
import { Cwd } from '../services/cwd.js';
|
|
43
44
|
import { createTenantDataPlaneProvisioningCoordinator, } from '../tenant-data-plane-provisioning.js';
|
|
44
45
|
import { ensureTenantStateStore, preflightTenantStateCredentials, } from '../tenant-state.js';
|
|
45
|
-
import { defaultTokenKeyCustodyAdapter, escrowTokenKey, resolveEscrowedTokenKey, tokenKeyCustodyIdFor, } from '../token-key-custody.js';
|
|
46
|
+
import { defaultTokenKeyCustodyAdapter, escrowTokenKey, resolveEscrowedTokenKey, sealTokenKeyCanary, tokenKeyCustodyAdapterFor, tokenKeyCustodyIdFor, } from '../token-key-custody.js';
|
|
46
47
|
import { deployPromoteCommand, requestControlPlaneDeployPromotion } from './deploy-promote.js';
|
|
47
48
|
import { withTransientDeployRetry } from './deploy-retry.js';
|
|
48
49
|
async function hostedHttp(input) {
|
|
@@ -494,11 +495,13 @@ async function hydrateCloudflareTokenFromControlPlane(input) {
|
|
|
494
495
|
const lease = await leaseCloudflareViaSession(input.argv, input.root);
|
|
495
496
|
if (!lease)
|
|
496
497
|
return false;
|
|
497
|
-
|
|
498
|
-
|
|
498
|
+
applyLeasedCloudflareCredentials({
|
|
499
|
+
apiToken: lease.apiToken,
|
|
500
|
+
accountId: lease.cloudflareAccountId,
|
|
501
|
+
});
|
|
499
502
|
return true;
|
|
500
503
|
}
|
|
501
|
-
async function waitForHealthyControlPlaneDrift({ controlPlaneUrl, projectId, deployId, userId, timeoutMs, workerUrl, }) {
|
|
504
|
+
async function waitForHealthyControlPlaneDrift({ controlPlaneUrl, accountId, projectId, deployId, userId, timeoutMs, workerUrl, }) {
|
|
502
505
|
const startedAt = Date.now();
|
|
503
506
|
let lastState = 'unknown';
|
|
504
507
|
let lastReason;
|
|
@@ -508,7 +511,7 @@ async function waitForHealthyControlPlaneDrift({ controlPlaneUrl, projectId, dep
|
|
|
508
511
|
}
|
|
509
512
|
const response = await hostedHttp({
|
|
510
513
|
url: new URL(`/v1/projects/${encodeURIComponent(projectId)}/drift`, controlPlaneUrl),
|
|
511
|
-
headers: await controlPlaneAuthHeaders(userId),
|
|
514
|
+
headers: await controlPlaneAuthHeaders(userId, undefined, accountId),
|
|
512
515
|
});
|
|
513
516
|
const body = await response.json().catch(() => undefined);
|
|
514
517
|
if (response.ok) {
|
|
@@ -976,7 +979,7 @@ async function executeHubSpotDirectDeployCommand({ argv, root, json, }) {
|
|
|
976
979
|
? (readStatus(uploaded.deployStatus) ?? 'UNKNOWN')
|
|
977
980
|
: undefined;
|
|
978
981
|
const deployUnverified = uploaded.autoDeployExpected && deployState === undefined;
|
|
979
|
-
const ok =
|
|
982
|
+
const ok = hubSpotUploadSucceeded(uploaded);
|
|
980
983
|
const result = {
|
|
981
984
|
schema_version: 1,
|
|
982
985
|
ok,
|
|
@@ -1101,8 +1104,14 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1101
1104
|
const noHeartbeat = argv.includes('--no-heartbeat');
|
|
1102
1105
|
const noRecord = argv.includes('--no-record');
|
|
1103
1106
|
const explicitControlPlaneUrl = resolveFlag(argv, '--control-plane-url');
|
|
1104
|
-
const controlPlaneUrl = explicitControlPlaneUrl ?? (planOnly ? undefined : process.env.HSX_CONTROL_PLANE_URL);
|
|
1105
1107
|
const linked = await isLinked();
|
|
1108
|
+
const controlPlaneUrl = resolveDeployControlPlaneUrl({
|
|
1109
|
+
explicitControlPlaneUrl,
|
|
1110
|
+
environmentControlPlaneUrl: process.env.HSX_CONTROL_PLANE_URL,
|
|
1111
|
+
platformControlPlaneUrl: resolveControlPlaneUrl(argv),
|
|
1112
|
+
linked,
|
|
1113
|
+
planOnly,
|
|
1114
|
+
});
|
|
1106
1115
|
const userId = resolveFlag(argv, '--user-id') ?? process.env.HSX_USER_ID ?? 'local-cli-user';
|
|
1107
1116
|
const resolvedAcct = await resolveAccountIdOrPrompt({
|
|
1108
1117
|
argv,
|
|
@@ -1156,7 +1165,6 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1156
1165
|
const needsHubSpotBundle = needsRuntime || app.mcpServers.length > 0;
|
|
1157
1166
|
const { deploymentMode, cloudflareDeployRequested, hubspotUploadRequested } = resolveDeployTargets({
|
|
1158
1167
|
buildOnly,
|
|
1159
|
-
planOnly,
|
|
1160
1168
|
needsRuntime,
|
|
1161
1169
|
hasProjectId: Boolean(projectId),
|
|
1162
1170
|
hubspotOnly,
|
|
@@ -1468,7 +1476,17 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1468
1476
|
// Worker origin is resolvable up front, upload BEFORE the Cloudflare deploy so
|
|
1469
1477
|
// the minted app id binds in a single pass. Falls back to the original
|
|
1470
1478
|
// deploy-then-upload order whenever the origin can't be resolved.
|
|
1471
|
-
const appIdKnownBeforeDeploy =
|
|
1479
|
+
const appIdKnownBeforeDeploy = controlPlanePlan && controlPlaneBackedPlan && controlPlaneUrl
|
|
1480
|
+
? await resolveHubSpotAppIdForDeploy({
|
|
1481
|
+
argv,
|
|
1482
|
+
root,
|
|
1483
|
+
controlPlaneUrl,
|
|
1484
|
+
userId,
|
|
1485
|
+
accountId: controlPlanePlan.accountId,
|
|
1486
|
+
projectId: controlPlanePlan.projectId,
|
|
1487
|
+
environment: resolveFlag(argv, '--env') ?? resolveFlag(argv, '--environment') ?? 'production',
|
|
1488
|
+
})
|
|
1489
|
+
: (readOptionalHubSpotAppId(resolveFlag(argv, '--hubspot-app-id') ?? process.env.HSX_HUBSPOT_APP_ID) ?? (await readPersistedHubSpotAppId(root, await resolveHubSpotProjectName(argv, root))));
|
|
1472
1490
|
const uploadFirstOrigin = cloudflareDeployRequested &&
|
|
1473
1491
|
!cloudflareDryRun &&
|
|
1474
1492
|
!planOnly &&
|
|
@@ -1617,6 +1635,10 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1617
1635
|
plan: controlPlanePlan,
|
|
1618
1636
|
promoteWhenHealthy,
|
|
1619
1637
|
...(capturedRevision ? { revision: capturedRevision } : {}),
|
|
1638
|
+
...(() => {
|
|
1639
|
+
const deployedWorkerUrl = cloudflareDeployResult?.workers.find((worker) => worker.url)?.url;
|
|
1640
|
+
return deployedWorkerUrl ? { workerUrl: deployedWorkerUrl } : {};
|
|
1641
|
+
})(),
|
|
1620
1642
|
})
|
|
1621
1643
|
: controlPlaneUrl
|
|
1622
1644
|
? await requestHostedControlPlaneDeployRecordAndMaybePromote({
|
|
@@ -1715,7 +1737,7 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1715
1737
|
// run-008 build #2's failure mode. Only a SUCCESS status counts.
|
|
1716
1738
|
const hubspotUploadFailed = !planOnly &&
|
|
1717
1739
|
hubspotUploadRequested &&
|
|
1718
|
-
(!hubspotUploadResult ||
|
|
1740
|
+
(!hubspotUploadResult || !hubSpotUploadSucceeded(hubspotUploadResult));
|
|
1719
1741
|
const cloudflareDeployed = !planOnly && cloudflareDeployRequested && Boolean(cloudflareDeployResult);
|
|
1720
1742
|
// A live worker with only the HubSpot upload failing: not a clean success,
|
|
1721
1743
|
// but distinct from total failure so callers don't tear down a good worker.
|
|
@@ -1831,6 +1853,16 @@ export async function deployCommand({ argv, root, json, }) {
|
|
|
1831
1853
|
// automation doesn't treat a serving worker as a total failure), 1 = failure.
|
|
1832
1854
|
return { exitCode: plan.ok ? 0 : plan.partial ? 20 : 1 };
|
|
1833
1855
|
}
|
|
1856
|
+
/** @internal Exported for the linked-default deploy regression suite. */
|
|
1857
|
+
export function resolveDeployControlPlaneUrl(input) {
|
|
1858
|
+
if (input.explicitControlPlaneUrl)
|
|
1859
|
+
return input.explicitControlPlaneUrl;
|
|
1860
|
+
if (input.planOnly)
|
|
1861
|
+
return undefined;
|
|
1862
|
+
if (input.linked)
|
|
1863
|
+
return input.platformControlPlaneUrl;
|
|
1864
|
+
return input.environmentControlPlaneUrl;
|
|
1865
|
+
}
|
|
1834
1866
|
function buildDeployPlanSummary(input) {
|
|
1835
1867
|
const lines = ['', 'Planned changes:'];
|
|
1836
1868
|
const capCount = input.workers.reduce((c, w) => c + w.capabilities.length, 0);
|
|
@@ -2701,6 +2733,14 @@ function defaultHubSpotFieldType(type) {
|
|
|
2701
2733
|
return 'text';
|
|
2702
2734
|
}
|
|
2703
2735
|
}
|
|
2736
|
+
/** @internal Exported for deterministic deploy-outcome regression tests. */
|
|
2737
|
+
export function hubSpotUploadSucceeded(upload) {
|
|
2738
|
+
if (readStatus(upload.buildStatus) !== 'SUCCESS')
|
|
2739
|
+
return false;
|
|
2740
|
+
if (upload.deployStatus !== undefined)
|
|
2741
|
+
return readStatus(upload.deployStatus) === 'SUCCESS';
|
|
2742
|
+
return !upload.autoDeployExpected;
|
|
2743
|
+
}
|
|
2704
2744
|
async function executeCloudflareDeploy({ argv, root, workers, controlPlanePlan, controlPlaneUrl, userId, machineId, machineSecret, heartbeatEnabled, dryRun, note, }) {
|
|
2705
2745
|
// Fail fast on missing Cloudflare credentials BEFORE deploying any Worker, so
|
|
2706
2746
|
// an unlinked deploy can't leave a Worker live and then error on the
|
|
@@ -2733,7 +2773,15 @@ async function executeCloudflareDeploy({ argv, root, workers, controlPlanePlan,
|
|
|
2733
2773
|
const appConfig = await readHsxAppConfig(root);
|
|
2734
2774
|
const environment = resolveFlag(argv, '--env') ?? resolveFlag(argv, '--environment') ?? 'production';
|
|
2735
2775
|
const authUrl = await resolveHubSpotAppAuthUrl(argv, root);
|
|
2736
|
-
const hubspotAppId = requireHubSpotCardAppId(
|
|
2776
|
+
const hubspotAppId = requireHubSpotCardAppId(await resolveHubSpotAppIdForDeploy({
|
|
2777
|
+
argv,
|
|
2778
|
+
root,
|
|
2779
|
+
...(controlPlaneUrl ? { controlPlaneUrl } : {}),
|
|
2780
|
+
userId,
|
|
2781
|
+
accountId: controlPlanePlan.accountId,
|
|
2782
|
+
projectId: controlPlanePlan.projectId,
|
|
2783
|
+
environment,
|
|
2784
|
+
}), authUrl);
|
|
2737
2785
|
const stored = controlPlaneUrl
|
|
2738
2786
|
? await readDeployHubSpotOAuthSecret({
|
|
2739
2787
|
controlPlaneUrl,
|
|
@@ -2770,7 +2818,7 @@ async function executeCloudflareDeploy({ argv, root, workers, controlPlanePlan,
|
|
|
2770
2818
|
? {
|
|
2771
2819
|
authority: createControlPlaneTenantProvisioningAuthority({
|
|
2772
2820
|
controlPlaneUrl,
|
|
2773
|
-
authorizationHeaders: () => controlPlaneAuthHeaders(userId),
|
|
2821
|
+
authorizationHeaders: () => controlPlaneAuthHeaders(userId, undefined, controlPlanePlan.accountId),
|
|
2774
2822
|
http: hostedHttp,
|
|
2775
2823
|
validateCompleteResources: (resources) => validateTenantDataPlaneResourcesForActiveAccount(argv, root, resources),
|
|
2776
2824
|
}),
|
|
@@ -3294,9 +3342,15 @@ async function ensureTenantDeployInstallRuntimeBinding(input) {
|
|
|
3294
3342
|
hubSpotAppId: input.hubSpotAppId,
|
|
3295
3343
|
},
|
|
3296
3344
|
kind: 'install-kv',
|
|
3345
|
+
legacyResourceNameExists: (resourceName) => cloudflareResourceNameExists({
|
|
3346
|
+
argv: input.argv,
|
|
3347
|
+
root: input.root,
|
|
3348
|
+
kind: 'install-kv',
|
|
3349
|
+
resourceName,
|
|
3350
|
+
}),
|
|
3297
3351
|
async provisionByName(installKvNamespaceName) {
|
|
3298
3352
|
// ADR-022: generate and escrow the token key before creating Cloudflare
|
|
3299
|
-
// state,
|
|
3353
|
+
// state, after long-name recovery ambiguity has already been ruled out.
|
|
3300
3354
|
const tokenKeySecretValue = randomBytes(32).toString('base64url');
|
|
3301
3355
|
const tokenKeyCustody = await escrowTokenKey({
|
|
3302
3356
|
adapter: defaultTokenKeyCustodyAdapter(),
|
|
@@ -3332,7 +3386,7 @@ async function ensureTenantDeployInstallRuntimeBinding(input) {
|
|
|
3332
3386
|
if (!namespaceOutput) {
|
|
3333
3387
|
const listed = await runCloudflareCommand(['bun', 'x', 'wrangler', 'kv', 'namespace', 'list'], { cwd: input.root, env: cloudflareDeployEnv(input.argv) });
|
|
3334
3388
|
try {
|
|
3335
|
-
const namespaces =
|
|
3389
|
+
const namespaces = parseWranglerJsonArray(listed.stdout);
|
|
3336
3390
|
resourceId = namespaces.find((namespace) => namespace.title === installKvNamespaceName)?.id;
|
|
3337
3391
|
}
|
|
3338
3392
|
catch {
|
|
@@ -3372,6 +3426,9 @@ async function ensureTenantDeployInstallRuntimeBinding(input) {
|
|
|
3372
3426
|
};
|
|
3373
3427
|
}
|
|
3374
3428
|
async function ensureDeployInstallRuntimeBinding(input) {
|
|
3429
|
+
const preserved = await recoverCompletedLinkInstallRuntimeBinding(input);
|
|
3430
|
+
if (preserved)
|
|
3431
|
+
return preserved;
|
|
3375
3432
|
const existing = await readDeployInstallRuntimeBinding(input);
|
|
3376
3433
|
if (existing) {
|
|
3377
3434
|
const credentials = resolveCloudflareCredentials(input.argv);
|
|
@@ -3413,9 +3470,15 @@ async function ensureDeployInstallRuntimeBinding(input) {
|
|
|
3413
3470
|
hubSpotAppId: input.hubSpotAppId,
|
|
3414
3471
|
},
|
|
3415
3472
|
kind: 'install-kv',
|
|
3473
|
+
legacyResourceNameExists: (resourceName) => cloudflareResourceNameExists({
|
|
3474
|
+
argv: input.argv,
|
|
3475
|
+
root: input.root,
|
|
3476
|
+
kind: 'install-kv',
|
|
3477
|
+
resourceName,
|
|
3478
|
+
}),
|
|
3416
3479
|
async provisionByName(installKvNamespaceName) {
|
|
3417
|
-
// ADR-022: escrow precedes Cloudflare mutation, while the
|
|
3418
|
-
// guard precedes both. The control plane never receives key bytes.
|
|
3480
|
+
// ADR-022: escrow precedes Cloudflare mutation, while the legacy-name
|
|
3481
|
+
// ambiguity guard precedes both. The control plane never receives key bytes.
|
|
3419
3482
|
const tokenKeySecretValue = randomBytes(32).toString('base64url');
|
|
3420
3483
|
const tokenKeyCustody = await escrowTokenKey({
|
|
3421
3484
|
adapter: defaultTokenKeyCustodyAdapter(),
|
|
@@ -3451,7 +3514,7 @@ async function ensureDeployInstallRuntimeBinding(input) {
|
|
|
3451
3514
|
if (!namespaceOutput) {
|
|
3452
3515
|
const listed = await runCloudflareCommand(['bun', 'x', 'wrangler', 'kv', 'namespace', 'list'], { cwd: input.root, env: cloudflareDeployEnv(input.argv) });
|
|
3453
3516
|
try {
|
|
3454
|
-
const namespaces =
|
|
3517
|
+
const namespaces = parseWranglerJsonArray(listed.stdout);
|
|
3455
3518
|
resourceId = namespaces.find((namespace) => namespace.title === installKvNamespaceName)?.id;
|
|
3456
3519
|
}
|
|
3457
3520
|
catch {
|
|
@@ -3485,10 +3548,106 @@ async function ensureDeployInstallRuntimeBinding(input) {
|
|
|
3485
3548
|
tokenKeyCustody,
|
|
3486
3549
|
};
|
|
3487
3550
|
}
|
|
3551
|
+
/**
|
|
3552
|
+
* A completed ADR-022 link journal is the authoritative preserve-set for the
|
|
3553
|
+
* first linked redeploy. Rehydrate its exact INSTALL_KV and re-keyed token-key
|
|
3554
|
+
* custody before consulting account-scoped control-plane metadata, which may
|
|
3555
|
+
* not have been backfilled yet. This prevents link from silently provisioning
|
|
3556
|
+
* a replacement namespace or key.
|
|
3557
|
+
*/
|
|
3558
|
+
async function recoverCompletedLinkInstallRuntimeBinding(input) {
|
|
3559
|
+
const pointer = await loadCloudflarePointer(input.root);
|
|
3560
|
+
if (!pointer || !pointer.ownerId.startsWith('acct_'))
|
|
3561
|
+
return undefined;
|
|
3562
|
+
const journal = await loadLinkMigrationJournal(linkMigrationJournalId({
|
|
3563
|
+
cloudflareAccountId: pointer.cloudflareAccountId,
|
|
3564
|
+
stateKvNamespaceId: pointer.stateKvNamespaceId,
|
|
3565
|
+
newOwnerId: pointer.ownerId,
|
|
3566
|
+
}));
|
|
3567
|
+
if (!journal || journal.status !== 'done')
|
|
3568
|
+
return undefined;
|
|
3569
|
+
const binding = journal.bindings.find((candidate) => candidate.projectId === input.projectId &&
|
|
3570
|
+
candidate.environment === input.environment &&
|
|
3571
|
+
candidate.hubSpotAppId === input.hubSpotAppId);
|
|
3572
|
+
if (!binding)
|
|
3573
|
+
return undefined;
|
|
3574
|
+
if (input.dryRun) {
|
|
3575
|
+
return {
|
|
3576
|
+
installKvNamespaceId: binding.installKvNamespaceId,
|
|
3577
|
+
installKvNamespaceName: binding.installKvNamespaceName,
|
|
3578
|
+
tokenKeySecretName: binding.tokenKeySecretName,
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
const valid = await validateCloudflareResourceForDeploy({
|
|
3582
|
+
argv: input.argv,
|
|
3583
|
+
root: input.root,
|
|
3584
|
+
kind: 'install-kv',
|
|
3585
|
+
resourceId: binding.installKvNamespaceId,
|
|
3586
|
+
});
|
|
3587
|
+
if (!valid) {
|
|
3588
|
+
throw new Error(`The completed link journal's preserved install KV ${binding.installKvNamespaceId} is no longer present in the connected Cloudflare account.`);
|
|
3589
|
+
}
|
|
3590
|
+
const custodyMechanism = Schema.decodeUnknownSync(schemas.TokenKeyCustodyMechanism)(binding.custodyMechanism);
|
|
3591
|
+
const adapter = tokenKeyCustodyAdapterFor(custodyMechanism);
|
|
3592
|
+
const tokenKeySecretValue = await adapter.retrieve(binding.newCustodyId);
|
|
3593
|
+
if (!tokenKeySecretValue) {
|
|
3594
|
+
throw new Error(`The completed link journal's re-keyed token custody ${binding.newCustodyId} is unavailable.`);
|
|
3595
|
+
}
|
|
3596
|
+
const tokenKeyCustody = Schema.decodeUnknownSync(schemas.EscrowedTokenKeyCustody)({
|
|
3597
|
+
kind: 'escrowed',
|
|
3598
|
+
mechanism: custodyMechanism,
|
|
3599
|
+
custodyId: binding.newCustodyId,
|
|
3600
|
+
escrowedAt: journal.updatedAt,
|
|
3601
|
+
canary: await sealTokenKeyCanary(tokenKeySecretValue),
|
|
3602
|
+
});
|
|
3603
|
+
await putDeployInstallRuntimeBinding({
|
|
3604
|
+
...input,
|
|
3605
|
+
installKvNamespaceId: binding.installKvNamespaceId,
|
|
3606
|
+
installKvNamespaceName: binding.installKvNamespaceName,
|
|
3607
|
+
tokenKeySecretName: binding.tokenKeySecretName,
|
|
3608
|
+
});
|
|
3609
|
+
const { pointer: currentPointer, stateStore } = await ensureTenantStateStore({
|
|
3610
|
+
argv: input.argv,
|
|
3611
|
+
root: input.root,
|
|
3612
|
+
});
|
|
3613
|
+
const existing = await stateStore.getBinding({
|
|
3614
|
+
projectId: input.projectId,
|
|
3615
|
+
environment: input.environment,
|
|
3616
|
+
hubSpotAppId: input.hubSpotAppId,
|
|
3617
|
+
});
|
|
3618
|
+
await stateStore.putBinding(Schema.decodeUnknownSync(schemas.LocalAppRuntimeBinding)({
|
|
3619
|
+
...(existing ?? {}),
|
|
3620
|
+
schemaVersion: 2,
|
|
3621
|
+
ownerId: currentPointer.ownerId,
|
|
3622
|
+
projectId: input.projectId,
|
|
3623
|
+
environment: input.environment,
|
|
3624
|
+
hubSpotAppId: input.hubSpotAppId,
|
|
3625
|
+
installKvNamespaceId: binding.installKvNamespaceId,
|
|
3626
|
+
installKvNamespaceName: binding.installKvNamespaceName,
|
|
3627
|
+
tokenKeySecretName: binding.tokenKeySecretName,
|
|
3628
|
+
tokenKeyCustody,
|
|
3629
|
+
updatedAt: new Date().toISOString(),
|
|
3630
|
+
}));
|
|
3631
|
+
return {
|
|
3632
|
+
installKvNamespaceId: binding.installKvNamespaceId,
|
|
3633
|
+
installKvNamespaceName: binding.installKvNamespaceName,
|
|
3634
|
+
tokenKeySecretName: binding.tokenKeySecretName,
|
|
3635
|
+
tokenKeySecretValue,
|
|
3636
|
+
tokenKeyCustody,
|
|
3637
|
+
...(existing?.tenantD1DatabaseId ? { tenantD1DatabaseId: existing.tenantD1DatabaseId } : {}),
|
|
3638
|
+
...(existing?.tenantD1DatabaseName
|
|
3639
|
+
? { tenantD1DatabaseName: existing.tenantD1DatabaseName }
|
|
3640
|
+
: {}),
|
|
3641
|
+
...(existing?.flagsKvNamespaceId ? { flagsKvNamespaceId: existing.flagsKvNamespaceId } : {}),
|
|
3642
|
+
...(existing?.flagsKvNamespaceName
|
|
3643
|
+
? { flagsKvNamespaceName: existing.flagsKvNamespaceName }
|
|
3644
|
+
: {}),
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3488
3647
|
async function readDeployInstallRuntimeBinding(input) {
|
|
3489
3648
|
const response = await hostedHttp({
|
|
3490
3649
|
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects/${encodeURIComponent(input.projectId)}/hubspot-apps/${encodeURIComponent(input.hubSpotAppId)}/install-runtime?environment=${encodeURIComponent(input.environment)}`, input.controlPlaneUrl),
|
|
3491
|
-
headers: await controlPlaneAuthHeaders(input.userId),
|
|
3650
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
3492
3651
|
});
|
|
3493
3652
|
if (response.status === 404) {
|
|
3494
3653
|
return undefined;
|
|
@@ -3506,7 +3665,7 @@ async function putDeployInstallRuntimeBinding(input) {
|
|
|
3506
3665
|
const response = await hostedHttp({
|
|
3507
3666
|
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects/${encodeURIComponent(input.projectId)}/hubspot-apps/${encodeURIComponent(input.hubSpotAppId)}/install-runtime`, input.controlPlaneUrl),
|
|
3508
3667
|
method: 'PUT',
|
|
3509
|
-
headers: await controlPlaneAuthHeaders(input.userId),
|
|
3668
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
3510
3669
|
body: {
|
|
3511
3670
|
environment: input.environment,
|
|
3512
3671
|
installKvNamespaceId: input.installKvNamespaceId,
|
|
@@ -3532,7 +3691,7 @@ async function ensureHostedProjectRegistration(input) {
|
|
|
3532
3691
|
const response = await hostedHttp({
|
|
3533
3692
|
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects`, input.controlPlaneUrl),
|
|
3534
3693
|
method: 'POST',
|
|
3535
|
-
headers: await controlPlaneAuthHeaders(input.userId),
|
|
3694
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
3536
3695
|
body: { projectId: input.projectId, displayName: input.projectId },
|
|
3537
3696
|
});
|
|
3538
3697
|
if (response.ok || response.status === 409)
|
|
@@ -3551,7 +3710,7 @@ async function requestDeployBillingRuntimeToken(input) {
|
|
|
3551
3710
|
const response = await hostedHttp({
|
|
3552
3711
|
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects/${encodeURIComponent(input.projectId)}/billing/runtime-token`, input.controlPlaneUrl),
|
|
3553
3712
|
method: 'POST',
|
|
3554
|
-
headers: await controlPlaneAuthHeaders(input.userId),
|
|
3713
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
3555
3714
|
body: {
|
|
3556
3715
|
environment: input.environment,
|
|
3557
3716
|
},
|
|
@@ -3624,6 +3783,12 @@ async function ensureTenantDataPlane(input) {
|
|
|
3624
3783
|
kind: 'tenant-d1',
|
|
3625
3784
|
...(exactTenantD1DatabaseId ? { exactResourceId: exactTenantD1DatabaseId } : {}),
|
|
3626
3785
|
...(exactTenantD1DatabaseName ? { exactResourceName: exactTenantD1DatabaseName } : {}),
|
|
3786
|
+
legacyResourceNameExists: (resourceName) => cloudflareResourceNameExists({
|
|
3787
|
+
argv: input.argv,
|
|
3788
|
+
root: input.root,
|
|
3789
|
+
kind: 'tenant-d1',
|
|
3790
|
+
resourceName,
|
|
3791
|
+
}),
|
|
3627
3792
|
validateExactResource: (resourceId) => {
|
|
3628
3793
|
const credentials = resolveCloudflareCredentials(input.argv);
|
|
3629
3794
|
if (!credentials.accountId || !credentials.apiToken) {
|
|
@@ -3665,6 +3830,12 @@ async function ensureTenantDataPlane(input) {
|
|
|
3665
3830
|
kind: 'flags-kv',
|
|
3666
3831
|
...(exactFlagsKvNamespaceId ? { exactResourceId: exactFlagsKvNamespaceId } : {}),
|
|
3667
3832
|
...(exactFlagsKvNamespaceName ? { exactResourceName: exactFlagsKvNamespaceName } : {}),
|
|
3833
|
+
legacyResourceNameExists: (resourceName) => cloudflareResourceNameExists({
|
|
3834
|
+
argv: input.argv,
|
|
3835
|
+
root: input.root,
|
|
3836
|
+
kind: 'flags-kv',
|
|
3837
|
+
resourceName,
|
|
3838
|
+
}),
|
|
3668
3839
|
validateExactResource: (resourceId) => {
|
|
3669
3840
|
const credentials = resolveCloudflareCredentials(input.argv);
|
|
3670
3841
|
if (!credentials.accountId || !credentials.apiToken) {
|
|
@@ -3699,7 +3870,7 @@ async function ensureTenantDataPlane(input) {
|
|
|
3699
3870
|
if (!created) {
|
|
3700
3871
|
const listed = await runCloudflareCommand(['bun', 'x', 'wrangler', 'kv', 'namespace', 'list'], { cwd: input.root, env });
|
|
3701
3872
|
try {
|
|
3702
|
-
const namespaces =
|
|
3873
|
+
const namespaces = parseWranglerJsonArray(listed.stdout);
|
|
3703
3874
|
resourceId = namespaces.find((namespace) => namespace.title === flagsKvNamespaceName)?.id;
|
|
3704
3875
|
}
|
|
3705
3876
|
catch {
|
|
@@ -3804,13 +3975,29 @@ async function validateCloudflareResourceForDeploy(input) {
|
|
|
3804
3975
|
env: cloudflareDeployEnv(input.argv),
|
|
3805
3976
|
});
|
|
3806
3977
|
try {
|
|
3807
|
-
const rows =
|
|
3978
|
+
const rows = parseWranglerJsonArray(listed.stdout);
|
|
3808
3979
|
return rows.some((row) => row.id === input.resourceId || row.uuid === input.resourceId);
|
|
3809
3980
|
}
|
|
3810
3981
|
catch {
|
|
3811
3982
|
throw new Error(`HSX_E_CLOUDFLARE_RESOURCE_VALIDATION_FAILED: Wrangler returned malformed ${input.kind} list output.`);
|
|
3812
3983
|
}
|
|
3813
3984
|
}
|
|
3985
|
+
async function cloudflareResourceNameExists(input) {
|
|
3986
|
+
const command = input.kind === 'tenant-d1'
|
|
3987
|
+
? ['bun', 'x', 'wrangler', 'd1', 'list', '--json']
|
|
3988
|
+
: ['bun', 'x', 'wrangler', 'kv', 'namespace', 'list'];
|
|
3989
|
+
const listed = await runCloudflareCommand(command, {
|
|
3990
|
+
cwd: input.root,
|
|
3991
|
+
env: cloudflareDeployEnv(input.argv),
|
|
3992
|
+
});
|
|
3993
|
+
try {
|
|
3994
|
+
const rows = parseWranglerJsonArray(listed.stdout);
|
|
3995
|
+
return rows.some((row) => row.name === input.resourceName || row.title === input.resourceName);
|
|
3996
|
+
}
|
|
3997
|
+
catch {
|
|
3998
|
+
throw new Error(`HSX_E_CLOUDFLARE_RESOURCE_VALIDATION_FAILED: Wrangler returned malformed ${input.kind} list output.`);
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
3814
4001
|
export function renderWranglerConfig(input) {
|
|
3815
4002
|
const lines = [
|
|
3816
4003
|
'# Generated by hs-x. Do not edit by hand.',
|
|
@@ -3884,7 +4071,7 @@ function tomlString(value) {
|
|
|
3884
4071
|
async function readDeployHubSpotOAuthSecret(input) {
|
|
3885
4072
|
const response = await hostedHttp({
|
|
3886
4073
|
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects/${encodeURIComponent(input.projectId)}/hubspot-apps/${encodeURIComponent(input.hubSpotAppId)}/oauth-secret?environment=${encodeURIComponent(input.environment)}`, input.controlPlaneUrl),
|
|
3887
|
-
headers: await controlPlaneAuthHeaders(input.userId),
|
|
4074
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
3888
4075
|
});
|
|
3889
4076
|
if (response.status === 404) {
|
|
3890
4077
|
return undefined;
|
|
@@ -4880,6 +5067,45 @@ async function readPersistedHubSpotAppBinding(root, expectedProjectName) {
|
|
|
4880
5067
|
async function readPersistedHubSpotAppId(root, expectedProjectName) {
|
|
4881
5068
|
return (await readPersistedHubSpotAppBinding(root, expectedProjectName))?.appId;
|
|
4882
5069
|
}
|
|
5070
|
+
/**
|
|
5071
|
+
* Resolve the one HubSpot app a linked project already registered. The common
|
|
5072
|
+
* path remains implicit: a first combined deploy persists the app id, while a
|
|
5073
|
+
* checkout missing that advisory file can recover an unambiguous binding from
|
|
5074
|
+
* the control plane. Explicit ids remain the advanced override.
|
|
5075
|
+
*/
|
|
5076
|
+
async function resolveHubSpotAppIdForDeploy(input) {
|
|
5077
|
+
const explicit = readOptionalHubSpotAppId(resolveFlag(input.argv, '--hubspot-app-id') ?? process.env.HSX_HUBSPOT_APP_ID);
|
|
5078
|
+
const projectName = await resolveHubSpotProjectName(input.argv, input.root);
|
|
5079
|
+
const persisted = await readPersistedHubSpotAppId(input.root, projectName);
|
|
5080
|
+
const selected = explicit ?? persisted;
|
|
5081
|
+
if (selected || !input.controlPlaneUrl)
|
|
5082
|
+
return selected;
|
|
5083
|
+
const response = await hostedHttp({
|
|
5084
|
+
url: new URL(`/v1/accounts/${encodeURIComponent(input.accountId)}/projects/${encodeURIComponent(input.projectId)}/runtime-bindings`, input.controlPlaneUrl),
|
|
5085
|
+
headers: await controlPlaneAuthHeaders(input.userId, undefined, input.accountId),
|
|
5086
|
+
});
|
|
5087
|
+
if (!response.ok)
|
|
5088
|
+
return undefined;
|
|
5089
|
+
const body = await response.json();
|
|
5090
|
+
const bindings = Schema.decodeUnknownSync(schemas.ListProjectRuntimeBindingsResponse)(body).bindings;
|
|
5091
|
+
const appIds = [
|
|
5092
|
+
...new Set(bindings
|
|
5093
|
+
.filter((binding) => binding.environment === input.environment)
|
|
5094
|
+
.map((binding) => binding.hubSpotAppId)),
|
|
5095
|
+
];
|
|
5096
|
+
if (appIds.length === 0)
|
|
5097
|
+
return undefined;
|
|
5098
|
+
if (appIds.length > 1) {
|
|
5099
|
+
throw new Error(`Project ${input.projectId} has multiple HubSpot apps in ${input.environment} (${appIds.join(', ')}). Pass --hubspot-app-id once to choose one.`);
|
|
5100
|
+
}
|
|
5101
|
+
const appId = appIds[0];
|
|
5102
|
+
await persistHubSpotAppBinding(input.root, {
|
|
5103
|
+
appId,
|
|
5104
|
+
projectName,
|
|
5105
|
+
developerAccountId: undefined,
|
|
5106
|
+
});
|
|
5107
|
+
return appId;
|
|
5108
|
+
}
|
|
4883
5109
|
async function resolveHubSpotAppAuthUrl(argv, root) {
|
|
4884
5110
|
const projectName = await resolveHubSpotProjectName(argv, root);
|
|
4885
5111
|
const persisted = await readPersistedHubSpotAppBinding(root, projectName);
|
|
@@ -4997,7 +5223,7 @@ async function requestHostedControlPlaneDeployPlan({ request, controlPlaneUrl, u
|
|
|
4997
5223
|
const response = await hostedHttp({
|
|
4998
5224
|
url: new URL('/v1/deploys/plan', controlPlaneUrl),
|
|
4999
5225
|
method: 'POST',
|
|
5000
|
-
headers: await controlPlaneAuthHeaders(userId),
|
|
5226
|
+
headers: await controlPlaneAuthHeaders(userId, undefined, request.accountId),
|
|
5001
5227
|
body: request,
|
|
5002
5228
|
});
|
|
5003
5229
|
const body = await response.json();
|
|
@@ -5008,7 +5234,7 @@ async function requestHostedControlPlaneDeployPlan({ request, controlPlaneUrl, u
|
|
|
5008
5234
|
}
|
|
5009
5235
|
return Schema.decodeUnknownSync(schemas.DeployPlanResponse)(body);
|
|
5010
5236
|
}
|
|
5011
|
-
async function requestLocalControlPlaneDeployRecordAndMaybePromote({ request, plan, promoteWhenHealthy, revision, }) {
|
|
5237
|
+
async function requestLocalControlPlaneDeployRecordAndMaybePromote({ request, plan, promoteWhenHealthy, workerUrl, revision, }) {
|
|
5012
5238
|
const localAttestationAuth = revision
|
|
5013
5239
|
? { grantSecret: randomBytes(32).toString('base64url'), hubSpotAppId: 1 }
|
|
5014
5240
|
: undefined;
|
|
@@ -5051,6 +5277,7 @@ async function requestLocalControlPlaneDeployRecordAndMaybePromote({ request, pl
|
|
|
5051
5277
|
manifestHash: plan.manifestHash,
|
|
5052
5278
|
signedManifest: request.manifest,
|
|
5053
5279
|
signature: `sig_${plan.deployId}`,
|
|
5280
|
+
...(workerUrl ? { workerUrl } : {}),
|
|
5054
5281
|
// ADR-021 R03: the real Cloudflare Worker version identity captured
|
|
5055
5282
|
// back from the confirmed active deployment. Absent only when capture
|
|
5056
5283
|
// was skipped/failed (best-effort) — then this deploy is history-only.
|
|
@@ -5133,13 +5360,14 @@ async function requestHostedControlPlaneDeployRecordAndMaybePromote({ request, p
|
|
|
5133
5360
|
const response = await hostedHttp({
|
|
5134
5361
|
url: new URL(`/v1/deploys/${encodeURIComponent(plan.deployId)}/record`, controlPlaneUrl),
|
|
5135
5362
|
method: 'POST',
|
|
5136
|
-
headers: await controlPlaneAuthHeaders(userId),
|
|
5363
|
+
headers: await controlPlaneAuthHeaders(userId, undefined, request.accountId),
|
|
5137
5364
|
body: {
|
|
5138
5365
|
accountId: request.accountId,
|
|
5139
5366
|
projectId: request.projectId,
|
|
5140
5367
|
manifestHash: plan.manifestHash,
|
|
5141
5368
|
signedManifest: request.manifest,
|
|
5142
5369
|
signature: `sig_${plan.deployId}`,
|
|
5370
|
+
...(workerUrl ? { workerUrl } : {}),
|
|
5143
5371
|
// ADR-021 R03: the real Cloudflare Worker version identity captured back
|
|
5144
5372
|
// from the confirmed active deployment. Absent only when capture was
|
|
5145
5373
|
// skipped/failed (best-effort) — then this deploy is history-only.
|
|
@@ -5159,6 +5387,7 @@ async function requestHostedControlPlaneDeployRecordAndMaybePromote({ request, p
|
|
|
5159
5387
|
}
|
|
5160
5388
|
const drift = await waitForHealthyControlPlaneDrift({
|
|
5161
5389
|
controlPlaneUrl,
|
|
5390
|
+
accountId: request.accountId,
|
|
5162
5391
|
projectId: request.projectId,
|
|
5163
5392
|
deployId: plan.deployId,
|
|
5164
5393
|
userId,
|
|
@@ -5461,7 +5690,7 @@ function discoverManageSchemaMode(definition) {
|
|
|
5461
5690
|
}
|
|
5462
5691
|
function discoverSourceDefinitions(source) {
|
|
5463
5692
|
const definitions = new Map();
|
|
5464
|
-
for (const match of source.matchAll(/(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*defineSource(\.push)?\s*\(\s*\{([\s\S]*?)\}\s*\)/g)) {
|
|
5693
|
+
for (const match of source.matchAll(/(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*defineSource(\.push)?\s*(?:<[\s\S]*?>)?\s*\(\s*\{([\s\S]*?)\}\s*\)/g)) {
|
|
5465
5694
|
const variableName = match[1];
|
|
5466
5695
|
const body = match[3] ?? '';
|
|
5467
5696
|
if (!variableName) {
|
|
@@ -5585,6 +5814,9 @@ const deployOptions = {
|
|
|
5585
5814
|
message: Options.text('message').pipe(Options.withAlias('m'), Options.optional), // --message / -m
|
|
5586
5815
|
platformVersion: Options.text('platform-version').pipe(Options.optional), // --platform-version
|
|
5587
5816
|
hubspotProjectName: Options.text('hubspot-project-name').pipe(Options.optional), // --hubspot-project-name
|
|
5817
|
+
cloudflareAccountId: Options.text('cloudflare-account-id').pipe(Options.optional), // --cloudflare-account-id
|
|
5818
|
+
cloudflareApiToken: Options.text('cloudflare-api-token').pipe(Options.optional), // --cloudflare-api-token
|
|
5819
|
+
apiToken: Options.text('api-token').pipe(Options.optional), // --api-token
|
|
5588
5820
|
// ── boolean flags (argv.includes) ──
|
|
5589
5821
|
plan: Options.boolean('plan').pipe(Options.withDefault(false)), // --plan
|
|
5590
5822
|
dryRun: Options.boolean('dry-run').pipe(Options.withDefault(false)), // --dry-run
|
|
@@ -5651,6 +5883,9 @@ const deployBaseCmd = Command.make('deploy', deployOptions, (opts) => runHandler
|
|
|
5651
5883
|
push('--message', opts.message);
|
|
5652
5884
|
push('--platform-version', opts.platformVersion);
|
|
5653
5885
|
push('--hubspot-project-name', opts.hubspotProjectName);
|
|
5886
|
+
push('--cloudflare-account-id', opts.cloudflareAccountId);
|
|
5887
|
+
push('--cloudflare-api-token', opts.cloudflareApiToken);
|
|
5888
|
+
push('--api-token', opts.apiToken);
|
|
5654
5889
|
flag('--plan', opts.plan);
|
|
5655
5890
|
flag('--dry-run', opts.dryRun);
|
|
5656
5891
|
flag('--build-only', opts.buildOnly);
|