deepline 0.3.90 → 0.3.91

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 (87) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +101 -6
  2. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
  3. package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
  4. package/dist/bundling-sources/sdk/src/release.ts +7 -12
  5. package/dist/bundling-sources/sdk/src/types.ts +6 -1
  6. package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
  7. package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
  8. package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
  9. package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
  10. package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
  11. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +5 -1
  12. package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
  13. package/dist/bundling-sources/shared_libs/monitors/package.json +107 -5
  14. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +12 -7
  15. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +36 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -1
  17. package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
  29. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +46 -3
  45. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +1 -1
  46. package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
  47. package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
  48. package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
  49. package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
  50. package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
  51. package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
  52. package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
  53. package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
  54. package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
  55. package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
  56. package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
  57. package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
  58. package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +79 -6
  59. package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
  60. package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
  61. package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
  62. package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
  63. package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
  64. package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
  65. package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
  66. package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
  67. package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
  68. package/dist/cli/index.js +369 -162
  69. package/dist/cli/index.mjs +369 -162
  70. package/dist/{compiler-manifest-BaJfE-5H.d.mts → compiler-manifest-x14KL83E.d.mts} +1 -1
  71. package/dist/{compiler-manifest-BaJfE-5H.d.ts → compiler-manifest-x14KL83E.d.ts} +1 -1
  72. package/dist/index.d.mts +45 -8
  73. package/dist/index.d.ts +45 -8
  74. package/dist/index.js +61 -14
  75. package/dist/index.mjs +61 -14
  76. package/dist/install-integrity.json +54 -2
  77. package/dist/monitor-fleet-contract.d.mts +3 -10
  78. package/dist/monitor-fleet-contract.d.ts +3 -10
  79. package/dist/monitor-fleet-contract.js +2 -0
  80. package/dist/monitor-fleet-contract.mjs +1 -1
  81. package/dist/plays/bundle-play-file.d.mts +2 -2
  82. package/dist/plays/bundle-play-file.d.ts +2 -2
  83. package/dist/release.d.mts +3 -3
  84. package/dist/release.d.ts +3 -3
  85. package/dist/release.js +7 -12
  86. package/dist/release.mjs +7 -12
  87. package/package.json +2 -2
@@ -5,7 +5,7 @@
5
5
  * read/write from a runner crosses HTTP to a co-located gateway process that
6
6
  * holds the runtime Postgres (PlanetScale) pool. There is exactly one receipt
7
7
  * backend — runners have no direct-Postgres receipt path and no app runtime
8
- * API fallback (see apps/play-runner/src/entry.ts).
8
+ * API fallback (see packages/play-runtime/runner-app/index.ts).
9
9
  *
10
10
  * The module is transport-agnostic on the database side so the production
