@treeseed/sdk 0.10.22 → 0.10.23
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/dist/db/market-schema.js +3 -2
- package/dist/market-client.d.ts +4 -0
- package/dist/market-client.js +6 -0
- package/dist/operations/providers/default.js +26 -4
- package/dist/operations/repository-operations.js +6 -2
- package/dist/operations/services/config-runtime.d.ts +1 -1
- package/dist/operations/services/deploy.d.ts +18 -1
- package/dist/operations/services/deploy.js +176 -24
- package/dist/operations/services/github-automation.d.ts +10 -1
- package/dist/operations/services/github-automation.js +18 -4
- package/dist/operations/services/hosting-audit.d.ts +2 -1
- package/dist/operations/services/hosting-audit.js +12 -1
- package/dist/operations/services/hub-launch.d.ts +1 -0
- package/dist/operations/services/hub-launch.js +1 -0
- package/dist/operations/services/hub-provider-launch.d.ts +9 -0
- package/dist/operations/services/hub-provider-launch.js +140 -40
- package/dist/operations/services/managed-host-security.d.ts +1 -1
- package/dist/operations/services/managed-host-security.js +4 -1
- package/dist/operations/services/project-platform.js +16 -0
- package/dist/operations/services/railway-api.js +2 -1
- package/dist/operations/services/railway-deploy.d.ts +2 -1
- package/dist/operations/services/railway-deploy.js +15 -18
- package/dist/platform/environment.d.ts +1 -1
- package/dist/platform/environment.js +1 -1
- package/dist/reconcile/builtin-adapters.js +155 -25
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.js +17 -1
- package/dist/reconcile/units.js +1 -0
- package/dist/sdk-types.d.ts +1 -1
- package/dist/sdk-types.js +2 -0
- package/dist/workflow/operations.d.ts +2 -0
- package/drizzle/market/0000_market_control_plane.sql +3 -3
- package/drizzle/market/0003_project_team_slug_unique.sql +4 -0
- package/package.json +1 -1
package/dist/db/market-schema.js
CHANGED
|
@@ -297,13 +297,14 @@ const webSessions = pgTable("web_sessions", {
|
|
|
297
297
|
const projects = pgTable("projects", {
|
|
298
298
|
id: text("id").primaryKey(),
|
|
299
299
|
teamId: text("team_id").notNull(),
|
|
300
|
-
slug: text("slug").notNull()
|
|
300
|
+
slug: text("slug").notNull(),
|
|
301
301
|
name: text("name").notNull(),
|
|
302
302
|
description: text("description"),
|
|
303
303
|
metadataJson: text("metadata_json"),
|
|
304
304
|
createdAt: text("created_at").notNull(),
|
|
305
305
|
updatedAt: text("updated_at").notNull()
|
|
306
306
|
}, (table) => [
|
|
307
|
+
uniqueIndex("idx_projects_team_slug").on(table.teamId, table.slug),
|
|
307
308
|
index("idx_projects_team_id").on(table.teamId)
|
|
308
309
|
]);
|
|
309
310
|
const projectConnections = pgTable("project_connections", {
|
|
@@ -445,7 +446,7 @@ const catalogItems = pgTable("catalog_items", {
|
|
|
445
446
|
createdAt: text("created_at").notNull(),
|
|
446
447
|
updatedAt: text("updated_at").notNull()
|
|
447
448
|
}, (table) => [
|
|
448
|
-
uniqueIndex("
|
|
449
|
+
uniqueIndex("idx_catalog_items_team_kind_slug").on(table.teamId, table.kind, table.slug),
|
|
449
450
|
index("idx_catalog_items_team_kind").on(table.teamId, table.kind, table.updatedAt),
|
|
450
451
|
index("idx_catalog_items_visibility_listing").on(table.visibility, table.listingEnabled, table.updatedAt)
|
|
451
452
|
]);
|
package/dist/market-client.d.ts
CHANGED
|
@@ -374,6 +374,10 @@ export declare class MarketClient {
|
|
|
374
374
|
ok: true;
|
|
375
375
|
payload: ProjectDeployment;
|
|
376
376
|
}>;
|
|
377
|
+
projectDeploymentById(deploymentId: string): Promise<{
|
|
378
|
+
ok: true;
|
|
379
|
+
payload: ProjectDeployment;
|
|
380
|
+
}>;
|
|
377
381
|
projectDeploymentEvents(projectId: string, deploymentId: string, options?: {
|
|
378
382
|
limit?: number | string | null;
|
|
379
383
|
}): Promise<{
|
package/dist/market-client.js
CHANGED
|
@@ -485,6 +485,12 @@ class MarketClient {
|
|
|
485
485
|
{ requireAuth: true }
|
|
486
486
|
);
|
|
487
487
|
}
|
|
488
|
+
projectDeploymentById(deploymentId) {
|
|
489
|
+
return this.request(
|
|
490
|
+
`/v1/project-deployments/${encodeURIComponent(deploymentId)}`,
|
|
491
|
+
{ requireAuth: true }
|
|
492
|
+
);
|
|
493
|
+
}
|
|
488
494
|
projectDeploymentEvents(projectId, deploymentId, options = {}) {
|
|
489
495
|
const query = options.limit ? `?limit=${encodeURIComponent(String(options.limit))}` : "";
|
|
490
496
|
return this.request(
|
|
@@ -91,6 +91,28 @@ function failureResult(metadata, message, options = {}) {
|
|
|
91
91
|
function contextEnv(context) {
|
|
92
92
|
return { ...process.env, ...context.env ?? {} };
|
|
93
93
|
}
|
|
94
|
+
async function withTemporaryProcessEnv(env, action) {
|
|
95
|
+
const previous = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const [key, value] of Object.entries(env)) {
|
|
97
|
+
previous.set(key, process.env[key]);
|
|
98
|
+
if (value === void 0) {
|
|
99
|
+
delete process.env[key];
|
|
100
|
+
} else {
|
|
101
|
+
process.env[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await action();
|
|
106
|
+
} finally {
|
|
107
|
+
for (const [key, value] of previous) {
|
|
108
|
+
if (value === void 0) {
|
|
109
|
+
delete process.env[key];
|
|
110
|
+
} else {
|
|
111
|
+
process.env[key] = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
94
116
|
function operationEnv(context) {
|
|
95
117
|
const tenantConfigPath = resolve(context.cwd, "treeseed.site.yaml");
|
|
96
118
|
return existsSync(tenantConfigPath) ? resolveTreeseedLaunchEnvironment({ tenantRoot: context.cwd, scope: "local", baseEnv: contextEnv(context) }) : contextEnv(context);
|
|
@@ -399,21 +421,21 @@ class HubValidateLaunchOperation extends BaseOperation {
|
|
|
399
421
|
class HubExecuteLaunchOperation extends BaseOperation {
|
|
400
422
|
async execute(input, context) {
|
|
401
423
|
const intent = input.intent && typeof input.intent === "object" ? input.intent : input;
|
|
402
|
-
const result = await executeKnowledgeHubLaunch(intent, {
|
|
424
|
+
const result = await withTemporaryProcessEnv(contextEnv(context), () => executeKnowledgeHubLaunch(intent, {
|
|
403
425
|
onPhase: async (phase) => {
|
|
404
426
|
await context.onProgress?.({
|
|
405
427
|
kind: "hub_launch_phase",
|
|
406
428
|
...phase
|
|
407
429
|
});
|
|
408
430
|
}
|
|
409
|
-
});
|
|
431
|
+
}));
|
|
410
432
|
return operationResult(this.metadata, result);
|
|
411
433
|
}
|
|
412
434
|
}
|
|
413
435
|
class HubResumeLaunchOperation extends BaseOperation {
|
|
414
436
|
async execute(input, context) {
|
|
415
437
|
const intent = input.intent && typeof input.intent === "object" ? input.intent : input;
|
|
416
|
-
const result = await executeKnowledgeHubLaunch(intent, {
|
|
438
|
+
const result = await withTemporaryProcessEnv(contextEnv(context), () => executeKnowledgeHubLaunch(intent, {
|
|
417
439
|
onPhase: async (phase) => {
|
|
418
440
|
await context.onProgress?.({
|
|
419
441
|
kind: "hub_launch_phase",
|
|
@@ -421,7 +443,7 @@ class HubResumeLaunchOperation extends BaseOperation {
|
|
|
421
443
|
...phase
|
|
422
444
|
});
|
|
423
445
|
}
|
|
424
|
-
});
|
|
446
|
+
}));
|
|
425
447
|
return operationResult(this.metadata, {
|
|
426
448
|
resumed: true,
|
|
427
449
|
...result
|
|
@@ -310,15 +310,19 @@ async function writeContentRecord(repoPath, collection, input, normalizedInput)
|
|
|
310
310
|
const existingTarget = input.overwrite === true ? [`${normalized.slug}.mdx`, `${normalized.slug}.md`].map((file) => resolve(root, file)).find((candidate) => existsSync(candidate)) : null;
|
|
311
311
|
const target = existingTarget ?? safeContentPath(repoPath, collection, normalized.slug, normalized.extension);
|
|
312
312
|
if (existsSync(target) && input.overwrite !== true) throw new Error("A content record with that slug already exists.");
|
|
313
|
+
const frontmatter = existingTarget && input.preserveFrontmatter === true ? {
|
|
314
|
+
...normalized.frontmatter,
|
|
315
|
+
...(await readContentRecord(repoPath, collection, normalized.slug)).frontmatter
|
|
316
|
+
} : normalized.frontmatter;
|
|
313
317
|
const relativePath = await writeParsedRecord(repoPath, {
|
|
314
318
|
path: target,
|
|
315
|
-
frontmatter
|
|
319
|
+
frontmatter,
|
|
316
320
|
body: normalized.body
|
|
317
321
|
});
|
|
318
322
|
return {
|
|
319
323
|
collection,
|
|
320
324
|
slug: normalized.slug,
|
|
321
|
-
id:
|
|
325
|
+
id: frontmatter.id,
|
|
322
326
|
path: relativePath,
|
|
323
327
|
href: collection === "agents" ? `/app/projects/${encodeURIComponent(String(input.projectId ?? ""))}/agents/${encodeURIComponent(normalized.slug)}` : `/app/work/${collection}/${encodeURIComponent(normalized.slug)}`
|
|
324
328
|
};
|
|
@@ -55,7 +55,7 @@ export type TreeseedConfigEntrySnapshot = {
|
|
|
55
55
|
group: string;
|
|
56
56
|
cluster: string;
|
|
57
57
|
startupProfile: 'core' | 'optional' | 'advanced';
|
|
58
|
-
requirement: 'required' | 'conditional' | 'optional';
|
|
58
|
+
requirement: 'required' | 'conditional' | 'optional' | 'generated';
|
|
59
59
|
description: string;
|
|
60
60
|
howToGet: string;
|
|
61
61
|
sensitivity: 'secret' | 'plain' | 'derived';
|
|
@@ -85,7 +85,7 @@ export declare function buildPublicVars(deployConfig: any, options?: {}): {
|
|
|
85
85
|
export declare function buildSecretMap(deployConfig: any, state: any): {
|
|
86
86
|
TREESEED_FORM_TOKEN_SECRET: any;
|
|
87
87
|
TREESEED_EDITORIAL_PREVIEW_SECRET: any;
|
|
88
|
-
TREESEED_TURNSTILE_SECRET_KEY:
|
|
88
|
+
TREESEED_TURNSTILE_SECRET_KEY: any;
|
|
89
89
|
TREESEED_SMTP_PASSWORD: string | null;
|
|
90
90
|
};
|
|
91
91
|
export declare function loadDeployState(tenantRoot: any, deployConfig: any, options?: {}): any;
|
|
@@ -307,6 +307,10 @@ export declare function listD1Databases(tenantRoot: any, env: any): any;
|
|
|
307
307
|
export declare function listQueues(tenantRoot: any, env: any): any;
|
|
308
308
|
export declare function listR2Buckets(tenantRoot: any, env: any): any;
|
|
309
309
|
export declare function listPagesProjects(tenantRoot: any, env: any): any;
|
|
310
|
+
export declare function listTurnstileWidgets(tenantRoot: any, env: any): any;
|
|
311
|
+
export declare function getTurnstileWidget(env: any, sitekey: any): any;
|
|
312
|
+
export declare function createTurnstileWidget(env: any, input: any): any;
|
|
313
|
+
export declare function updateTurnstileWidget(env: any, sitekey: any, input: any): any;
|
|
310
314
|
export declare function buildCloudflarePagesFunctionBindings(state: any): {
|
|
311
315
|
r2_buckets?: {
|
|
312
316
|
[x: number]: {
|
|
@@ -330,6 +334,7 @@ export declare function buildProvisioningSummary(deployConfig: any, state: any,
|
|
|
330
334
|
siteUrl: any;
|
|
331
335
|
accountId: any;
|
|
332
336
|
pages: any;
|
|
337
|
+
turnstileWidget: any;
|
|
333
338
|
formGuardKv: any;
|
|
334
339
|
sessionKv: any;
|
|
335
340
|
siteDataDb: any;
|
|
@@ -341,6 +346,7 @@ export declare function buildProvisioningSummary(deployConfig: any, state: any,
|
|
|
341
346
|
queue: any;
|
|
342
347
|
dlq: any;
|
|
343
348
|
database: any;
|
|
349
|
+
turnstileWidget: any;
|
|
344
350
|
formGuardKv: any;
|
|
345
351
|
railwayProject: any;
|
|
346
352
|
webDomain: any;
|
|
@@ -891,6 +897,7 @@ export declare function destroyTreeseedEnvironmentResources(tenantRoot: any, opt
|
|
|
891
897
|
siteUrl: any;
|
|
892
898
|
accountId: any;
|
|
893
899
|
pages: any;
|
|
900
|
+
turnstileWidget: any;
|
|
894
901
|
formGuardKv: any;
|
|
895
902
|
sessionKv: any;
|
|
896
903
|
siteDataDb: any;
|
|
@@ -902,6 +909,7 @@ export declare function destroyTreeseedEnvironmentResources(tenantRoot: any, opt
|
|
|
902
909
|
queue: any;
|
|
903
910
|
dlq: any;
|
|
904
911
|
database: any;
|
|
912
|
+
turnstileWidget: any;
|
|
905
913
|
formGuardKv: any;
|
|
906
914
|
railwayProject: any;
|
|
907
915
|
webDomain: any;
|
|
@@ -963,6 +971,7 @@ export declare function destroyCloudflareResources(tenantRoot: any, options?: {}
|
|
|
963
971
|
siteUrl: any;
|
|
964
972
|
accountId: any;
|
|
965
973
|
pages: any;
|
|
974
|
+
turnstileWidget: any;
|
|
966
975
|
formGuardKv: any;
|
|
967
976
|
sessionKv: any;
|
|
968
977
|
siteDataDb: any;
|
|
@@ -974,6 +983,7 @@ export declare function destroyCloudflareResources(tenantRoot: any, options?: {}
|
|
|
974
983
|
queue: any;
|
|
975
984
|
dlq: any;
|
|
976
985
|
database: any;
|
|
986
|
+
turnstileWidget: any;
|
|
977
987
|
formGuardKv: any;
|
|
978
988
|
railwayProject: any;
|
|
979
989
|
webDomain: any;
|
|
@@ -1008,6 +1018,11 @@ export declare function destroyCloudflareResources(tenantRoot: any, options?: {}
|
|
|
1008
1018
|
status: any;
|
|
1009
1019
|
name: any;
|
|
1010
1020
|
};
|
|
1021
|
+
turnstileWidget: {
|
|
1022
|
+
status: any;
|
|
1023
|
+
sitekey: any;
|
|
1024
|
+
name: null;
|
|
1025
|
+
};
|
|
1011
1026
|
formGuard: {
|
|
1012
1027
|
status: string;
|
|
1013
1028
|
id: any;
|
|
@@ -1064,6 +1079,7 @@ export declare function provisionCloudflareResources(tenantRoot: any, options?:
|
|
|
1064
1079
|
siteUrl: any;
|
|
1065
1080
|
accountId: any;
|
|
1066
1081
|
pages: any;
|
|
1082
|
+
turnstileWidget: any;
|
|
1067
1083
|
formGuardKv: any;
|
|
1068
1084
|
sessionKv: any;
|
|
1069
1085
|
siteDataDb: any;
|
|
@@ -1075,6 +1091,7 @@ export declare function provisionCloudflareResources(tenantRoot: any, options?:
|
|
|
1075
1091
|
queue: any;
|
|
1076
1092
|
dlq: any;
|
|
1077
1093
|
database: any;
|
|
1094
|
+
turnstileWidget: any;
|
|
1078
1095
|
formGuardKv: any;
|
|
1079
1096
|
railwayProject: any;
|
|
1080
1097
|
webDomain: any;
|
|
@@ -41,6 +41,13 @@ function ensureParent(filePath) {
|
|
|
41
41
|
function stableHash(value) {
|
|
42
42
|
return createHash("sha256").update(value).digest("hex");
|
|
43
43
|
}
|
|
44
|
+
function compactDeploymentKey(input) {
|
|
45
|
+
const rawKey = sanitizeResourceKey(input.rawKey ?? "");
|
|
46
|
+
if (rawKey && rawKey.length <= 40) return rawKey;
|
|
47
|
+
const base = sanitizeSegment(input.slug ?? input.projectSegment ?? "project").slice(0, 27) || "project";
|
|
48
|
+
const hash = stableHash(`${input.teamId ?? ""}:${input.projectId ?? ""}:${input.slug ?? ""}`).slice(0, 8);
|
|
49
|
+
return `${base}-${hash}`;
|
|
50
|
+
}
|
|
44
51
|
function readJson(filePath, fallback) {
|
|
45
52
|
if (!existsSync(filePath)) {
|
|
46
53
|
return fallback;
|
|
@@ -69,6 +76,9 @@ function loadTenantDeployConfig(tenantRoot) {
|
|
|
69
76
|
function sanitizeSegment(value) {
|
|
70
77
|
return String(value).trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 36) || "default";
|
|
71
78
|
}
|
|
79
|
+
function sanitizeResourceKey(value) {
|
|
80
|
+
return String(value).trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "");
|
|
81
|
+
}
|
|
72
82
|
function requireConfiguredIdentityValue(value, label) {
|
|
73
83
|
const normalized = typeof value === "string" && value.trim() ? value.trim() : "";
|
|
74
84
|
if (!normalized) {
|
|
@@ -87,7 +97,13 @@ function resolveTreeseedResourceIdentity(deployConfig, target) {
|
|
|
87
97
|
);
|
|
88
98
|
const teamSegment = sanitizeSegment(teamId);
|
|
89
99
|
const projectSegment = sanitizeSegment(projectId);
|
|
90
|
-
const deploymentKey =
|
|
100
|
+
const deploymentKey = compactDeploymentKey({
|
|
101
|
+
rawKey: `${teamSegment}-${projectSegment}`,
|
|
102
|
+
teamId,
|
|
103
|
+
projectId,
|
|
104
|
+
projectSegment,
|
|
105
|
+
slug: deployConfig.slug
|
|
106
|
+
});
|
|
91
107
|
const environment = target.kind === "persistent" ? target.scope : target.branchName;
|
|
92
108
|
const environmentSegment = target.kind === "persistent" ? target.scope : sanitizeSegment(target.branchName);
|
|
93
109
|
return {
|
|
@@ -168,6 +184,13 @@ function resolveConfiguredSurfaceBaseUrl(deployConfig, target, surface) {
|
|
|
168
184
|
}
|
|
169
185
|
return null;
|
|
170
186
|
}
|
|
187
|
+
function configuredSurfaceHosts(deployConfig, target, surface) {
|
|
188
|
+
const hosts = [
|
|
189
|
+
resolveConfiguredSurfaceDomain(deployConfig, target, surface),
|
|
190
|
+
surface === "web" && target.kind === "persistent" && target.scope === "prod" ? primaryHost(deployConfig.surfaces?.web?.publicBaseUrl ?? deployConfig.siteUrl) : null
|
|
191
|
+
].filter(Boolean);
|
|
192
|
+
return [...new Set(hosts)];
|
|
193
|
+
}
|
|
171
194
|
function sharedDeploymentName(identity, role = "") {
|
|
172
195
|
const roleSegment = role === "workdayManager" ? "workday-manager" : role === "workerRunner" ? "worker-runner-01" : role;
|
|
173
196
|
return role ? `${identity.deploymentKey}-${sanitizeSegment(roleSegment)}` : identity.deploymentKey;
|
|
@@ -419,7 +442,7 @@ function buildSecretMap(deployConfig, state) {
|
|
|
419
442
|
return {
|
|
420
443
|
TREESEED_FORM_TOKEN_SECRET: envOrNull("TREESEED_FORM_TOKEN_SECRET") ?? generatedSecret,
|
|
421
444
|
TREESEED_EDITORIAL_PREVIEW_SECRET: envOrNull("TREESEED_EDITORIAL_PREVIEW_SECRET") ?? previewSecret,
|
|
422
|
-
TREESEED_TURNSTILE_SECRET_KEY: envOrNull("TREESEED_TURNSTILE_SECRET_KEY"),
|
|
445
|
+
TREESEED_TURNSTILE_SECRET_KEY: state.turnstileWidgets?.formGuard?.secret ?? envOrNull("TREESEED_TURNSTILE_SECRET_KEY"),
|
|
423
446
|
TREESEED_SMTP_PASSWORD: envOrNull("TREESEED_SMTP_PASSWORD")
|
|
424
447
|
};
|
|
425
448
|
}
|
|
@@ -431,6 +454,8 @@ function defaultStateFromConfig(deployConfig, target) {
|
|
|
431
454
|
const contentPreviewRootTemplate = deployConfig.cloudflare.r2?.previewRootTemplate ?? "teams/{teamId}/previews";
|
|
432
455
|
const contentDefaultTeamId = identity.teamId;
|
|
433
456
|
const contentManifestKey = contentManifestKeyTemplate.replaceAll("{teamId}", contentDefaultTeamId);
|
|
457
|
+
const turnstileName = environmentScopedIdentityName(identity, "turnstile", target);
|
|
458
|
+
const turnstileDomains = configuredSurfaceHosts(deployConfig, target, "web");
|
|
434
459
|
return {
|
|
435
460
|
version: 2,
|
|
436
461
|
target,
|
|
@@ -469,6 +494,17 @@ function defaultStateFromConfig(deployConfig, target) {
|
|
|
469
494
|
buildOutputDir: deployConfig.cloudflare.pages?.buildOutputDir ?? "dist",
|
|
470
495
|
url: resolveConfiguredSurfaceBaseUrl(deployConfig, target, "web")
|
|
471
496
|
},
|
|
497
|
+
turnstileWidgets: {
|
|
498
|
+
formGuard: {
|
|
499
|
+
name: turnstileName,
|
|
500
|
+
sitekey: null,
|
|
501
|
+
secret: null,
|
|
502
|
+
mode: "managed",
|
|
503
|
+
domains: turnstileDomains,
|
|
504
|
+
managed: true,
|
|
505
|
+
lastSyncedAt: null
|
|
506
|
+
}
|
|
507
|
+
},
|
|
472
508
|
content: {
|
|
473
509
|
runtimeProvider: deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay",
|
|
474
510
|
publishProvider: deployConfig.providers?.content?.publish ?? deployConfig.providers?.content?.runtime ?? "team_scoped_r2_overlay",
|
|
@@ -636,6 +672,23 @@ function loadDeployState(tenantRoot, deployConfig, options = {}) {
|
|
|
636
672
|
...defaults.generatedSecrets ?? {},
|
|
637
673
|
...persisted.generatedSecrets ?? {}
|
|
638
674
|
},
|
|
675
|
+
turnstileWidgets: {
|
|
676
|
+
...defaults.turnstileWidgets ?? {},
|
|
677
|
+
...persisted.turnstileWidgets ?? {},
|
|
678
|
+
formGuard: {
|
|
679
|
+
...defaults.turnstileWidgets?.formGuard ?? {},
|
|
680
|
+
...persisted.turnstileWidgets?.formGuard ?? {},
|
|
681
|
+
name: defaults.turnstileWidgets?.formGuard?.name ?? persisted.turnstileWidgets?.formGuard?.name ?? null,
|
|
682
|
+
mode: "managed",
|
|
683
|
+
managed: true,
|
|
684
|
+
domains: [
|
|
685
|
+
...new Set([
|
|
686
|
+
...Array.isArray(defaults.turnstileWidgets?.formGuard?.domains) ? defaults.turnstileWidgets.formGuard.domains : [],
|
|
687
|
+
...Array.isArray(persisted.turnstileWidgets?.formGuard?.domains) ? persisted.turnstileWidgets.formGuard.domains : []
|
|
688
|
+
].filter(Boolean))
|
|
689
|
+
]
|
|
690
|
+
}
|
|
691
|
+
},
|
|
639
692
|
content: {
|
|
640
693
|
...defaults.content ?? {},
|
|
641
694
|
...persisted.content ?? {},
|
|
@@ -926,6 +979,68 @@ function listPagesProjects(tenantRoot, env) {
|
|
|
926
979
|
});
|
|
927
980
|
return Array.isArray(payload?.result) ? payload.result : [];
|
|
928
981
|
}
|
|
982
|
+
function listTurnstileWidgets(tenantRoot, env) {
|
|
983
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
984
|
+
if (!accountId) {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
const payload = cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets?per_page=100`, {
|
|
988
|
+
env,
|
|
989
|
+
allowFailure: true
|
|
990
|
+
});
|
|
991
|
+
return Array.isArray(payload?.result) ? payload.result : [];
|
|
992
|
+
}
|
|
993
|
+
function getTurnstileWidget(env, sitekey) {
|
|
994
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
995
|
+
if (!accountId || !sitekey) {
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
const payload = cloudflareApiRequest(
|
|
999
|
+
`/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}`,
|
|
1000
|
+
{ env, allowFailure: true }
|
|
1001
|
+
);
|
|
1002
|
+
return payload?.result ?? null;
|
|
1003
|
+
}
|
|
1004
|
+
function createTurnstileWidget(env, input) {
|
|
1005
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1006
|
+
if (!accountId) {
|
|
1007
|
+
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID before creating Turnstile widgets.");
|
|
1008
|
+
}
|
|
1009
|
+
try {
|
|
1010
|
+
return cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets`, {
|
|
1011
|
+
method: "POST",
|
|
1012
|
+
env,
|
|
1013
|
+
body: {
|
|
1014
|
+
name: input.name,
|
|
1015
|
+
domains: input.domains ?? [],
|
|
1016
|
+
mode: input.mode ?? "managed"
|
|
1017
|
+
}
|
|
1018
|
+
})?.result ?? null;
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1021
|
+
throw new Error(`Cloudflare Turnstile widget creation failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
function updateTurnstileWidget(env, sitekey, input) {
|
|
1025
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1026
|
+
if (!accountId || !sitekey) {
|
|
1027
|
+
throw new Error("Configure CLOUDFLARE_ACCOUNT_ID and sitekey before updating Turnstile widgets.");
|
|
1028
|
+
}
|
|
1029
|
+
try {
|
|
1030
|
+
return cloudflareApiRequest(`/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}`, {
|
|
1031
|
+
method: "PUT",
|
|
1032
|
+
env,
|
|
1033
|
+
body: {
|
|
1034
|
+
name: input.name,
|
|
1035
|
+
domains: input.domains ?? [],
|
|
1036
|
+
mode: input.mode ?? "managed"
|
|
1037
|
+
}
|
|
1038
|
+
})?.result ?? null;
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1041
|
+
throw new Error(`Cloudflare Turnstile widget update failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
929
1044
|
function buildCloudflarePagesFunctionBindings(state) {
|
|
930
1045
|
const kvNamespaces = Object.fromEntries(
|
|
931
1046
|
Object.entries(state.kvNamespaces ?? {}).map(([key, namespace]) => {
|
|
@@ -1006,6 +1121,7 @@ function buildProvisioningSummary(deployConfig, state, target) {
|
|
|
1006
1121
|
siteUrl: target.kind === "branch" ? targetWorkersDevUrl(state.workerName) : deployConfig.siteUrl,
|
|
1007
1122
|
accountId: resolveConfiguredCloudflareAccountId(deployConfig),
|
|
1008
1123
|
pages: state.pages ?? null,
|
|
1124
|
+
turnstileWidget: state.turnstileWidgets?.formGuard ?? null,
|
|
1009
1125
|
formGuardKv: state.kvNamespaces.FORM_GUARD_KV,
|
|
1010
1126
|
sessionKv: state.kvNamespaces.SESSION ?? null,
|
|
1011
1127
|
siteDataDb: state.d1Databases.SITE_DATA_DB,
|
|
@@ -1017,6 +1133,7 @@ function buildProvisioningSummary(deployConfig, state, target) {
|
|
|
1017
1133
|
queue: state.queues?.agentWork?.name ?? null,
|
|
1018
1134
|
dlq: state.queues?.agentWork?.dlqName ?? null,
|
|
1019
1135
|
database: state.d1Databases?.SITE_DATA_DB?.databaseName ?? null,
|
|
1136
|
+
turnstileWidget: state.turnstileWidgets?.formGuard?.name ?? null,
|
|
1020
1137
|
formGuardKv: state.kvNamespaces?.FORM_GUARD_KV?.name ?? null,
|
|
1021
1138
|
railwayProject: state.services?.worker?.projectName ?? state.services?.api?.projectName ?? null,
|
|
1022
1139
|
webDomain: configuredWebDomain,
|
|
@@ -1533,14 +1650,7 @@ function resolveConfiguredContentPublicBaseUrl(deployConfig) {
|
|
|
1533
1650
|
return envOrNull("TREESEED_CONTENT_PUBLIC_BASE_URL") ?? deployConfig.cloudflare.r2?.publicBaseUrl ?? "";
|
|
1534
1651
|
}
|
|
1535
1652
|
function missingTurnstileRequirements() {
|
|
1536
|
-
|
|
1537
|
-
if (!envOrNull("TREESEED_PUBLIC_TURNSTILE_SITE_KEY")) {
|
|
1538
|
-
issues.push("Set TREESEED_PUBLIC_TURNSTILE_SITE_KEY before deploying.");
|
|
1539
|
-
}
|
|
1540
|
-
if (!envOrNull("TREESEED_TURNSTILE_SECRET_KEY")) {
|
|
1541
|
-
issues.push("Set TREESEED_TURNSTILE_SECRET_KEY before deploying.");
|
|
1542
|
-
}
|
|
1543
|
-
return issues;
|
|
1653
|
+
return [];
|
|
1544
1654
|
}
|
|
1545
1655
|
function missingContentRuntimeRequirements(deployConfig) {
|
|
1546
1656
|
const issues = [];
|
|
@@ -1564,20 +1674,6 @@ function collectMissingDeployInputs(tenantRoot) {
|
|
|
1564
1674
|
message: "Cloudflare account ID is missing. Set CLOUDFLARE_ACCOUNT_ID with treeseed config or provide it now."
|
|
1565
1675
|
});
|
|
1566
1676
|
}
|
|
1567
|
-
if (!envOrNull("TREESEED_PUBLIC_TURNSTILE_SITE_KEY")) {
|
|
1568
|
-
missing.push({
|
|
1569
|
-
key: "TREESEED_PUBLIC_TURNSTILE_SITE_KEY",
|
|
1570
|
-
label: "Turnstile public site key",
|
|
1571
|
-
message: "Turnstile public site key is missing for deploy."
|
|
1572
|
-
});
|
|
1573
|
-
}
|
|
1574
|
-
if (!envOrNull("TREESEED_TURNSTILE_SECRET_KEY")) {
|
|
1575
|
-
missing.push({
|
|
1576
|
-
key: "TREESEED_TURNSTILE_SECRET_KEY",
|
|
1577
|
-
label: "Turnstile secret key",
|
|
1578
|
-
message: "Turnstile secret key is missing for deploy."
|
|
1579
|
-
});
|
|
1580
|
-
}
|
|
1581
1677
|
if (deployConfig.providers?.content?.runtime === "team_scoped_r2_overlay" && !envOrNull("TREESEED_EDITORIAL_PREVIEW_SECRET")) {
|
|
1582
1678
|
missing.push({
|
|
1583
1679
|
key: "TREESEED_EDITORIAL_PREVIEW_SECRET",
|
|
@@ -1674,6 +1770,24 @@ function resolveExistingKvIdByName(kvNamespaces, expectedName, fallbackId) {
|
|
|
1674
1770
|
}
|
|
1675
1771
|
return kvNamespaces.find((entry) => entry?.title === expectedName)?.id ?? null;
|
|
1676
1772
|
}
|
|
1773
|
+
function resolveExistingTurnstileWidget(widgets, current) {
|
|
1774
|
+
if (!current?.name && !current?.sitekey) {
|
|
1775
|
+
return current;
|
|
1776
|
+
}
|
|
1777
|
+
const existing = widgets.find(
|
|
1778
|
+
(entry) => current.sitekey && entry?.sitekey === current.sitekey || current.name && entry?.name === current.name
|
|
1779
|
+
);
|
|
1780
|
+
if (!existing?.sitekey) {
|
|
1781
|
+
return current;
|
|
1782
|
+
}
|
|
1783
|
+
return {
|
|
1784
|
+
...current,
|
|
1785
|
+
sitekey: existing.sitekey,
|
|
1786
|
+
secret: existing.secret ?? current.secret ?? null,
|
|
1787
|
+
domains: Array.isArray(existing.domains) ? existing.domains : current.domains ?? [],
|
|
1788
|
+
mode: existing.mode ?? current.mode ?? "managed"
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1677
1791
|
function resolveExistingD1ByName(d1Databases, expectedName, current) {
|
|
1678
1792
|
if (current?.databaseId && !isPlaceholderResourceId(current.databaseId)) {
|
|
1679
1793
|
return current;
|
|
@@ -1703,6 +1817,24 @@ function deleteKvNamespace(tenantRoot, namespaceId, { env, dryRun, preview = fal
|
|
|
1703
1817
|
const deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: namespaceId, type: "kv-namespace" });
|
|
1704
1818
|
return { status: deleted.status, id: namespaceId, preview };
|
|
1705
1819
|
}
|
|
1820
|
+
function deleteTurnstileWidget(sitekey, { env, dryRun, name = null }) {
|
|
1821
|
+
if (!sitekey || isPlaceholderResourceId(sitekey)) {
|
|
1822
|
+
return { status: "missing", sitekey, name };
|
|
1823
|
+
}
|
|
1824
|
+
if (dryRun) {
|
|
1825
|
+
return { status: "planned", sitekey, name };
|
|
1826
|
+
}
|
|
1827
|
+
const accountId = env?.CLOUDFLARE_ACCOUNT_ID ?? process.env.CLOUDFLARE_ACCOUNT_ID ?? "";
|
|
1828
|
+
const path = accountId ? `/accounts/${encodeURIComponent(accountId)}/challenges/widgets/${encodeURIComponent(sitekey)}` : null;
|
|
1829
|
+
let deleted;
|
|
1830
|
+
try {
|
|
1831
|
+
deleted = deleteCloudflareApiResource(path, { env, dryRun: false, name: name ?? sitekey, type: "turnstile-widget" });
|
|
1832
|
+
} catch (error) {
|
|
1833
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1834
|
+
throw new Error(`Cloudflare Turnstile widget deletion failed. Ensure the API token has Turnstile Sites Write permission: ${detail}`);
|
|
1835
|
+
}
|
|
1836
|
+
return { status: deleted.status, sitekey, name };
|
|
1837
|
+
}
|
|
1706
1838
|
function deleteD1Database(tenantRoot, databaseName, { env, dryRun }) {
|
|
1707
1839
|
if (!databaseName) {
|
|
1708
1840
|
return { status: "missing", name: databaseName };
|
|
@@ -2385,6 +2517,7 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2385
2517
|
const queues = dryRun ? [] : listQueues(tenantRoot, env);
|
|
2386
2518
|
const buckets = dryRun ? [] : listR2Buckets(tenantRoot, env);
|
|
2387
2519
|
const pagesProjects = dryRun ? [] : listPagesProjects(tenantRoot, env);
|
|
2520
|
+
const turnstileWidgets = dryRun ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
2388
2521
|
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
2389
2522
|
kvNamespaces,
|
|
2390
2523
|
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
@@ -2402,11 +2535,17 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2402
2535
|
state.d1Databases.SITE_DATA_DB.databaseName,
|
|
2403
2536
|
state.d1Databases.SITE_DATA_DB
|
|
2404
2537
|
);
|
|
2538
|
+
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
2405
2539
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
2406
2540
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
2407
2541
|
const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
|
|
2408
2542
|
const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
|
|
2409
2543
|
const workerResult = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
|
|
2544
|
+
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
2545
|
+
env,
|
|
2546
|
+
dryRun,
|
|
2547
|
+
name: state.turnstileWidgets?.formGuard?.name
|
|
2548
|
+
});
|
|
2410
2549
|
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, dryRun });
|
|
2411
2550
|
const formGuardPreview = state.kvNamespaces.FORM_GUARD_KV.previewId && state.kvNamespaces.FORM_GUARD_KV.previewId !== state.kvNamespaces.FORM_GUARD_KV.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.previewId, { env, dryRun, preview: true }) : null;
|
|
2412
2551
|
const session = state.kvNamespaces.SESSION?.id ? deleteKvNamespace(tenantRoot, state.kvNamespaces.SESSION.id, { env, dryRun }) : null;
|
|
@@ -2454,6 +2593,7 @@ async function destroyTreeseedEnvironmentResources(tenantRoot, options = {}) {
|
|
|
2454
2593
|
const operations = {
|
|
2455
2594
|
cloudflare: [
|
|
2456
2595
|
resourceOperation("cloudflare", "worker", state.workerName, workerResult.status, workerResult),
|
|
2596
|
+
resourceOperation("cloudflare", "turnstile-widget", state.turnstileWidgets?.formGuard?.name, turnstileWidget.status, turnstileWidget),
|
|
2457
2597
|
resourceOperation("cloudflare", "kv-namespace", state.kvNamespaces.FORM_GUARD_KV.name, formGuard.status, formGuard),
|
|
2458
2598
|
...formGuardPreview ? [resourceOperation("cloudflare", "kv-namespace-preview", state.kvNamespaces.FORM_GUARD_KV.name, formGuardPreview.status, formGuardPreview)] : [],
|
|
2459
2599
|
...session ? [resourceOperation("cloudflare", "kv-namespace", state.kvNamespaces.SESSION.name, session.status, session)] : [],
|
|
@@ -2496,6 +2636,7 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2496
2636
|
const queues = listQueues(tenantRoot, env);
|
|
2497
2637
|
const buckets = dryRun ? [] : listR2Buckets(tenantRoot, env);
|
|
2498
2638
|
const pagesProjects = dryRun ? [] : listPagesProjects(tenantRoot, env);
|
|
2639
|
+
const turnstileWidgets = dryRun ? [] : listTurnstileWidgets(tenantRoot, env);
|
|
2499
2640
|
state.kvNamespaces.FORM_GUARD_KV.id = resolveExistingKvIdByName(
|
|
2500
2641
|
kvNamespaces,
|
|
2501
2642
|
state.kvNamespaces.FORM_GUARD_KV.name,
|
|
@@ -2506,11 +2647,17 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2506
2647
|
state.d1Databases.SITE_DATA_DB.databaseName,
|
|
2507
2648
|
state.d1Databases.SITE_DATA_DB
|
|
2508
2649
|
);
|
|
2650
|
+
state.turnstileWidgets.formGuard = resolveExistingTurnstileWidget(turnstileWidgets, state.turnstileWidgets?.formGuard);
|
|
2509
2651
|
const queue = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.name);
|
|
2510
2652
|
const dlq = queues.find((entry) => queueName(entry) === state.queues?.agentWork?.dlqName);
|
|
2511
2653
|
const bucket = buckets.find((entry) => entry?.name === state.content?.bucketName);
|
|
2512
2654
|
const pagesProject = pagesProjects.find((entry) => entry?.name === state.pages?.projectName);
|
|
2513
2655
|
const worker = deleteWorker(tenantRoot, state.workerName, { env, dryRun, force });
|
|
2656
|
+
const turnstileWidget = deleteTurnstileWidget(state.turnstileWidgets?.formGuard?.sitekey, {
|
|
2657
|
+
env,
|
|
2658
|
+
dryRun,
|
|
2659
|
+
name: state.turnstileWidgets?.formGuard?.name
|
|
2660
|
+
});
|
|
2514
2661
|
const formGuard = deleteKvNamespace(tenantRoot, state.kvNamespaces.FORM_GUARD_KV.id, { env, dryRun });
|
|
2515
2662
|
const database = deleteD1DatabaseForDestroy(tenantRoot, state.d1Databases.SITE_DATA_DB.databaseName, { env, dryRun, deleteData });
|
|
2516
2663
|
const deletedQueue = deleteQueueByName(tenantRoot, queue ?? { name: state.queues?.agentWork?.name }, { env, dryRun });
|
|
@@ -2524,6 +2671,7 @@ function destroyCloudflareResources(tenantRoot, options = {}) {
|
|
|
2524
2671
|
const pages = pagesProject || dryRun ? deletePagesProject(state.pages?.projectName, { env, dryRun }) : resourceOperation("cloudflare", "pages-project", state.pages?.projectName, "missing");
|
|
2525
2672
|
const operations = {
|
|
2526
2673
|
worker,
|
|
2674
|
+
turnstileWidget,
|
|
2527
2675
|
formGuard,
|
|
2528
2676
|
database,
|
|
2529
2677
|
queue: deletedQueue,
|
|
@@ -3093,12 +3241,14 @@ export {
|
|
|
3093
3241
|
collectMissingDeployInputs,
|
|
3094
3242
|
createBranchPreviewDeployTarget,
|
|
3095
3243
|
createPersistentDeployTarget,
|
|
3244
|
+
createTurnstileWidget,
|
|
3096
3245
|
deployTargetLabel,
|
|
3097
3246
|
deriveTreeseedStagingSurfaceDomain,
|
|
3098
3247
|
destroyCloudflareResources,
|
|
3099
3248
|
destroyTreeseedEnvironmentResources,
|
|
3100
3249
|
ensureGeneratedWranglerConfig,
|
|
3101
3250
|
finalizeDeploymentState,
|
|
3251
|
+
getTurnstileWidget,
|
|
3102
3252
|
hasProvisionedCloudflareResources,
|
|
3103
3253
|
isWranglerAlreadyExistsError,
|
|
3104
3254
|
listD1Databases,
|
|
@@ -3106,6 +3256,7 @@ export {
|
|
|
3106
3256
|
listPagesProjects,
|
|
3107
3257
|
listQueues,
|
|
3108
3258
|
listR2Buckets,
|
|
3259
|
+
listTurnstileWidgets,
|
|
3109
3260
|
loadDeployState,
|
|
3110
3261
|
markDeploymentInitialized,
|
|
3111
3262
|
markManagedServicesInitialized,
|
|
@@ -3132,6 +3283,7 @@ export {
|
|
|
3132
3283
|
scopeFromTarget,
|
|
3133
3284
|
shouldDeleteRailwayProjectAfterEnvironmentDestroy,
|
|
3134
3285
|
syncCloudflareSecrets,
|
|
3286
|
+
updateTurnstileWidget,
|
|
3135
3287
|
validateDeployPrerequisites,
|
|
3136
3288
|
validateDestroyPrerequisites,
|
|
3137
3289
|
verifyProvisionedCloudflareResources,
|
|
@@ -24,6 +24,14 @@ export interface TreeseedGitHubRepositoryTarget {
|
|
|
24
24
|
}
|
|
25
25
|
export declare function getGitHubAutomationMode(): string;
|
|
26
26
|
export declare function parseGitHubRepositoryFromRemote(remoteUrl: any): string | null;
|
|
27
|
+
export declare function resolveGitHubRemoteUrls(owner: any, name: any): {
|
|
28
|
+
slug: string;
|
|
29
|
+
owner: string;
|
|
30
|
+
name: string;
|
|
31
|
+
sshUrl: string;
|
|
32
|
+
httpsUrl: string;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
27
35
|
export declare function resolveGitHubRepositorySlug(tenantRoot: any): string;
|
|
28
36
|
export declare function maybeResolveGitHubRepositorySlug(tenantRoot: any): string | null;
|
|
29
37
|
export declare function resolveDefaultGitHubOwner(): string;
|
|
@@ -54,12 +62,13 @@ export declare function ensureGitHubBootstrapRepository(tenantRoot: string, { va
|
|
|
54
62
|
export declare function createGitHubRepository(input: any, { env }?: {
|
|
55
63
|
env?: NodeJS.ProcessEnv | undefined;
|
|
56
64
|
}): Promise<import("./github-api.ts").GitHubRepositorySummary>;
|
|
57
|
-
export declare function initializeGitHubRepositoryWorkingTree(cwd: any, repository: any, { defaultBranch, createStaging, commitMessage, remoteName, push, }?: {
|
|
65
|
+
export declare function initializeGitHubRepositoryWorkingTree(cwd: any, repository: any, { defaultBranch, createStaging, commitMessage, remoteName, push, forcePush, }?: {
|
|
58
66
|
defaultBranch?: string | undefined;
|
|
59
67
|
createStaging?: boolean | undefined;
|
|
60
68
|
commitMessage?: string | undefined;
|
|
61
69
|
remoteName?: string | undefined;
|
|
62
70
|
push?: boolean | undefined;
|
|
71
|
+
forcePush?: boolean | undefined;
|
|
63
72
|
}): {
|
|
64
73
|
repository: any;
|
|
65
74
|
remoteName: string;
|