@treeseed/sdk 0.10.12 → 0.10.14

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.
@@ -14,6 +14,12 @@ import {
14
14
  validateRepositoryHost
15
15
  } from "./operations/services/hub-launch.js";
16
16
  import { TreeseedOperationsSdk } from "./operations/runtime.js";
17
+ import {
18
+ cancelGitHubWorkflowRun,
19
+ dispatchGitHubWorkflowRun,
20
+ formatGitHubWorkflowFailure,
21
+ waitForGitHubWorkflowRunCompletion
22
+ } from "./operations/services/github-api.js";
17
23
  import { TreeseedOperationError } from "./operations-types.js";
18
24
  import {
19
25
  AGENT_OPERATION_MODES,
@@ -32,19 +38,23 @@ export {
32
38
  TreeseedOperationError,
33
39
  TreeseedOperationsSdk,
34
40
  TreeseedWorkflowSdk,
41
+ cancelGitHubWorkflowRun,
35
42
  collectTreeseedConfigSeedValues,
36
43
  createAgentOperationEvent,
37
44
  createKnowledgeHubRepositories,
38
45
  decideAgentOperationPermission,
39
46
  defaultHubContentResolutionPolicy,
40
47
  deniedAgentOperationResult,
48
+ dispatchGitHubWorkflowRun,
41
49
  executeKnowledgeHubLaunch,
42
50
  findTreeseedOperation,
51
+ formatGitHubWorkflowFailure,
43
52
  isAgentOperationName,
44
53
  listTreeseedOperationNames,
45
54
  normalizeKnowledgeHubLaunchIntent,
46
55
  planKnowledgeHubLaunch,
47
56
  planKnowledgeHubRepositories,
48
57
  resolveAgentOperationGrant,
49
- validateRepositoryHost
58
+ validateRepositoryHost,
59
+ waitForGitHubWorkflowRunCompletion
50
60
  };
@@ -84,6 +84,10 @@ export declare class PlatformOperationStore {
84
84
  ok: true;
85
85
  operation: PlatformOperation;
86
86
  }>;
87
+ cancel(operationId: string, request: PlatformRunnerJobUpdateRequest): Promise<{
88
+ ok: true;
89
+ operation: PlatformOperation;
90
+ }>;
87
91
  private upsertRepositoryClaim;
88
92
  private renewRepositoryClaimsForRunner;
89
93
  private releaseRepositoryClaimsForRunner;
@@ -131,6 +131,9 @@ function repositoryWorkspacePath(workspaceRoot, repository = {}) {
131
131
  const root = String(workspaceRoot ?? "/data").replace(/\/+$/u, "") || "/data";
132
132
  return `${root}/repositories/${repositoryKey(repository)}/repo`;
133
133
  }
134
+ function normalizeOperationCapabilities(capabilities) {
135
+ return Array.isArray(capabilities) ? capabilities.map((entry) => String(entry ?? "").trim()).filter(Boolean) : [];
136
+ }
134
137
  function convertQuestionPlaceholders(query) {
135
138
  let index = 0;
136
139
  return query.replace(/\?/gu, () => `$${++index}`);
@@ -311,20 +314,27 @@ class PlatformOperationStore {
311
314
  const leaseSeconds = Math.max(30, Math.min(Number(input.leaseSeconds ?? 300), 3600));
312
315
  const now = isoNow(this.now);
313
316
  const leaseExpiresAt = new Date(this.now().getTime() + leaseSeconds * 1e3).toISOString();
317
+ const capabilities = normalizeOperationCapabilities(input.capabilities);
318
+ const capabilityWhere = capabilities.length > 0 ? ` AND (${capabilities.map(() => `(namespace || ':' || operation) = ?`).join(" OR ")})` : "";
319
+ const capabilityParams = capabilities;
314
320
  const rows = input.operationId ? await this.database.all(
315
321
  `SELECT * FROM platform_operations
316
322
  WHERE id = ? AND (
317
323
  status = 'queued'
318
324
  OR (status = 'leased' AND lease_expires_at IS NOT NULL AND lease_expires_at < ?)
319
325
  )
326
+ ${capabilityWhere}
320
327
  ORDER BY created_at ASC LIMIT 1`,
321
- [input.operationId, now]
328
+ [input.operationId, now, ...capabilityParams]
322
329
  ) : await this.database.all(
323
330
  `SELECT * FROM platform_operations
324
- WHERE status = 'queued'
331
+ WHERE (
332
+ status = 'queued'
325
333
  OR (status = 'leased' AND lease_expires_at IS NOT NULL AND lease_expires_at < ?)
334
+ )
335
+ ${capabilityWhere}
326
336
  ORDER BY created_at ASC LIMIT 1`,
327
- [now]
337
+ [now, ...capabilityParams]
328
338
  );
329
339
  const row = rows[0];
330
340
  if (!row) return { ok: true, operation: null };
@@ -424,6 +434,22 @@ class PlatformOperationStore {
424
434
  });
425
435
  return this.getOperation(operationId);
426
436
  }
437
+ async cancel(operationId, request) {
438
+ await this.assertRunnerUpdate(operationId, request.runnerId);
439
+ const timestamp = isoNow(this.now);
440
+ await this.database.run(
441
+ `UPDATE platform_operations
442
+ SET status = 'cancelled', error_json = ?, lease_expires_at = NULL, cancelled_at = COALESCE(cancelled_at, ?), updated_at = ?, finished_at = COALESCE(finished_at, ?)
443
+ WHERE id = ?`,
444
+ [JSON.stringify(request.error ?? { message: "Platform operation was cancelled." }), timestamp, timestamp, timestamp, operationId]
445
+ );
446
+ await this.appendPlatformOperationEvent(operationId, request.event?.kind ?? "runner.cancelled", request.event?.data ?? {});
447
+ await this.releaseRepositoryClaimsForRunner(request.runnerId, {
448
+ claimState: "released",
449
+ metadata: { operationId, status: "cancelled" }
450
+ });
451
+ return this.getOperation(operationId);
452
+ }
427
453
  async upsertRepositoryClaim(input) {
428
454
  const repositoryKeyValue = repositoryKey(input.repository);
429
455
  const timestamp = isoNow(this.now);
@@ -154,6 +154,10 @@ export interface PlatformOperationRunnerCoreClient {
154
154
  ok: true;
155
155
  operation: PlatformOperation;
156
156
  }>;
157
+ cancel?(operationId: string, request: PlatformRunnerJobUpdateRequest): Promise<{
158
+ ok: true;
159
+ operation: PlatformOperation;
160
+ }>;
157
161
  }
158
162
  export interface PlatformOperationRunnerCoreOptions {
159
163
  client: PlatformOperationRunnerCoreClient;
@@ -262,4 +266,8 @@ export declare class PlatformRunnerClient {
262
266
  ok: true;
263
267
  operation: PlatformOperation;
264
268
  }>;
269
+ cancel(operationId: string, request: PlatformRunnerJobUpdateRequest): Promise<{
270
+ ok: true;
271
+ operation: PlatformOperation;
272
+ }>;
265
273
  }
@@ -190,6 +190,7 @@ async function runPlatformOperationOnce(options) {
190
190
  const claimed = await options.client.claimJob({
191
191
  runnerId: options.runnerId,
192
192
  operationId: options.operationId ?? void 0,
193
+ capabilities: registry.keys(),
193
194
  limit: options.limit ?? 1,
194
195
  leaseSeconds: options.leaseSeconds ?? 300
195
196
  });
@@ -259,6 +260,14 @@ async function runPlatformOperationOnce(options) {
259
260
  message: error instanceof Error ? error.message : String(error)
260
261
  };
261
262
  const eventKind = failure.message.toLowerCase().includes("cancel") ? "runner.cancelled" : "runner.retry_safe_failure";
263
+ if (eventKind === "runner.cancelled" && options.client.cancel) {
264
+ const cancelled = await options.client.cancel(operation.id, {
265
+ runnerId: options.runnerId,
266
+ error: failure,
267
+ event: { kind: eventKind, data: failure }
268
+ });
269
+ return { ok: false, claimed: true, operation: cancelled.operation, error: failure };
270
+ }
262
271
  if (eventKind === "runner.cancelled" && options.client.getOperation) {
263
272
  await options.client.appendEvent(operation.id, {
264
273
  runnerId: options.runnerId,
@@ -399,6 +408,16 @@ class PlatformRunnerClient {
399
408
  return response;
400
409
  });
401
410
  }
411
+ cancel(operationId, request) {
412
+ return this.requestJson(`${PLATFORM_OPERATION_ENDPOINTS.runnerJob(operationId)}/cancel`, {
413
+ method: "POST",
414
+ body: { ...request, marketId: this.marketId }
415
+ }).then((response) => {
416
+ assertPlatformOperationOkEnvelope(response, "Platform operation cancellation response");
417
+ assertPlatformOperation(response.operation);
418
+ return response;
419
+ });
420
+ }
402
421
  }
403
422
  export {
404
423
  PLATFORM_OPERATION_ENDPOINTS,
package/dist/remote.js CHANGED
@@ -56,10 +56,10 @@ class RemoteTreeseedClient {
56
56
  }
57
57
  }
58
58
  class RemoteTreeseedAuthClient {
59
+ client;
59
60
  constructor(client) {
60
61
  this.client = client;
61
62
  }
62
- client;
63
63
  startDeviceFlow(request) {
64
64
  return this.client.requestJson("/auth/device/start", {
65
65
  method: "POST",
@@ -85,10 +85,10 @@ class RemoteTreeseedAuthClient {
85
85
  }
86
86
  }
87
87
  class RemoteTreeseedSdkClient {
88
+ client;
88
89
  constructor(client) {
89
90
  this.client = client;
90
91
  }
91
- client;
92
92
  execute(operation, request) {
93
93
  return this.client.requestJson(`/sdk/${encodeURIComponent(operation)}`, {
94
94
  method: "POST",
@@ -182,10 +182,10 @@ class CloudflareQueuePushClient {
182
182
  }
183
183
  }
184
184
  class RemoteTreeseedOperationsClient {
185
+ client;
185
186
  constructor(client) {
186
187
  this.client = client;
187
188
  }
188
- client;
189
189
  execute(operation, request) {
190
190
  return this.client.requestJson(`/operations/${encodeURIComponent(operation)}`, {
191
191
  method: "POST",
@@ -195,10 +195,10 @@ class RemoteTreeseedOperationsClient {
195
195
  }
196
196
  }
197
197
  class RemoteTreeseedDispatchClient {
198
+ client;
198
199
  constructor(client) {
199
200
  this.client = client;
200
201
  }
201
- client;
202
202
  dispatch(projectId, request) {
203
203
  return this.client.requestJson(`/v1/projects/${encodeURIComponent(projectId)}/dispatch`, {
204
204
  method: "POST",
@@ -208,10 +208,10 @@ class RemoteTreeseedDispatchClient {
208
208
  }
209
209
  }
210
210
  class RemoteTreeseedJobsClient {
211
+ client;
211
212
  constructor(client) {
212
213
  this.client = client;
213
214
  }
214
- client;
215
215
  get(jobId) {
216
216
  return this.client.requestJson(`/v1/jobs/${encodeURIComponent(jobId)}`, {
217
217
  requireAuth: true
@@ -230,10 +230,10 @@ class RemoteTreeseedJobsClient {
230
230
  }
231
231
  }
232
232
  class RemoteTreeseedRunnerClient {
233
+ client;
233
234
  constructor(client) {
234
235
  this.client = client;
235
236
  }
236
- client;
237
237
  pull(projectId, request = {}) {
238
238
  return this.client.requestJson(`/v1/projects/${encodeURIComponent(projectId)}/runner/jobs/pull`, {
239
239
  method: "POST",
@@ -111,7 +111,7 @@ async function querySubscribers() {
111
111
  const child = spawnProcess('wrangler', [
112
112
  'd1',
113
113
  'execute',
114
- 'karyon-docs-site-data',
114
+ 'docs-site-data',
115
115
  '--local',
116
116
  '--config',
117
117
  fixtureWranglerConfig,
@@ -386,7 +386,9 @@ function resolveRepositorySlug(repoDir) {
386
386
  return parseGitHubRepositoryFromRemote(remote);
387
387
  }
388
388
  function isProductionLikeTarget(repository, siteUrl) {
389
- return repository === 'karyon-life/karyon' || /karyon\.life/i.test(siteUrl ?? '');
389
+ const productionRepositories = new Set(['knowledge-coop/market', 'treeseed-ai/market']);
390
+ const normalizedSiteUrl = String(siteUrl ?? '').replace(/\/+$/u, '').toLowerCase();
391
+ return productionRepositories.has(repository) || ['https://treeseed.ai', 'https://www.treeseed.ai'].includes(normalizedSiteUrl);
390
392
  }
391
393
  async function waitForGitHubWorkflow(repository, headSha, { timeoutMs = 900000 } = {}) {
392
394
  const startedAt = Date.now();
@@ -15,7 +15,9 @@ export declare const PROJECT_EXECUTION_OWNERS: readonly ["project_api", "project
15
15
  export declare const REMOTE_JOB_STATUSES: readonly ["pending", "claimed", "running", "completed", "failed", "cancelled"];
16
16
  export declare const PROJECT_ENVIRONMENT_NAMES: readonly ["local", "staging", "prod"];
17
17
  export declare const PROJECT_DEPLOYMENT_KINDS: readonly ["provision", "code", "content", "mixed"];
18
- export declare const PROJECT_DEPLOYMENT_STATUSES: readonly ["pending", "running", "succeeded", "failed", "cancelled"];
18
+ export declare const PROJECT_WEB_DEPLOYMENT_ACTIONS: readonly ["deploy_web", "publish_content", "monitor"];
19
+ export declare const PROJECT_DEPLOYMENT_ENVIRONMENTS: readonly ["staging", "prod"];
20
+ export declare const PROJECT_DEPLOYMENT_STATUSES: readonly ["pending", "queued", "claimed", "dispatching", "running", "monitoring", "succeeded", "failed", "cancelled", "timed_out"];
19
21
  export declare const PROJECT_INFRA_RESOURCE_PROVIDERS: readonly ["cloudflare", "railway", "github", "market"];
20
22
  export declare const PROJECT_INFRA_RESOURCE_KINDS: readonly ["pages", "worker", "r2", "d1", "queue", "dlq", "railway_project", "railway_service", "railway_schedule"];
21
23
  export declare const AGENT_POOL_STATUSES: readonly ["pending", "active", "degraded", "offline"];
@@ -37,7 +39,12 @@ export type ProjectExecutionOwner = (typeof PROJECT_EXECUTION_OWNERS)[number];
37
39
  export type RemoteJobStatus = (typeof REMOTE_JOB_STATUSES)[number];
38
40
  export type ProjectEnvironmentName = (typeof PROJECT_ENVIRONMENT_NAMES)[number];
39
41
  export type ProjectDeploymentKind = (typeof PROJECT_DEPLOYMENT_KINDS)[number];
42
+ export type ProjectWebDeploymentAction = (typeof PROJECT_WEB_DEPLOYMENT_ACTIONS)[number];
43
+ export type ProjectDeploymentEnvironment = (typeof PROJECT_DEPLOYMENT_ENVIRONMENTS)[number];
40
44
  export type ProjectDeploymentStatus = (typeof PROJECT_DEPLOYMENT_STATUSES)[number];
45
+ export type ProjectWebMonitorStatus = 'healthy' | 'degraded' | 'failed' | 'unknown';
46
+ export type ProjectWebMonitorCheckStatus = 'passed' | 'warning' | 'failed' | 'skipped';
47
+ export type ProjectWebMonitorCheckSource = 'market' | 'github' | 'cloudflare' | 'http' | 'sdk';
41
48
  export type ProjectInfrastructureResourceProvider = (typeof PROJECT_INFRA_RESOURCE_PROVIDERS)[number];
42
49
  export type ProjectInfrastructureResourceKind = (typeof PROJECT_INFRA_RESOURCE_KINDS)[number];
43
50
  export type AgentPoolStatus = (typeof AGENT_POOL_STATUSES)[number];
@@ -194,20 +201,108 @@ export interface ProjectInfrastructureResource {
194
201
  }
195
202
  export interface ProjectDeployment {
196
203
  id: string;
204
+ teamId: string | null;
197
205
  projectId: string;
198
206
  environment: ProjectEnvironmentName;
199
207
  deploymentKind: ProjectDeploymentKind;
208
+ action: ProjectWebDeploymentAction | string;
200
209
  status: ProjectDeploymentStatus;
210
+ platformOperationId: string | null;
211
+ retryOfDeploymentId: string | null;
212
+ resumedFromDeploymentId: string | null;
213
+ idempotencyKey: string | null;
214
+ requestedByUserId: string | null;
201
215
  sourceRef: string | null;
202
216
  releaseTag: string | null;
203
217
  commitSha: string | null;
204
218
  triggeredByType: string | null;
205
219
  triggeredById: string | null;
220
+ repository?: Record<string, unknown>;
221
+ externalWorkflow?: Record<string, unknown>;
222
+ target?: Record<string, unknown>;
223
+ monitor?: Record<string, unknown>;
224
+ summary: string | null;
225
+ error?: Record<string, unknown>;
206
226
  metadata?: Record<string, unknown>;
207
227
  startedAt: string | null;
208
228
  finishedAt: string | null;
209
229
  createdAt: string;
210
230
  updatedAt: string;
231
+ completedAt: string | null;
232
+ }
233
+ export interface ProjectWebMonitorCheck {
234
+ key: string;
235
+ label: string;
236
+ status: ProjectWebMonitorCheckStatus;
237
+ summary: string;
238
+ source: ProjectWebMonitorCheckSource;
239
+ url?: string;
240
+ inspectCommand?: string;
241
+ }
242
+ export interface ProjectWebMonitorResult {
243
+ environment: ProjectDeploymentEnvironment;
244
+ status: ProjectWebMonitorStatus;
245
+ checkedAt: string;
246
+ checks: ProjectWebMonitorCheck[];
247
+ urls: string[];
248
+ warnings: string[];
249
+ }
250
+ export interface ProjectDeploymentEvent {
251
+ id: string;
252
+ deploymentId: string;
253
+ projectId: string;
254
+ teamId: string;
255
+ operationId: string | null;
256
+ kind: string;
257
+ message: string;
258
+ status: string | null;
259
+ severity: string;
260
+ sequence: number;
261
+ payload?: Record<string, unknown>;
262
+ createdAt: string;
263
+ }
264
+ export interface ProjectDeploymentActionAvailability {
265
+ environment: ProjectDeploymentEnvironment;
266
+ action: ProjectWebDeploymentAction;
267
+ available: boolean;
268
+ blockedBy: Array<{
269
+ code: string;
270
+ message: string;
271
+ href?: string;
272
+ }>;
273
+ }
274
+ export interface ProjectDeploymentReadiness {
275
+ ready: boolean;
276
+ blockers: Array<{
277
+ code: string;
278
+ message: string;
279
+ href?: string;
280
+ }>;
281
+ checks: Array<{
282
+ code: string;
283
+ label: string;
284
+ ready: boolean;
285
+ message: string;
286
+ href?: string;
287
+ }>;
288
+ }
289
+ export interface CreateProjectWebDeploymentRequest {
290
+ environment: ProjectDeploymentEnvironment;
291
+ action: ProjectWebDeploymentAction;
292
+ source?: 'market_ui' | 'market_api' | 'cli' | 'launch_flow';
293
+ reason?: string;
294
+ idempotencyKey?: string;
295
+ previewId?: string | null;
296
+ dryRun?: boolean;
297
+ confirmProduction?: boolean;
298
+ }
299
+ export interface CreateProjectWebDeploymentResponse {
300
+ ok: true;
301
+ deployment: ProjectDeployment;
302
+ operation: Record<string, unknown>;
303
+ pollUrl: string;
304
+ eventsUrl: string;
305
+ stateUrl: string;
211
306
  }
212
307
  export interface AgentPoolAutoscalePolicy {
213
308
  minWorkers: number;
@@ -1909,14 +2004,27 @@ export interface CreateProjectDeploymentRequest {
1909
2004
  environment: ProjectEnvironmentName;
1910
2005
  deploymentKind: ProjectDeploymentKind;
1911
2006
  status?: ProjectDeploymentStatus;
2007
+ action?: ProjectWebDeploymentAction | string;
2008
+ platformOperationId?: string | null;
2009
+ retryOfDeploymentId?: string | null;
2010
+ resumedFromDeploymentId?: string | null;
2011
+ idempotencyKey?: string | null;
2012
+ requestedByUserId?: string | null;
1912
2013
  sourceRef?: string | null;
1913
2014
  releaseTag?: string | null;
1914
2015
  commitSha?: string | null;
1915
2016
  triggeredByType?: string | null;
1916
2017
  triggeredById?: string | null;
2018
+ repository?: Record<string, unknown> | null;
2019
+ externalWorkflow?: Record<string, unknown> | null;
2020
+ target?: Record<string, unknown> | null;
2021
+ monitor?: Record<string, unknown> | null;
2022
+ summary?: string | null;
2023
+ error?: Record<string, unknown> | null;
1917
2024
  metadata?: Record<string, unknown> | null;
1918
2025
  startedAt?: string | null;
1919
2026
  finishedAt?: string | null;
2027
+ completedAt?: string | null;
1920
2028
  }
1921
2029
  export interface UpsertAgentPoolRequest {
1922
2030
  id?: string;
package/dist/sdk-types.js CHANGED
@@ -41,7 +41,9 @@ const PROJECT_EXECUTION_OWNERS = ["project_api", "project_runner", "market"];
41
41
  const REMOTE_JOB_STATUSES = ["pending", "claimed", "running", "completed", "failed", "cancelled"];
42
42
  const PROJECT_ENVIRONMENT_NAMES = ["local", "staging", "prod"];
43
43
  const PROJECT_DEPLOYMENT_KINDS = ["provision", "code", "content", "mixed"];
44
- const PROJECT_DEPLOYMENT_STATUSES = ["pending", "running", "succeeded", "failed", "cancelled"];
44
+ const PROJECT_WEB_DEPLOYMENT_ACTIONS = ["deploy_web", "publish_content", "monitor"];
45
+ const PROJECT_DEPLOYMENT_ENVIRONMENTS = ["staging", "prod"];
46
+ const PROJECT_DEPLOYMENT_STATUSES = ["pending", "queued", "claimed", "dispatching", "running", "monitoring", "succeeded", "failed", "cancelled", "timed_out"];
45
47
  const PROJECT_INFRA_RESOURCE_PROVIDERS = ["cloudflare", "railway", "github", "market"];
46
48
  const PROJECT_INFRA_RESOURCE_KINDS = [
47
49
  "pages",
@@ -67,6 +69,7 @@ function projectConnectionModeFromHosting(kind, registration = "none") {
67
69
  export {
68
70
  AGENT_POOL_STATUSES,
69
71
  PROJECT_CONNECTION_MODES,
72
+ PROJECT_DEPLOYMENT_ENVIRONMENTS,
70
73
  PROJECT_DEPLOYMENT_KINDS,
71
74
  PROJECT_DEPLOYMENT_STATUSES,
72
75
  PROJECT_ENVIRONMENT_NAMES,
@@ -74,6 +77,7 @@ export {
74
77
  PROJECT_INFRA_RESOURCE_KINDS,
75
78
  PROJECT_INFRA_RESOURCE_PROVIDERS,
76
79
  PROJECT_RUNNER_REGISTRATION_STATES,
80
+ PROJECT_WEB_DEPLOYMENT_ACTIONS,
77
81
  REMOTE_JOB_STATUSES,
78
82
  SDK_DISPATCH_EXECUTION_CLASSES,
79
83
  SDK_DISPATCH_NAMESPACES,
@@ -135,20 +135,20 @@ CREATE TABLE IF NOT EXISTS "better_auth_account" (
135
135
  "accessToken" text,
136
136
  "refreshToken" text,
137
137
  "idToken" text,
138
- "accessTokenExpiresAt" integer,
139
- "refreshTokenExpiresAt" integer,
138
+ "accessTokenExpiresAt" bigint,
139
+ "refreshTokenExpiresAt" bigint,
140
140
  "scope" text,
141
141
  "password" text,
142
- "createdAt" integer NOT NULL,
143
- "updatedAt" integer NOT NULL
142
+ "createdAt" bigint NOT NULL,
143
+ "updatedAt" bigint NOT NULL
144
144
  );
145
145
 
146
146
  CREATE TABLE IF NOT EXISTS "better_auth_session" (
147
147
  "id" text PRIMARY KEY NOT NULL,
148
- "expiresAt" integer NOT NULL,
148
+ "expiresAt" bigint NOT NULL,
149
149
  "token" text NOT NULL,
150
- "createdAt" integer NOT NULL,
151
- "updatedAt" integer NOT NULL,
150
+ "createdAt" bigint NOT NULL,
151
+ "updatedAt" bigint NOT NULL,
152
152
  "ipAddress" text,
153
153
  "userAgent" text,
154
154
  "userId" text NOT NULL,
@@ -161,8 +161,8 @@ CREATE TABLE IF NOT EXISTS "better_auth_user" (
161
161
  "email" text NOT NULL,
162
162
  "emailVerified" integer DEFAULT 0 NOT NULL,
163
163
  "image" text,
164
- "createdAt" integer NOT NULL,
165
- "updatedAt" integer NOT NULL,
164
+ "createdAt" bigint NOT NULL,
165
+ "updatedAt" bigint NOT NULL,
166
166
  "username" text,
167
167
  "firstName" text,
168
168
  "lastName" text,
@@ -173,9 +173,9 @@ CREATE TABLE IF NOT EXISTS "better_auth_verification" (
173
173
  "id" text PRIMARY KEY NOT NULL,
174
174
  "identifier" text NOT NULL,
175
175
  "value" text NOT NULL,
176
- "expiresAt" integer NOT NULL,
177
- "createdAt" integer NOT NULL,
178
- "updatedAt" integer NOT NULL
176
+ "expiresAt" bigint NOT NULL,
177
+ "createdAt" bigint NOT NULL,
178
+ "updatedAt" bigint NOT NULL
179
179
  );
180
180
 
181
181
  CREATE TABLE IF NOT EXISTS "capacity_grants" (
@@ -844,22 +844,51 @@ CREATE TABLE IF NOT EXISTS "project_connections" (
844
844
  CONSTRAINT "project_connections_project_id_unique" UNIQUE("project_id")
845
845
  );
846
846
 
847
+ CREATE TABLE IF NOT EXISTS "project_deployment_events" (
848
+ "id" text PRIMARY KEY NOT NULL,
849
+ "deployment_id" text NOT NULL,
850
+ "project_id" text NOT NULL,
851
+ "team_id" text NOT NULL,
852
+ "operation_id" text,
853
+ "kind" text NOT NULL,
854
+ "message" text NOT NULL,
855
+ "status" text,
856
+ "severity" text DEFAULT 'info' NOT NULL,
857
+ "sequence" integer NOT NULL,
858
+ "payload_json" text DEFAULT '{}' NOT NULL,
859
+ "created_at" text NOT NULL
860
+ );
861
+
847
862
  CREATE TABLE IF NOT EXISTS "project_deployments" (
848
863
  "id" text PRIMARY KEY NOT NULL,
864
+ "team_id" text NOT NULL,
849
865
  "project_id" text NOT NULL,
850
866
  "environment" text NOT NULL,
851
867
  "deployment_kind" text NOT NULL,
868
+ "action" text DEFAULT 'deploy_web' NOT NULL,
852
869
  "status" text NOT NULL,
870
+ "platform_operation_id" text,
871
+ "retry_of_deployment_id" text,
872
+ "resumed_from_deployment_id" text,
873
+ "idempotency_key" text,
874
+ "requested_by_user_id" text,
853
875
  "source_ref" text,
854
876
  "release_tag" text,
855
877
  "commit_sha" text,
856
878
  "triggered_by_type" text,
857
879
  "triggered_by_id" text,
880
+ "repository_json" text DEFAULT '{}' NOT NULL,
881
+ "external_workflow_json" text DEFAULT '{}' NOT NULL,
882
+ "target_json" text DEFAULT '{}' NOT NULL,
883
+ "monitor_json" text DEFAULT '{}' NOT NULL,
884
+ "summary" text,
885
+ "error_json" text DEFAULT '{}' NOT NULL,
858
886
  "metadata_json" text,
859
887
  "started_at" text,
860
888
  "finished_at" text,
861
889
  "created_at" text NOT NULL,
862
- "updated_at" text NOT NULL
890
+ "updated_at" text NOT NULL,
891
+ "completed_at" text
863
892
  );
864
893
 
865
894
  CREATE TABLE IF NOT EXISTS "project_environments" (
@@ -1397,6 +1426,20 @@ CREATE TABLE IF NOT EXISTS "teams" (
1397
1426
  CONSTRAINT "teams_slug_unique" UNIQUE("slug")
1398
1427
  );
1399
1428
 
1429
+ CREATE TABLE IF NOT EXISTS "user_email_addresses" (
1430
+ "id" text PRIMARY KEY NOT NULL,
1431
+ "user_id" text NOT NULL,
1432
+ "email" text NOT NULL,
1433
+ "normalized_email" text NOT NULL,
1434
+ "status" text DEFAULT 'pending' NOT NULL,
1435
+ "is_primary" integer DEFAULT 0 NOT NULL,
1436
+ "verification_requested_at" text,
1437
+ "verified_at" text,
1438
+ "created_at" text NOT NULL,
1439
+ "updated_at" text NOT NULL,
1440
+ CONSTRAINT "user_email_addresses_normalized_email_unique" UNIQUE("normalized_email")
1441
+ );
1442
+
1400
1443
  CREATE TABLE IF NOT EXISTS "user_identities" (
1401
1444
  "id" text PRIMARY KEY NOT NULL,
1402
1445
  "user_id" text NOT NULL,
@@ -1539,7 +1582,6 @@ CREATE TABLE IF NOT EXISTS "worker_runners" (
1539
1582
  "updated_at" text NOT NULL
1540
1583
  );
1541
1584
 
1542
-
1543
1585
  -- Treeseed Market schema adoption columns
1544
1586
  ALTER TABLE "agent_messages" ADD COLUMN IF NOT EXISTS "id" integer;
1545
1587
  ALTER TABLE "agent_messages" ADD COLUMN IF NOT EXISTS "type" text;
@@ -1680,6 +1722,9 @@ ALTER TABLE "better_auth_verification" ADD COLUMN IF NOT EXISTS "value" text;
1680
1722
  ALTER TABLE "better_auth_verification" ADD COLUMN IF NOT EXISTS "expiresAt" integer;
1681
1723
  ALTER TABLE "better_auth_verification" ADD COLUMN IF NOT EXISTS "createdAt" integer;
1682
1724
  ALTER TABLE "better_auth_verification" ADD COLUMN IF NOT EXISTS "updatedAt" integer;
1725
+ ALTER TABLE "better_auth_verification" ALTER COLUMN "expiresAt" TYPE bigint;
1726
+ ALTER TABLE "better_auth_verification" ALTER COLUMN "createdAt" TYPE bigint;
1727
+ ALTER TABLE "better_auth_verification" ALTER COLUMN "updatedAt" TYPE bigint;
1683
1728
  ALTER TABLE "capacity_grants" ADD COLUMN IF NOT EXISTS "id" text;
1684
1729
  ALTER TABLE "capacity_grants" ADD COLUMN IF NOT EXISTS "capacity_provider_id" text;
1685
1730
  ALTER TABLE "capacity_grants" ADD COLUMN IF NOT EXISTS "lane_id" text;
@@ -2751,6 +2796,17 @@ ALTER TABLE "worker_runners" ADD COLUMN IF NOT EXISTS "created_at" text;
2751
2796
  ALTER TABLE "worker_runners" ADD COLUMN IF NOT EXISTS "updated_at" text;
2752
2797
  -- End Treeseed Market schema adoption columns
2753
2798
 
2799
+ -- Backfill verified account emails from existing active credential rows.
2800
+ INSERT INTO user_email_addresses (
2801
+ id, user_id, email, normalized_email, status, is_primary, verification_requested_at, verified_at, created_at, updated_at
2802
+ )
2803
+ SELECT 'email_' || md5(user_id || ':' || LOWER(email)), user_id, email, LOWER(email), 'verified', 1, created_at, COALESCE(updated_at, created_at), created_at, updated_at
2804
+ FROM market_auth_credentials
2805
+ WHERE email IS NOT NULL
2806
+ AND email != ''
2807
+ AND status = 'active'
2808
+ ON CONFLICT (normalized_email) DO NOTHING;
2809
+
2754
2810
  CREATE INDEX IF NOT EXISTS "idx_agent_pool_registrations_pool_heartbeat" ON "agent_pool_registrations" USING btree ("pool_id","heartbeat_at");
2755
2811
  CREATE INDEX IF NOT EXISTS "idx_agent_pool_scale_decisions_pool_created" ON "agent_pool_scale_decisions" USING btree ("pool_id","created_at");
2756
2812
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_agent_pools_project_environment_name" ON "agent_pools" USING btree ("project_id","environment","name");
@@ -2813,7 +2869,15 @@ CREATE INDEX IF NOT EXISTS "idx_platform_repository_claims_runner" ON "platform_
2813
2869
  CREATE INDEX IF NOT EXISTS "idx_priority_overrides_project_priority" ON "priority_overrides" USING btree ("project_id","priority","updated_at");
2814
2870
  CREATE INDEX IF NOT EXISTS "idx_priority_snapshots_project_generated" ON "priority_snapshots" USING btree ("project_id","generated_at");
2815
2871
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_project_capability_grants_project_operation" ON "project_capability_grants" USING btree ("project_id","namespace","operation");
2872
+ CREATE INDEX IF NOT EXISTS "idx_project_deployment_events_deployment_sequence" ON "project_deployment_events" USING btree ("deployment_id","sequence");
2873
+ CREATE INDEX IF NOT EXISTS "idx_project_deployment_events_project_created" ON "project_deployment_events" USING btree ("project_id","created_at");
2874
+ CREATE INDEX IF NOT EXISTS "idx_project_deployment_events_operation" ON "project_deployment_events" USING btree ("operation_id");
2875
+ CREATE INDEX IF NOT EXISTS "idx_project_deployments_project_created" ON "project_deployments" USING btree ("project_id","created_at");
2816
2876
  CREATE INDEX IF NOT EXISTS "idx_project_deployments_project_environment" ON "project_deployments" USING btree ("project_id","environment","created_at");
2877
+ CREATE INDEX IF NOT EXISTS "idx_project_deployments_project_status" ON "project_deployments" USING btree ("project_id","status","updated_at");
2878
+ CREATE INDEX IF NOT EXISTS "idx_project_deployments_operation" ON "project_deployments" USING btree ("platform_operation_id");
2879
+ CREATE INDEX IF NOT EXISTS "idx_project_deployments_team_created" ON "project_deployments" USING btree ("team_id","created_at");
2880
+ CREATE UNIQUE INDEX IF NOT EXISTS "idx_project_deployments_idempotency" ON "project_deployments" USING btree ("project_id","idempotency_key");
2817
2881
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_project_environments_project_environment" ON "project_environments" USING btree ("project_id","environment");
2818
2882
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_project_infrastructure_resource_unique" ON "project_infrastructure_resources" USING btree ("project_id","environment","provider","resource_kind","logical_name");
2819
2883
  CREATE INDEX IF NOT EXISTS "idx_project_summary_snapshots_team_generated" ON "project_summary_snapshots" USING btree ("team_id","generated_at");
@@ -2853,6 +2917,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS "idx_team_memberships_team_user" ON "team_memb
2853
2917
  CREATE INDEX IF NOT EXISTS "idx_team_web_hosts_team_provider" ON "team_web_hosts" USING btree ("team_id","provider","status");
2854
2918
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_team_web_hosts_team_provider_name" ON "team_web_hosts" USING btree ("team_id","provider","name");
2855
2919
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_teams_name" ON "teams" USING btree ("name");
2920
+ CREATE INDEX IF NOT EXISTS "idx_user_email_addresses_user" ON "user_email_addresses" USING btree ("user_id","status","is_primary");
2921
+ CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_email_addresses_normalized" ON "user_email_addresses" USING btree ("normalized_email");
2856
2922
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_identities_provider_subject" ON "user_identities" USING btree ("provider","provider_subject");
2857
2923
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_role_bindings_user_role" ON "user_role_bindings" USING btree ("user_id","role_id");
2858
2924
  CREATE UNIQUE INDEX IF NOT EXISTS "idx_users_username" ON "users" USING btree ("username");
@@ -0,0 +1,26 @@
1
+ CREATE TABLE IF NOT EXISTS "user_email_addresses" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "user_id" text NOT NULL,
4
+ "email" text NOT NULL,
5
+ "normalized_email" text NOT NULL,
6
+ "status" text DEFAULT 'pending' NOT NULL,
7
+ "is_primary" integer DEFAULT 0 NOT NULL,
8
+ "verification_requested_at" text,
9
+ "verified_at" text,
10
+ "created_at" text NOT NULL,
11
+ "updated_at" text NOT NULL,
12
+ CONSTRAINT "user_email_addresses_normalized_email_unique" UNIQUE("normalized_email")
13
+ );
14
+
15
+ CREATE INDEX IF NOT EXISTS "idx_user_email_addresses_user" ON "user_email_addresses" USING btree ("user_id","status","is_primary");
16
+ CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_email_addresses_normalized" ON "user_email_addresses" USING btree ("normalized_email");
17
+
18
+ INSERT INTO user_email_addresses (
19
+ id, user_id, email, normalized_email, status, is_primary, verification_requested_at, verified_at, created_at, updated_at
20
+ )
21
+ SELECT 'email_' || md5(user_id || ':' || LOWER(email)), user_id, email, LOWER(email), 'verified', 1, created_at, COALESCE(updated_at, created_at), created_at, updated_at
22
+ FROM market_auth_credentials
23
+ WHERE email IS NOT NULL
24
+ AND email != ''
25
+ AND status = 'active'
26
+ ON CONFLICT (normalized_email) DO NOTHING;