11
11
  * gateway (apps/runtime-receipt-gateway/index.ts, pooled `pg` against
@@ -0,0 +1,50 @@
1
+ import { createHash } from 'node:crypto';
2
+ import {
3
+ PLAY_SECRET_UNAVAILABLE,
4
+ playSecretUnavailableErrorFrom,
5
+ } from '../plays/secrets/errors';
6
+
7
+ const RETRY_BASE_MS = 1_000;
8
+ const RETRY_CAP_MS = 15 * 60 * 1_000;
9
+
10
+ /**
11
+ * Admission has not called a customer provider or started user code. Unknown
12
+ * failures therefore stay retryable: an internal 5xx must not discard a
13
+ * source delivery. Only a proven local configuration error is blocked.
14
+ */
15
+ export function classifyWebhookReceiptAdmissionFailure(error: unknown): {
16
+ retryable: boolean;
17
+ code: 'INTERNAL_ADMISSION_FAILURE' | typeof PLAY_SECRET_UNAVAILABLE;
18
+ } {
19
+ const unavailableSecret = playSecretUnavailableErrorFrom(error);
20
+ if (unavailableSecret) {
21
+ return { retryable: false, code: PLAY_SECRET_UNAVAILABLE };
22
+ }
23
+ return { retryable: true, code: 'INTERNAL_ADMISSION_FAILURE' };
24
+ }
25
+
26
+ /**
27
+ * Stable jitter spreads a burst without making a receipt's retry timing
28
+ * unknowable to operators. The cap makes a prolonged platform incident cheap
29
+ * while the receipt remains durable and visible for recovery.
30
+ */
31
+ export function webhookReceiptRetryDelayMs(input: {
32
+ eventId: string;
33
+ attempt: number;
34
+ }): number {
35
+ const exponent = Math.min(Math.max(input.attempt - 1, 0), 10);
36
+ const ceiling = Math.min(RETRY_CAP_MS, RETRY_BASE_MS * 2 ** exponent);
37
+ const hash = createHash('sha256')
38
+ .update(input.eventId)
39
+ .update(':')
40
+ .update(String(input.attempt))
41
+ .digest();
42
+ // Keep a nonzero delay and give concurrent deliveries independent stable
43
+ // placement across the exponential window. The first retry spans 1–2s;
44
+ // otherwise a burst of first-attempt failures would synchronise again.
45
+ const fraction = hash.readUInt32BE(0) / 0xffff_ffff;
46
+ return Math.min(
47
+ RETRY_CAP_MS,
48
+ Math.max(RETRY_BASE_MS, Math.round(ceiling * (1 + fraction))),
49
+ );
50
+ }
@@ -0,0 +1,24 @@
1
+ import { tmpdir } from 'node:os';
2
+ import { join, resolve } from 'node:path';
3
+ import type { PlayBundlingAdapter } from './index';
4
+ import { discoverPackagedLocalFiles } from '../local-file-discovery';
5
+
6
+ /** Repository source bundling shared by the API and the deploy worker.
7
+ * Callers supply their repository root; cloud validation is an API adapter.
8
+ */
9
+ export function createRepositoryPlayBundlingAdapter(options: {
10
+ projectRoot: string;
11
+ sourceIdentityRoot?: string;
12
+ }): PlayBundlingAdapter {
13
+ const sdkSourceRoot = resolve(options.projectRoot, 'packages', 'sdk', 'src');
14
+ return {
15
+ projectRoot: options.projectRoot,
16
+ sourceIdentityRoot: options.sourceIdentityRoot,
17
+ nodeModulesDir: resolve(options.projectRoot, 'node_modules'),
18
+ cacheDir: join(tmpdir(), 'deepline-play-artifacts-v37'),
19
+ sdkSourceRoot,
20
+ sdkPackageJson: resolve(sdkSourceRoot, '..', 'package.json'),
21
+ sdkEntryFile: resolve(sdkSourceRoot, 'index.ts'),
22
+ discoverPackagedLocalFiles,
23
+ };
24
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * The bounded, durable read model for direct `ctx.runPlayAsync` descendants.
3
+ *
4
+ * A child belongs to an authored launch site through `launchKey`, not through
5
+ * its play name: one Play may be launched from several sites, and an async
6
+ * launch inside a fan-out has many independently progressing invocations.
7
+ * `invocationKey` is deliberately opaque; callers use its `runId` to drill
8
+ * into one actual Run instead of reconstructing an identity from input data.
9
+ */
10
+ export type AsyncChildRunSummary = {
11
+ runId: string;
12
+ playName: string | null;
13
+ launchKey: string | null;
14
+ invocationKey: string | null;
15
+ rootBillingOwned: boolean;
16
+ status: string;
17
+ parentRunId: string | null;
18
+ rootRunId: string | null;
19
+ createdAt: number | null;
20
+ startedAt: number | null;
21
+ finishedAt: number | null;
22
+ };
23
+
24
+ export type AsyncChildRunStatusCounts = {
25
+ total: number;
26
+ queued: number;
27
+ active: number;
28
+ waiting: number;
29
+ succeeded: number;
30
+ failed: number;
31
+ cancelled: number;
32
+ unknown: number;
33
+ };
34
+
35
+ export type AsyncChildRunLaunchProjection = {
36
+ /** `null` is an older child run that predates durable authored attribution. */
37
+ launchKey: string | null;
38
+ playName: string | null;
39
+ counts: AsyncChildRunStatusCounts;
40
+ children: AsyncChildRunSummary[];
41
+ };
42
+
43
+ export type AsyncChildRunProjection = {
44
+ kind: 'async_child_runs';
45
+ /** Direct children returned by the bounded durable index read. */
46
+ returnedCount: number;
47
+ /** False means the displayed counts are a lower bound, never a fake total. */
48
+ complete: boolean;
49
+ counts: AsyncChildRunStatusCounts;
50
+ launches: AsyncChildRunLaunchProjection[];
51
+ };
52
+
53
+ /**
54
+ * Wire guard for the bounded projection. Callers must never turn a partial
55
+ * response into an invented zero-child fanout.
56
+ */
57
+ export function isAsyncChildRunProjection(
58
+ value: unknown,
59
+ ): value is AsyncChildRunProjection {
60
+ if (!value || typeof value !== 'object') return false;
61
+ const candidate = value as Record<string, unknown>;
62
+ return (
63
+ candidate.kind === 'async_child_runs' &&
64
+ typeof candidate.returnedCount === 'number' &&
65
+ typeof candidate.complete === 'boolean' &&
66
+ Array.isArray(candidate.launches) &&
67
+ candidate.counts !== null &&
68
+ typeof candidate.counts === 'object'
69
+ );
70
+ }
71
+
72
+ /**
73
+ * New async children persist root-owned billing attribution at admission. Their
74
+ * execution facts are therefore already present in the root Run's billing
75
+ * query. Historic children have no such proof and retain the compatibility
76
+ * lookup rather than being silently undercounted.
77
+ */
78
+ export function requiresLegacyChildBillingRead(input: {
79
+ billingRunId: string;
80
+ child: Pick<AsyncChildRunSummary, 'runId' | 'rootBillingOwned'>;
81
+ }): boolean {
82
+ return (
83
+ !input.child.rootBillingOwned && input.billingRunId !== input.child.runId
84
+ );
85
+ }
86
+
87
+ const EMPTY_COUNTS = (): AsyncChildRunStatusCounts => ({
88
+ total: 0,
89
+ queued: 0,
90
+ active: 0,
91
+ waiting: 0,
92
+ succeeded: 0,
93
+ failed: 0,
94
+ cancelled: 0,
95
+ unknown: 0,
96
+ });
97
+
98
+ function addStatus(counts: AsyncChildRunStatusCounts, status: string): void {
99
+ counts.total += 1;
100
+ switch (status.trim().toLowerCase()) {
101
+ case 'queued':
102
+ case 'pending':
103
+ case 'scheduled':
104
+ counts.queued += 1;
105
+ return;
106
+ case 'running':
107
+ case 'in_progress':
108
+ counts.active += 1;
109
+ return;
110
+ case 'waiting':
111
+ case 'suspended':
112
+ case 'awaiting_signal':
113
+ counts.waiting += 1;
114
+ return;
115
+ case 'completed':
116
+ case 'complete':
117
+ case 'succeeded':
118
+ case 'success':
119
+ counts.succeeded += 1;
120
+ return;
121
+ case 'failed':
122
+ case 'error':
123
+ counts.failed += 1;
124
+ return;
125
+ case 'cancelled':
126
+ case 'canceled':
127
+ case 'stopped':
128
+ counts.cancelled += 1;
129
+ return;
130
+ default:
131
+ counts.unknown += 1;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Builds the one child-run representation shared by agent/API callers and UI.
137
+ * The input must already be sorted by newest child first; grouping preserves
138
+ * that order so a consumer can display recent child Runs without re-sorting.
139
+ */
140
+ export function projectAsyncChildRuns(input: {
141
+ children: readonly AsyncChildRunSummary[];
142
+ complete: boolean;
143
+ }): AsyncChildRunProjection {
144
+ const counts = EMPTY_COUNTS();
145
+ const byLaunchKey = new Map<string, AsyncChildRunLaunchProjection>();
146
+
147
+ for (const child of input.children) {
148
+ addStatus(counts, child.status);
149
+ const mapKey = child.launchKey ?? '__unattributed_async_child__';
150
+ let launch = byLaunchKey.get(mapKey);
151
+ if (!launch) {
152
+ launch = {
153
+ launchKey: child.launchKey,
154
+ playName: child.playName,
155
+ counts: EMPTY_COUNTS(),
156
+ children: [],
157
+ };
158
+ byLaunchKey.set(mapKey, launch);
159
+ }
160
+ // A key is the authored site, so preserve the first (most-recent) name
161
+ // while still retaining exact identity on every child row.
162
+ if (!launch.playName && child.playName) launch.playName = child.playName;
163
+ addStatus(launch.counts, child.status);
164
+ launch.children.push(child);
165
+ }
166
+
167
+ return {
168
+ kind: 'async_child_runs',
169
+ returnedCount: input.children.length,
170
+ complete: input.complete,
171
+ counts,
172
+ launches: [...byLaunchKey.values()],
173
+ };
174
+ }
@@ -0,0 +1,267 @@
1
+ import type { PlayBundleArtifact } from '../artifact-types';
2
+ import type { PlayCompilerManifest } from '../compiler-manifest';
3
+ import type { PlayDeploymentStatus } from '../deployment-status';
4
+ import type {
5
+ SerializedPlayDeployment,
6
+ SerializedPlayProject,
7
+ } from './project-serializer';
8
+
9
+ /**
10
+ * The deploy worker's only channel to the control plane: the app HTTP API,
11
+ * authenticated with the per-deployment token minted at webhook intake. The
12
+ * worker holds no Convex, R2, or GitHub App credentials.
13
+ */
14
+
15
+ // Mirrors the SDK's byte-aware chunker: the Vercel request-body ceiling is
16
+ // the real constraint, not the server's 25-item batch cap.
17
+ const REGISTER_MAX_BATCH_COUNT = 3;
18
+ const REGISTER_MAX_BATCH_BYTES = 2_500_000;
19
+
20
+ export type DeployApiErrorCode =
21
+ | 'PLAY_DEPLOYMENT_SUPERSEDED'
22
+ | 'PLAY_DEPLOYMENT_TERMINAL'
23
+ | 'PLAY_PROJECT_DISABLED'
24
+ | string;
25
+
26
+ export class DeployApiError extends Error {
27
+ readonly status: number;
28
+ readonly code: DeployApiErrorCode | null;
29
+ readonly body: unknown;
30
+ constructor(input: {
31
+ message: string;
32
+ status: number;
33
+ code?: string | null;
34
+ body?: unknown;
35
+ }) {
36
+ super(input.message);
37
+ this.name = 'DeployApiError';
38
+ this.status = input.status;
39
+ this.code = input.code ?? null;
40
+ this.body = input.body;
41
+ }
42
+ }
43
+
44
+ export type RegisterArtifactEntry = {
45
+ name: string;
46
+ sourceCode: string;
47
+ sourceFiles?: Record<string, string>;
48
+ description?: string;
49
+ artifact: PlayBundleArtifact;
50
+ compilerManifest: PlayCompilerManifest;
51
+ /**
52
+ * Repo-relative path this play was built from. Per-entry, not per-request:
53
+ * one deployment registers up to 25 plays from 25 different files.
54
+ */
55
+ sourceFilePath?: string;
56
+ };
57
+
58
+ export type RegisteredArtifact = {
59
+ name?: string;
60
+ definitionId?: string;
61
+ revisionId?: string;
62
+ version?: number;
63
+ [key: string]: unknown;
64
+ };
65
+
66
+ export type DeploymentPlayState = {
67
+ name: string;
68
+ exists: boolean;
69
+ playProjectId: string | null;
70
+ /** repoFullName of the project that currently owns this play, if any. */
71
+ playProjectRepo: string | null;
72
+ /** Whether that owner is disabled; null when nothing owns the play. */
73
+ playProjectDisabled: boolean | null;
74
+ driftFromGit?: boolean;
75
+ workingRevision: {
76
+ revisionId: string;
77
+ sourceHash: string | null;
78
+ artifactHash: string | null;
79
+ graphHash: string | null;
80
+ artifactKind: string | null;
81
+ version: number | null;
82
+ } | null;
83
+ liveRevision: DeploymentPlayState['workingRevision'];
84
+ };
85
+
86
+ export class DeployApiClient {
87
+ constructor(
88
+ private readonly config: {
89
+ appBaseUrl: string;
90
+ deployToken: string;
91
+ deploymentId: string;
92
+ fetchImpl?: typeof fetch;
93
+ },
94
+ ) {}
95
+
96
+ private async request<T>(input: {
97
+ method: 'GET' | 'POST';
98
+ path: string;
99
+ body?: unknown;
100
+ }): Promise<T> {
101
+ const doFetch = this.config.fetchImpl ?? fetch;
102
+ const response = await doFetch(
103
+ `${this.config.appBaseUrl.replace(/\/$/, '')}${input.path}`,
104
+ {
105
+ method: input.method,
106
+ headers: {
107
+ Authorization: `Bearer ${this.config.deployToken}`,
108
+ ...(input.body !== undefined
109
+ ? { 'Content-Type': 'application/json' }
110
+ : {}),
111
+ },
112
+ body: input.body !== undefined ? JSON.stringify(input.body) : undefined,
113
+ },
114
+ );
115
+ const text = await response.text();
116
+ let parsed: unknown = null;
117
+ try {
118
+ parsed = text ? JSON.parse(text) : null;
119
+ } catch {
120
+ parsed = text;
121
+ }
122
+ if (!response.ok) {
123
+ const record = (parsed ?? {}) as { error?: unknown; code?: unknown };
124
+ throw new DeployApiError({
125
+ message:
126
+ typeof record.error === 'string'
127
+ ? record.error
128
+ : `Deploy API ${input.path} failed (${response.status}).`,
129
+ status: response.status,
130
+ code: typeof record.code === 'string' ? record.code : null,
131
+ body: parsed,
132
+ });
133
+ }
134
+ return parsed as T;
135
+ }
136
+
137
+ /** Every deployment-scoped route hangs off this one path. */
138
+ private deploymentPath(suffix = ''): string {
139
+ return `/api/v2/deployments/${this.config.deploymentId}${suffix}`;
140
+ }
141
+
142
+ getDeployment(): Promise<{
143
+ deployment: SerializedPlayDeployment;
144
+ project: SerializedPlayProject;
145
+ /** Server-computed; qualified when several targets build one repo. */
146
+ checkRunName: string;
147
+ /** The deploy target's workspace slug, for the manifest `org` guard. */
148
+ workspaceSlug: string | null;
149
+ }> {
150
+ return this.request({
151
+ method: 'GET',
152
+ path: this.deploymentPath(),
153
+ });
154
+ }
155
+
156
+ setStatus(input: {
157
+ status: Exclude<PlayDeploymentStatus, 'queued'>;
158
+ error?: string;
159
+ results?: unknown;
160
+ buildSeconds?: number;
161
+ checkRunId?: number;
162
+ /** Production push only: the PR whose merge produced the commit. */
163
+ mergedPullRequestNumber?: number;
164
+ /** Head-guard supersedes only: lets the server re-enqueue an uncovered head. */
165
+ observedHeadSha?: string;
166
+ }): Promise<{ ok: boolean }> {
167
+ return this.request({
168
+ method: 'POST',
169
+ path: this.deploymentPath('/status'),
170
+ body: input,
171
+ });
172
+ }
173
+
174
+ mintGitHubToken(): Promise<{
175
+ token: string;
176
+ expiresAt: string;
177
+ repoId: number;
178
+ repoFullName: string;
179
+ }> {
180
+ return this.request({
181
+ method: 'POST',
182
+ path: this.deploymentPath('/github-token'),
183
+ body: {},
184
+ });
185
+ }
186
+
187
+ playsState(names: string[]): Promise<{ plays: DeploymentPlayState[] }> {
188
+ return this.request({
189
+ method: 'POST',
190
+ path: this.deploymentPath('/plays-state'),
191
+ body: { names },
192
+ });
193
+ }
194
+
195
+ compileManifest(input: {
196
+ name: string;
197
+ sourceCode: string;
198
+ sourceFiles?: Record<string, string>;
199
+ artifact: PlayBundleArtifact;
200
+ importedPlayDependencies?: PlayCompilerManifest[];
201
+ }): Promise<{ compilerManifest: PlayCompilerManifest; warnings?: string[] }> {
202
+ return this.request({
203
+ method: 'POST',
204
+ path: '/api/v2/plays/compile-manifest',
205
+ body: input,
206
+ });
207
+ }
208
+
209
+ async registerArtifacts(
210
+ entries: RegisterArtifactEntry[],
211
+ ): Promise<RegisteredArtifact[]> {
212
+ const results: RegisteredArtifact[] = [];
213
+ let batch: RegisterArtifactEntry[] = [];
214
+ let batchBytes = 0;
215
+ const flush = async () => {
216
+ if (batch.length === 0) return;
217
+ const payload = await this.request<{ artifacts: RegisteredArtifact[] }>({
218
+ method: 'POST',
219
+ path: '/api/v2/plays/artifacts',
220
+ body: {
221
+ artifacts: batch.map((entry) => ({ ...entry, publish: false })),
222
+ },
223
+ });
224
+ results.push(...payload.artifacts);
225
+ batch = [];
226
+ batchBytes = 0;
227
+ };
228
+ for (const entry of entries) {
229
+ // Serialized UTF-8 bytes of the wire entry: sourceFiles, manifests,
230
+ // JSON escaping, and multibyte characters all count against the Vercel
231
+ // body limit, not just the two code strings.
232
+ const entryBytes = Buffer.byteLength(
233
+ JSON.stringify({ ...entry, publish: false }),
234
+ 'utf8',
235
+ );
236
+ if (
237
+ batch.length >= REGISTER_MAX_BATCH_COUNT ||
238
+ (batch.length > 0 && batchBytes + entryBytes > REGISTER_MAX_BATCH_BYTES)
239
+ ) {
240
+ await flush();
241
+ }
242
+ batch.push(entry);
243
+ batchBytes += entryBytes;
244
+ }
245
+ await flush();
246
+ return results;
247
+ }
248
+
249
+ promote(plays: Array<{ name: string; revisionId: string }>): Promise<{
250
+ success: boolean;
251
+ outcomes: Array<{
252
+ name: string;
253
+ revisionId: string;
254
+ status: 'promoted' | 'failed';
255
+ version?: number;
256
+ error?: string;
257
+ }>;
258
+ phase?: string;
259
+ error?: string;
260
+ }> {
261
+ return this.request({
262
+ method: 'POST',
263
+ path: this.deploymentPath('/promote'),
264
+ body: { plays },
265
+ });
266
+ }
267
+ }
@@ -0,0 +1,73 @@
1
+ import type { GenericId } from 'convex/values';
2
+ import type { PlayDeploymentStatus } from '../deployment-status';
3
+
4
+ export type { PlayDeploymentStatus } from '../deployment-status';
5
+
6
+ /**
7
+ * Explicit row types for the GitHub deploy control plane, mirroring
8
+ * convex/schema.ts. App code deliberately does NOT use `Doc<'...'>` here:
9
+ * the schema-derived document types degrade to `unknown` fields under the
10
+ * full-project tsc pass (the schema type is past TypeScript's inference
11
+ * budget), which is why the rest of `src/` avoids `Doc<>` too. Keep these in
12
+ * sync with the schema tables by hand; the convex typecheck still validates
13
+ * the source of truth.
14
+ */
15
+
16
+ export type GithubInstallationDoc = {
17
+ _id: GenericId<'githubInstallations'>;
18
+ _creationTime: number;
19
+ gitHost: string;
20
+ installationId: string;
21
+ orgId: string;
22
+ accountLogin: string;
23
+ accountType: 'User' | 'Organization';
24
+ targetId: number;
25
+ repositorySelection: 'all' | 'selected';
26
+ installedByUserId: string;
27
+ verifiedAt: number;
28
+ suspendedAt?: number;
29
+ deletedAt?: number;
30
+ createdAt: number;
31
+ updatedAt: number;
32
+ };
33
+
34
+ export type PlayProjectDoc = {
35
+ _id: GenericId<'playProjects'>;
36
+ _creationTime: number;
37
+ orgId: string;
38
+ gitHost: string;
39
+ installationId: string;
40
+ repoId: number;
41
+ repoFullName: string;
42
+ rootDirectory: string;
43
+ manifestPath?: string;
44
+ productionBranch: string;
45
+ checkOnPullRequest: boolean;
46
+ disabledAt?: number;
47
+ createdByUserId: string;
48
+ createdAt: number;
49
+ updatedAt: number;
50
+ };
51
+
52
+ export type PlayDeploymentDoc = {
53
+ _id: GenericId<'playDeployments'>;
54
+ _creationTime: number;
55
+ orgId: string;
56
+ playProjectId: GenericId<'playProjects'>;
57
+ commitSha: string;
58
+ ref: string;
59
+ isProduction: boolean;
60
+ trigger: 'push' | 'pull_request' | 'manual' | 'redeliver';
61
+ deliveryId?: string;
62
+ status: PlayDeploymentStatus;
63
+ checkRunId?: number;
64
+ pullRequestNumber?: number;
65
+ mergedPullRequestNumber?: number;
66
+ committerLogin?: string;
67
+ results?: unknown;
68
+ buildSeconds?: number;
69
+ startedAt?: number;
70
+ finishedAt?: number;
71
+ error?: string;
72
+ createdAt: number;
73
+ };