@treeseed/sdk 0.6.39 → 0.6.41
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/capacity.d.ts +53 -0
- package/dist/capacity.js +100 -0
- package/dist/control-plane-client.d.ts +41 -1
- package/dist/control-plane-client.js +154 -0
- package/dist/control-plane.d.ts +6 -1
- package/dist/control-plane.js +39 -2
- package/dist/d1-store.d.ts +63 -1
- package/dist/d1-store.js +190 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -0
- package/dist/operations/services/config-runtime.js +2 -2
- package/dist/operations/services/deploy.js +3 -2
- package/dist/operations/services/knowledge-coop-launch.js +5 -28
- package/dist/operations/services/package-reference-policy.d.ts +68 -0
- package/dist/operations/services/package-reference-policy.js +135 -0
- package/dist/operations/services/project-platform.d.ts +14 -0
- package/dist/operations/services/project-platform.js +3 -2
- package/dist/operations/services/railway-api.d.ts +33 -0
- package/dist/operations/services/railway-api.js +273 -0
- package/dist/operations/services/railway-deploy.d.ts +22 -0
- package/dist/operations/services/railway-deploy.js +216 -18
- package/dist/operations/services/release-candidate.d.ts +2 -0
- package/dist/operations/services/release-candidate.js +28 -0
- package/dist/operations/services/runtime-tools.js +1 -1
- package/dist/operations-registry.js +1 -0
- package/dist/reconcile/bootstrap-systems.js +1 -1
- package/dist/reconcile/builtin-adapters.js +5 -9
- package/dist/reconcile/contracts.d.ts +1 -1
- package/dist/reconcile/desired-state.js +9 -17
- package/dist/reconcile/state.js +4 -4
- package/dist/reconcile/units.js +4 -8
- package/dist/sdk-types.d.ts +566 -3
- package/dist/sdk.d.ts +12 -1
- package/dist/sdk.js +44 -0
- package/dist/stores/operational-store.d.ts +12 -1
- package/dist/stores/operational-store.js +283 -5
- package/dist/treeseed/template-catalog/templates/starter-basic/template/treeseed.site.yaml +5 -24
- package/dist/types/agents.d.ts +27 -0
- package/dist/workflow/operations.d.ts +94 -2
- package/dist/workflow/operations.js +90 -32
- package/dist/workflow-state.js +3 -5
- package/dist/workflow.d.ts +8 -1
- package/dist/workflow.js +6 -0
- package/package.json +5 -1
package/dist/sdk.js
CHANGED
|
@@ -265,6 +265,50 @@ class AgentSdk {
|
|
|
265
265
|
const payload = await this.database.upsertWorkPolicy(request);
|
|
266
266
|
return this.envelope("work_day", "update", payload);
|
|
267
267
|
}
|
|
268
|
+
async createWorkdayRequest(request) {
|
|
269
|
+
const payload = await this.database.createWorkdayRequest(request);
|
|
270
|
+
return this.envelope("workday_request", "create", payload);
|
|
271
|
+
}
|
|
272
|
+
async listWorkdayRequests(projectId, environment, state) {
|
|
273
|
+
const payload = await this.database.listWorkdayRequests(projectId, environment, state);
|
|
274
|
+
return this.envelope("workday_request", "search", payload, { count: payload.length });
|
|
275
|
+
}
|
|
276
|
+
async claimWorkdayManagerLease(request) {
|
|
277
|
+
const payload = await this.database.claimWorkdayManagerLease(request);
|
|
278
|
+
return this.envelope("workday_manager_lease", "claim", payload);
|
|
279
|
+
}
|
|
280
|
+
async releaseWorkdayManagerLease(request) {
|
|
281
|
+
const payload = await this.database.releaseWorkdayManagerLease(request);
|
|
282
|
+
return this.envelope("workday_manager_lease", "release", payload);
|
|
283
|
+
}
|
|
284
|
+
async recordWorkerRunner(request) {
|
|
285
|
+
const payload = await this.database.recordWorkerRunner(request);
|
|
286
|
+
return this.envelope("worker_runner", "update", payload);
|
|
287
|
+
}
|
|
288
|
+
async listWorkerRunners(projectId, environment) {
|
|
289
|
+
const payload = await this.database.listWorkerRunners(projectId, environment);
|
|
290
|
+
return this.envelope("worker_runner", "search", payload, { count: payload.length });
|
|
291
|
+
}
|
|
292
|
+
async recordRepositoryClaim(request) {
|
|
293
|
+
const payload = await this.database.recordRepositoryClaim(request);
|
|
294
|
+
return this.envelope("repository_claim", "update", payload);
|
|
295
|
+
}
|
|
296
|
+
async listRepositoryClaims(projectId, repositoryId) {
|
|
297
|
+
const payload = await this.database.listRepositoryClaims(projectId, repositoryId);
|
|
298
|
+
return this.envelope("repository_claim", "search", payload, { count: payload.length });
|
|
299
|
+
}
|
|
300
|
+
async recordRunnerScaleDecision(request) {
|
|
301
|
+
const payload = await this.database.recordRunnerScaleDecision(request);
|
|
302
|
+
return this.envelope("runner_scale_decision", "create", payload);
|
|
303
|
+
}
|
|
304
|
+
async listRunnerScaleDecisions(projectId, environment, workDayId) {
|
|
305
|
+
const payload = await this.database.listRunnerScaleDecisions(projectId, environment, workDayId);
|
|
306
|
+
return this.envelope("runner_scale_decision", "search", payload, { count: payload.length });
|
|
307
|
+
}
|
|
308
|
+
async updateWorkDayGraph(request) {
|
|
309
|
+
const payload = await this.database.updateWorkDayGraph(request);
|
|
310
|
+
return this.envelope("work_day", "update", payload);
|
|
311
|
+
}
|
|
268
312
|
async listPriorityOverrides(projectId) {
|
|
269
313
|
const payload = await this.database.listPriorityOverrides(projectId);
|
|
270
314
|
return this.envelope("task", "search", payload, { count: payload.length });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PrioritySnapshot, SdkCreatePrioritySnapshotRequest, SdkAppendTaskEventRequest, SdkClaimTaskRequest, SdkCloseWorkDayRequest, SdkCompleteTaskRequest, SdkCreateReportRequest, SdkCreateTaskRequest, SdkFailTaskRequest, SdkGraphRunEntity, SdkPriorityOverrideRequest, SdkReportEntity, SdkRecordScaleDecisionRequest, SdkRecordTaskCreditsRequest, SdkStartWorkDayRequest, SdkTaskEntity, SdkTaskEventEntity, SdkTaskOutputEntity, SdkTaskProgressRequest, SdkTaskSearchRequest, SdkUpsertWorkPolicyRequest, SdkWorkDayEntity, ScaleDecision, TaskCreditLedgerEntry, WorkdayPolicy } from '../sdk-types.ts';
|
|
1
|
+
import type { PrioritySnapshot, SdkCreatePrioritySnapshotRequest, SdkAppendTaskEventRequest, SdkClaimTaskRequest, SdkCloseWorkDayRequest, SdkCompleteTaskRequest, SdkCreateReportRequest, SdkCreateTaskRequest, SdkFailTaskRequest, SdkGraphRunEntity, SdkClaimWorkdayManagerLeaseRequest, SdkCreateWorkdayRequest, SdkPriorityOverrideRequest, SdkRecordRepositoryClaimRequest, SdkRecordRunnerScaleDecisionRequest, SdkReportEntity, SdkRecordScaleDecisionRequest, SdkRecordWorkerRunnerRequest, SdkRecordTaskCreditsRequest, SdkReleaseWorkdayManagerLeaseRequest, SdkStartWorkDayRequest, SdkTaskEntity, SdkTaskEventEntity, SdkTaskOutputEntity, SdkTaskProgressRequest, SdkTaskSearchRequest, SdkUpsertWorkPolicyRequest, SdkUpdateWorkDayGraphRequest, SdkWorkDayEntity, RepositoryClaim, RunnerScaleDecision, ScaleDecision, TaskCreditLedgerEntry, WorkdayPolicy, WorkdayManagerLease, WorkdayRequest, WorkerRunner } from '../sdk-types.ts';
|
|
2
2
|
import { SqliteStoreBase, type DatabaseRow } from './helpers.ts';
|
|
3
3
|
export declare class OperationalStore extends SqliteStoreBase {
|
|
4
4
|
getWorkDay(id: string): Promise<SdkWorkDayEntity | null>;
|
|
@@ -23,6 +23,17 @@ export declare class OperationalStore extends SqliteStoreBase {
|
|
|
23
23
|
getReport(id: string): Promise<SdkReportEntity | null>;
|
|
24
24
|
getWorkPolicy(projectId: string, environment?: string): Promise<WorkdayPolicy | null>;
|
|
25
25
|
upsertWorkPolicy(request: SdkUpsertWorkPolicyRequest): Promise<WorkdayPolicy | null>;
|
|
26
|
+
createWorkdayRequest(request: SdkCreateWorkdayRequest): Promise<WorkdayRequest | null>;
|
|
27
|
+
listWorkdayRequests(projectId: string, environment: string, state?: string | null): Promise<WorkdayRequest[]>;
|
|
28
|
+
claimWorkdayManagerLease(request: SdkClaimWorkdayManagerLeaseRequest): Promise<WorkdayManagerLease | null>;
|
|
29
|
+
releaseWorkdayManagerLease(request: SdkReleaseWorkdayManagerLeaseRequest): Promise<WorkdayManagerLease | null>;
|
|
30
|
+
recordWorkerRunner(request: SdkRecordWorkerRunnerRequest): Promise<WorkerRunner | null>;
|
|
31
|
+
listWorkerRunners(projectId: string, environment: string): Promise<WorkerRunner[]>;
|
|
32
|
+
recordRepositoryClaim(request: SdkRecordRepositoryClaimRequest): Promise<RepositoryClaim | null>;
|
|
33
|
+
listRepositoryClaims(projectId: string, repositoryId?: string | null): Promise<RepositoryClaim[]>;
|
|
34
|
+
recordRunnerScaleDecision(request: SdkRecordRunnerScaleDecisionRequest): Promise<RunnerScaleDecision | null>;
|
|
35
|
+
listRunnerScaleDecisions(projectId: string, environment: string, workDayId?: string | null): Promise<RunnerScaleDecision[]>;
|
|
36
|
+
updateWorkDayGraph(request: SdkUpdateWorkDayGraphRequest): Promise<SdkWorkDayEntity | null>;
|
|
26
37
|
listPriorityOverrides(projectId: string): Promise<DatabaseRow[]>;
|
|
27
38
|
upsertPriorityOverride(request: SdkPriorityOverrideRequest): Promise<{
|
|
28
39
|
id: string;
|
|
@@ -107,6 +107,9 @@ function normalizeAutoscale(value, fallback) {
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
function workPolicyFromRow(row) {
|
|
110
|
+
const metadata = parseJsonValue(row.metadata_json, {});
|
|
111
|
+
const autoscale = normalizeAutoscale(parseJsonValue(row.autoscale_json, {}));
|
|
112
|
+
const dailyCreditBudget = Number(row.daily_credit_budget ?? row.daily_task_credit_budget ?? 0);
|
|
110
113
|
return {
|
|
111
114
|
projectId: String(row.project_id ?? ""),
|
|
112
115
|
environment: String(row.environment ?? "local"),
|
|
@@ -114,12 +117,99 @@ function workPolicyFromRow(row) {
|
|
|
114
117
|
timezone: "UTC",
|
|
115
118
|
windows: []
|
|
116
119
|
}),
|
|
117
|
-
|
|
120
|
+
enabled: row.enabled === void 0 || row.enabled === null ? metadata.enabled !== false : Number(row.enabled) !== 0,
|
|
121
|
+
startCron: String(row.start_cron ?? metadata.startCron ?? "0 9 * * 1-5"),
|
|
122
|
+
durationMinutes: Number(row.duration_minutes ?? metadata.durationMinutes ?? 480),
|
|
123
|
+
maxRunners: Number(row.max_runners ?? metadata.maxRunners ?? autoscale.maxWorkers ?? 1),
|
|
124
|
+
maxWorkersPerRunner: Number(row.max_workers_per_runner ?? metadata.maxWorkersPerRunner ?? 4),
|
|
125
|
+
dailyCreditBudget,
|
|
126
|
+
closeoutGraceMinutes: Number(row.closeout_grace_minutes ?? metadata.closeoutGraceMinutes ?? 15),
|
|
127
|
+
dailyTaskCreditBudget: dailyCreditBudget,
|
|
118
128
|
maxQueuedTasks: Number(row.max_queued_tasks ?? 0),
|
|
119
129
|
maxQueuedCredits: Number(row.max_queued_credits ?? 0),
|
|
120
|
-
autoscale
|
|
130
|
+
autoscale,
|
|
121
131
|
creditWeights: parseJsonValue(row.credit_weights_json, []),
|
|
122
|
-
metadata
|
|
132
|
+
metadata
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function workdayRequestFromRow(row) {
|
|
136
|
+
return {
|
|
137
|
+
id: String(row.id ?? ""),
|
|
138
|
+
projectId: String(row.project_id ?? ""),
|
|
139
|
+
environment: String(row.environment ?? "local"),
|
|
140
|
+
type: String(row.type ?? "one_off_run"),
|
|
141
|
+
state: String(row.state ?? "pending"),
|
|
142
|
+
workDayId: row.work_day_id === void 0 || row.work_day_id === null ? null : String(row.work_day_id),
|
|
143
|
+
requestedBy: row.requested_by === void 0 || row.requested_by === null ? null : String(row.requested_by),
|
|
144
|
+
reason: row.reason === void 0 || row.reason === null ? null : String(row.reason),
|
|
145
|
+
payload: parseJsonValue(row.payload_json, {}),
|
|
146
|
+
metadata: parseJsonValue(row.metadata_json, {}),
|
|
147
|
+
createdAt: String(row.created_at ?? nowIso()),
|
|
148
|
+
updatedAt: String(row.updated_at ?? nowIso())
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function workdayManagerLeaseFromRow(row) {
|
|
152
|
+
return {
|
|
153
|
+
id: String(row.id ?? ""),
|
|
154
|
+
projectId: String(row.project_id ?? ""),
|
|
155
|
+
environment: String(row.environment ?? "local"),
|
|
156
|
+
workDayId: row.work_day_id === void 0 || row.work_day_id === null ? null : String(row.work_day_id),
|
|
157
|
+
managerId: String(row.manager_id ?? ""),
|
|
158
|
+
state: String(row.state ?? "active"),
|
|
159
|
+
heartbeatAt: String(row.heartbeat_at ?? nowIso()),
|
|
160
|
+
expiresAt: String(row.expires_at ?? nowIso()),
|
|
161
|
+
metadata: parseJsonValue(row.metadata_json, {}),
|
|
162
|
+
createdAt: String(row.created_at ?? nowIso()),
|
|
163
|
+
updatedAt: String(row.updated_at ?? nowIso())
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function workerRunnerFromRow(row) {
|
|
167
|
+
return {
|
|
168
|
+
id: String(row.id ?? ""),
|
|
169
|
+
projectId: String(row.project_id ?? ""),
|
|
170
|
+
environment: String(row.environment ?? "local"),
|
|
171
|
+
runnerId: String(row.runner_id ?? ""),
|
|
172
|
+
runnerServiceName: String(row.runner_service_name ?? ""),
|
|
173
|
+
volumeIdentity: String(row.volume_identity ?? ""),
|
|
174
|
+
state: String(row.state ?? "active"),
|
|
175
|
+
maxLocalWorkers: Number(row.max_local_workers ?? 4),
|
|
176
|
+
activeLocalWorkers: Number(row.active_local_workers ?? 0),
|
|
177
|
+
availableCapacity: Number(row.available_capacity ?? 0),
|
|
178
|
+
lastHeartbeatAt: row.last_heartbeat_at === void 0 || row.last_heartbeat_at === null ? null : String(row.last_heartbeat_at),
|
|
179
|
+
claimedRepositoryIds: parseJsonValue(row.claimed_repository_ids_json, []),
|
|
180
|
+
metadata: parseJsonValue(row.metadata_json, {}),
|
|
181
|
+
createdAt: String(row.created_at ?? nowIso()),
|
|
182
|
+
updatedAt: String(row.updated_at ?? nowIso())
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function repositoryClaimFromRow(row) {
|
|
186
|
+
return {
|
|
187
|
+
id: String(row.id ?? ""),
|
|
188
|
+
projectId: String(row.project_id ?? ""),
|
|
189
|
+
repositoryId: String(row.repository_id ?? ""),
|
|
190
|
+
runnerId: String(row.runner_id ?? ""),
|
|
191
|
+
runnerServiceName: String(row.runner_service_name ?? ""),
|
|
192
|
+
volumeIdentity: String(row.volume_identity ?? ""),
|
|
193
|
+
lastSeenCommit: row.last_seen_commit === void 0 || row.last_seen_commit === null ? null : String(row.last_seen_commit),
|
|
194
|
+
lastTaskAt: row.last_task_at === void 0 || row.last_task_at === null ? null : String(row.last_task_at),
|
|
195
|
+
claimState: String(row.claim_state ?? "active"),
|
|
196
|
+
metadata: parseJsonValue(row.metadata_json, {}),
|
|
197
|
+
createdAt: String(row.created_at ?? nowIso()),
|
|
198
|
+
updatedAt: String(row.updated_at ?? nowIso())
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function runnerScaleDecisionFromRow(row) {
|
|
202
|
+
return {
|
|
203
|
+
id: String(row.id ?? ""),
|
|
204
|
+
projectId: String(row.project_id ?? ""),
|
|
205
|
+
environment: String(row.environment ?? "local"),
|
|
206
|
+
workDayId: row.work_day_id === void 0 || row.work_day_id === null ? null : String(row.work_day_id),
|
|
207
|
+
runnerId: row.runner_id === void 0 || row.runner_id === null ? null : String(row.runner_id),
|
|
208
|
+
runnerServiceName: row.runner_service_name === void 0 || row.runner_service_name === null ? null : String(row.runner_service_name),
|
|
209
|
+
action: String(row.action ?? "noop"),
|
|
210
|
+
reason: String(row.reason ?? "reconcile"),
|
|
211
|
+
metadata: parseJsonValue(row.metadata_json, {}),
|
|
212
|
+
createdAt: String(row.created_at ?? nowIso())
|
|
123
213
|
};
|
|
124
214
|
}
|
|
125
215
|
function prioritySnapshotFromRow(row) {
|
|
@@ -356,14 +446,22 @@ class OperationalStore extends SqliteStoreBase {
|
|
|
356
446
|
}
|
|
357
447
|
async upsertWorkPolicy(request) {
|
|
358
448
|
const timestamp = nowIso();
|
|
449
|
+
const dailyCreditBudget = Number(request.dailyCreditBudget ?? request.dailyTaskCreditBudget ?? 0);
|
|
359
450
|
await this.execute(
|
|
360
451
|
`INSERT OR REPLACE INTO work_policies (
|
|
361
|
-
project_id, environment, schedule_json, daily_task_credit_budget, max_queued_tasks, max_queued_credits, autoscale_json, credit_weights_json, metadata_json, created_at, updated_at
|
|
452
|
+
project_id, environment, schedule_json, enabled, start_cron, duration_minutes, max_runners, max_workers_per_runner, daily_credit_budget, closeout_grace_minutes, daily_task_credit_budget, max_queued_tasks, max_queued_credits, autoscale_json, credit_weights_json, metadata_json, created_at, updated_at
|
|
362
453
|
) VALUES (
|
|
363
454
|
${toSqlValue(request.projectId)},
|
|
364
455
|
${toSqlValue(request.environment)},
|
|
365
456
|
${toSqlValue(json(request.schedule))},
|
|
366
|
-
${
|
|
457
|
+
${request.enabled === false ? 0 : 1},
|
|
458
|
+
${toSqlValue(request.startCron ?? "0 9 * * 1-5")},
|
|
459
|
+
${Number(request.durationMinutes ?? 480)},
|
|
460
|
+
${Number(request.maxRunners ?? request.autoscale.maxWorkers ?? 1)},
|
|
461
|
+
${Number(request.maxWorkersPerRunner ?? 4)},
|
|
462
|
+
${dailyCreditBudget},
|
|
463
|
+
${Number(request.closeoutGraceMinutes ?? 15)},
|
|
464
|
+
${dailyCreditBudget},
|
|
367
465
|
${Number(request.maxQueuedTasks ?? 0)},
|
|
368
466
|
${Number(request.maxQueuedCredits ?? 0)},
|
|
369
467
|
${toSqlValue(json(request.autoscale))},
|
|
@@ -375,6 +473,186 @@ class OperationalStore extends SqliteStoreBase {
|
|
|
375
473
|
);
|
|
376
474
|
return this.getWorkPolicy(request.projectId, request.environment);
|
|
377
475
|
}
|
|
476
|
+
async createWorkdayRequest(request) {
|
|
477
|
+
const id = request.id ?? crypto.randomUUID();
|
|
478
|
+
const timestamp = nowIso();
|
|
479
|
+
await this.execute(
|
|
480
|
+
`INSERT INTO workday_requests (
|
|
481
|
+
id, project_id, environment, type, state, work_day_id, requested_by, reason, payload_json, metadata_json, created_at, updated_at
|
|
482
|
+
) VALUES (
|
|
483
|
+
${toSqlValue(id)},
|
|
484
|
+
${toSqlValue(request.projectId)},
|
|
485
|
+
${toSqlValue(request.environment)},
|
|
486
|
+
${toSqlValue(request.type)},
|
|
487
|
+
${toSqlValue(request.state ?? "pending")},
|
|
488
|
+
${toSqlValue(request.workDayId ?? null)},
|
|
489
|
+
${toSqlValue(request.requestedBy ?? null)},
|
|
490
|
+
${toSqlValue(request.reason ?? null)},
|
|
491
|
+
${toSqlValue(json(request.payload ?? {}))},
|
|
492
|
+
${toSqlValue(json(request.metadata ?? {}))},
|
|
493
|
+
${toSqlValue(timestamp)},
|
|
494
|
+
${toSqlValue(timestamp)}
|
|
495
|
+
)`
|
|
496
|
+
);
|
|
497
|
+
const row = await this.selectFirst(`SELECT * FROM workday_requests WHERE id = ${toSqlValue(id)} LIMIT 1`);
|
|
498
|
+
return row ? workdayRequestFromRow(row) : null;
|
|
499
|
+
}
|
|
500
|
+
async listWorkdayRequests(projectId, environment, state) {
|
|
501
|
+
if (!await this.tableExists("workday_requests")) return [];
|
|
502
|
+
const rows = await this.selectAll(
|
|
503
|
+
`SELECT * FROM workday_requests WHERE project_id = ${toSqlValue(projectId)} AND environment = ${toSqlValue(environment)}${state ? ` AND state = ${toSqlValue(state)}` : ""} ORDER BY created_at ASC`
|
|
504
|
+
);
|
|
505
|
+
return rows.map(workdayRequestFromRow);
|
|
506
|
+
}
|
|
507
|
+
async claimWorkdayManagerLease(request) {
|
|
508
|
+
const timestamp = request.now ?? nowIso();
|
|
509
|
+
const active = await this.selectFirst(
|
|
510
|
+
`SELECT * FROM workday_manager_leases WHERE project_id = ${toSqlValue(request.projectId)} AND environment = ${toSqlValue(request.environment)} AND state = 'active' ORDER BY updated_at DESC LIMIT 1`
|
|
511
|
+
);
|
|
512
|
+
if (active && String(active.manager_id ?? "") !== request.managerId) {
|
|
513
|
+
const heartbeatMs = Date.parse(String(active.heartbeat_at ?? ""));
|
|
514
|
+
const nowMs = Date.parse(timestamp);
|
|
515
|
+
const staleAfterMs = (request.staleAfterSeconds ?? request.ttlSeconds) * 1e3;
|
|
516
|
+
if (Number.isFinite(heartbeatMs) && Number.isFinite(nowMs) && nowMs - heartbeatMs <= staleAfterMs) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const id = active ? String(active.id) : request.id ?? crypto.randomUUID();
|
|
521
|
+
const expiresAt = new Date(Date.parse(timestamp) + request.ttlSeconds * 1e3).toISOString();
|
|
522
|
+
await this.execute(
|
|
523
|
+
`INSERT OR REPLACE INTO workday_manager_leases (
|
|
524
|
+
id, project_id, environment, work_day_id, manager_id, state, heartbeat_at, expires_at, metadata_json, created_at, updated_at
|
|
525
|
+
) VALUES (
|
|
526
|
+
${toSqlValue(id)},
|
|
527
|
+
${toSqlValue(request.projectId)},
|
|
528
|
+
${toSqlValue(request.environment)},
|
|
529
|
+
${toSqlValue(request.workDayId ?? active?.work_day_id ?? null)},
|
|
530
|
+
${toSqlValue(request.managerId)},
|
|
531
|
+
'active',
|
|
532
|
+
${toSqlValue(timestamp)},
|
|
533
|
+
${toSqlValue(expiresAt)},
|
|
534
|
+
${toSqlValue(json(request.metadata ?? parseJsonValue(active?.metadata_json, {})))},
|
|
535
|
+
COALESCE((SELECT created_at FROM workday_manager_leases WHERE id = ${toSqlValue(id)}), ${toSqlValue(timestamp)}),
|
|
536
|
+
${toSqlValue(timestamp)}
|
|
537
|
+
)`
|
|
538
|
+
);
|
|
539
|
+
const row = await this.selectFirst(`SELECT * FROM workday_manager_leases WHERE id = ${toSqlValue(id)} LIMIT 1`);
|
|
540
|
+
return row ? workdayManagerLeaseFromRow(row) : null;
|
|
541
|
+
}
|
|
542
|
+
async releaseWorkdayManagerLease(request) {
|
|
543
|
+
const timestamp = nowIso();
|
|
544
|
+
await this.execute(
|
|
545
|
+
`UPDATE workday_manager_leases SET state = 'released', updated_at = ${toSqlValue(timestamp)} WHERE id = ${toSqlValue(request.id)} AND manager_id = ${toSqlValue(request.managerId)}`
|
|
546
|
+
);
|
|
547
|
+
const row = await this.selectFirst(`SELECT * FROM workday_manager_leases WHERE id = ${toSqlValue(request.id)} LIMIT 1`);
|
|
548
|
+
return row ? workdayManagerLeaseFromRow(row) : null;
|
|
549
|
+
}
|
|
550
|
+
async recordWorkerRunner(request) {
|
|
551
|
+
const timestamp = nowIso();
|
|
552
|
+
const id = request.id ?? `${request.projectId}:${request.environment}:${request.runnerId}`;
|
|
553
|
+
const maxLocalWorkers = Number(request.maxLocalWorkers ?? 4);
|
|
554
|
+
const activeLocalWorkers = Number(request.activeLocalWorkers ?? 0);
|
|
555
|
+
await this.execute(
|
|
556
|
+
`INSERT OR REPLACE INTO worker_runners (
|
|
557
|
+
id, project_id, environment, runner_id, runner_service_name, volume_identity, state, max_local_workers, active_local_workers, available_capacity, last_heartbeat_at, claimed_repository_ids_json, metadata_json, created_at, updated_at
|
|
558
|
+
) VALUES (
|
|
559
|
+
${toSqlValue(id)},
|
|
560
|
+
${toSqlValue(request.projectId)},
|
|
561
|
+
${toSqlValue(request.environment)},
|
|
562
|
+
${toSqlValue(request.runnerId)},
|
|
563
|
+
${toSqlValue(request.runnerServiceName)},
|
|
564
|
+
${toSqlValue(request.volumeIdentity)},
|
|
565
|
+
${toSqlValue(request.state ?? "active")},
|
|
566
|
+
${maxLocalWorkers},
|
|
567
|
+
${activeLocalWorkers},
|
|
568
|
+
${Math.max(0, maxLocalWorkers - activeLocalWorkers)},
|
|
569
|
+
${toSqlValue(timestamp)},
|
|
570
|
+
${toSqlValue(json(request.claimedRepositoryIds ?? []))},
|
|
571
|
+
${toSqlValue(json(request.metadata ?? {}))},
|
|
572
|
+
COALESCE((SELECT created_at FROM worker_runners WHERE id = ${toSqlValue(id)}), ${toSqlValue(timestamp)}),
|
|
573
|
+
${toSqlValue(timestamp)}
|
|
574
|
+
)`
|
|
575
|
+
);
|
|
576
|
+
const row = await this.selectFirst(`SELECT * FROM worker_runners WHERE id = ${toSqlValue(id)} LIMIT 1`);
|
|
577
|
+
return row ? workerRunnerFromRow(row) : null;
|
|
578
|
+
}
|
|
579
|
+
async listWorkerRunners(projectId, environment) {
|
|
580
|
+
if (!await this.tableExists("worker_runners")) return [];
|
|
581
|
+
const rows = await this.selectAll(
|
|
582
|
+
`SELECT * FROM worker_runners WHERE project_id = ${toSqlValue(projectId)} AND environment = ${toSqlValue(environment)} ORDER BY runner_id ASC`
|
|
583
|
+
);
|
|
584
|
+
return rows.map(workerRunnerFromRow);
|
|
585
|
+
}
|
|
586
|
+
async recordRepositoryClaim(request) {
|
|
587
|
+
const timestamp = nowIso();
|
|
588
|
+
const id = request.id ?? `${request.projectId}:${request.repositoryId}:${request.runnerId}`;
|
|
589
|
+
await this.execute(
|
|
590
|
+
`INSERT OR REPLACE INTO repository_claims (
|
|
591
|
+
id, project_id, repository_id, runner_id, runner_service_name, volume_identity, last_seen_commit, last_task_at, claim_state, metadata_json, created_at, updated_at
|
|
592
|
+
) VALUES (
|
|
593
|
+
${toSqlValue(id)},
|
|
594
|
+
${toSqlValue(request.projectId)},
|
|
595
|
+
${toSqlValue(request.repositoryId)},
|
|
596
|
+
${toSqlValue(request.runnerId)},
|
|
597
|
+
${toSqlValue(request.runnerServiceName)},
|
|
598
|
+
${toSqlValue(request.volumeIdentity)},
|
|
599
|
+
${toSqlValue(request.lastSeenCommit ?? null)},
|
|
600
|
+
${toSqlValue(request.lastTaskAt ?? timestamp)},
|
|
601
|
+
${toSqlValue(request.claimState ?? "active")},
|
|
602
|
+
${toSqlValue(json(request.metadata ?? {}))},
|
|
603
|
+
COALESCE((SELECT created_at FROM repository_claims WHERE id = ${toSqlValue(id)}), ${toSqlValue(timestamp)}),
|
|
604
|
+
${toSqlValue(timestamp)}
|
|
605
|
+
)`
|
|
606
|
+
);
|
|
607
|
+
const row = await this.selectFirst(`SELECT * FROM repository_claims WHERE id = ${toSqlValue(id)} LIMIT 1`);
|
|
608
|
+
return row ? repositoryClaimFromRow(row) : null;
|
|
609
|
+
}
|
|
610
|
+
async listRepositoryClaims(projectId, repositoryId) {
|
|
611
|
+
if (!await this.tableExists("repository_claims")) return [];
|
|
612
|
+
const rows = await this.selectAll(
|
|
613
|
+
`SELECT * FROM repository_claims WHERE project_id = ${toSqlValue(projectId)}${repositoryId ? ` AND repository_id = ${toSqlValue(repositoryId)}` : ""} ORDER BY updated_at DESC`
|
|
614
|
+
);
|
|
615
|
+
return rows.map(repositoryClaimFromRow);
|
|
616
|
+
}
|
|
617
|
+
async recordRunnerScaleDecision(request) {
|
|
618
|
+
const id = request.id ?? crypto.randomUUID();
|
|
619
|
+
const timestamp = nowIso();
|
|
620
|
+
await this.execute(
|
|
621
|
+
`INSERT INTO runner_scale_decisions (
|
|
622
|
+
id, project_id, environment, work_day_id, runner_id, runner_service_name, action, reason, metadata_json, created_at
|
|
623
|
+
) VALUES (
|
|
624
|
+
${toSqlValue(id)},
|
|
625
|
+
${toSqlValue(request.projectId)},
|
|
626
|
+
${toSqlValue(request.environment)},
|
|
627
|
+
${toSqlValue(request.workDayId ?? null)},
|
|
628
|
+
${toSqlValue(request.runnerId ?? null)},
|
|
629
|
+
${toSqlValue(request.runnerServiceName ?? null)},
|
|
630
|
+
${toSqlValue(request.action)},
|
|
631
|
+
${toSqlValue(request.reason)},
|
|
632
|
+
${toSqlValue(json(request.metadata ?? {}))},
|
|
633
|
+
${toSqlValue(timestamp)}
|
|
634
|
+
)`
|
|
635
|
+
);
|
|
636
|
+
const row = await this.selectFirst(`SELECT * FROM runner_scale_decisions WHERE id = ${toSqlValue(id)} LIMIT 1`);
|
|
637
|
+
return row ? runnerScaleDecisionFromRow(row) : null;
|
|
638
|
+
}
|
|
639
|
+
async listRunnerScaleDecisions(projectId, environment, workDayId) {
|
|
640
|
+
if (!await this.tableExists("runner_scale_decisions")) return [];
|
|
641
|
+
const rows = await this.selectAll(
|
|
642
|
+
`SELECT * FROM runner_scale_decisions WHERE project_id = ${toSqlValue(projectId)} AND environment = ${toSqlValue(environment)}${workDayId ? ` AND work_day_id = ${toSqlValue(workDayId)}` : ""} ORDER BY created_at DESC`
|
|
643
|
+
);
|
|
644
|
+
return rows.map(runnerScaleDecisionFromRow);
|
|
645
|
+
}
|
|
646
|
+
async updateWorkDayGraph(request) {
|
|
647
|
+
const existing = await this.getWorkDay(request.id);
|
|
648
|
+
if (!existing) return null;
|
|
649
|
+
const currentSummary = parseJsonValue(existing.summaryJson, {});
|
|
650
|
+
const timestamp = nowIso();
|
|
651
|
+
await this.execute(
|
|
652
|
+
`UPDATE work_days SET graph_version = ${toSqlValue(request.graphVersion)}, summary_json = ${toSqlValue(json({ ...currentSummary, ...request.summaryPatch ?? {} }))}, updated_at = ${toSqlValue(timestamp)} WHERE id = ${toSqlValue(request.id)}`
|
|
653
|
+
);
|
|
654
|
+
return this.getWorkDay(request.id);
|
|
655
|
+
}
|
|
378
656
|
async listPriorityOverrides(projectId) {
|
|
379
657
|
if (!await this.tableExists("priority_overrides")) {
|
|
380
658
|
return [];
|
|
@@ -50,41 +50,22 @@ services:
|
|
|
50
50
|
environments:
|
|
51
51
|
local:
|
|
52
52
|
baseUrl: http://127.0.0.1:3000
|
|
53
|
-
|
|
53
|
+
workdayManager:
|
|
54
54
|
enabled: true
|
|
55
55
|
provider: railway
|
|
56
56
|
railway:
|
|
57
|
-
serviceName: __SITE_SLUG__-manager
|
|
57
|
+
serviceName: __SITE_SLUG__-workday-manager
|
|
58
58
|
rootDir: .
|
|
59
59
|
buildCommand: npm run build
|
|
60
|
-
startCommand: node ./node_modules/@treeseed/core/dist/services/manager.js
|
|
61
|
-
schedule: '*/5 * * * *'
|
|
62
|
-
worker:
|
|
63
|
-
enabled: true
|
|
64
|
-
provider: railway
|
|
65
|
-
railway:
|
|
66
|
-
serviceName: __SITE_SLUG__-worker
|
|
67
|
-
rootDir: .
|
|
68
|
-
buildCommand: npm run build
|
|
69
|
-
startCommand: node ./node_modules/@treeseed/core/dist/services/worker.js
|
|
70
|
-
workdayStart:
|
|
71
|
-
enabled: true
|
|
72
|
-
provider: railway
|
|
73
|
-
railway:
|
|
74
|
-
serviceName: __SITE_SLUG__-workday-start
|
|
75
|
-
rootDir: .
|
|
76
|
-
buildCommand: npm run build
|
|
77
|
-
startCommand: node ./node_modules/@treeseed/core/dist/services/workday-start.js
|
|
60
|
+
startCommand: node ./node_modules/@treeseed/core/dist/services/workday-manager.js
|
|
78
61
|
schedule: '0 9 * * 1-5'
|
|
79
|
-
|
|
62
|
+
workerRunner:
|
|
80
63
|
enabled: true
|
|
81
64
|
provider: railway
|
|
82
65
|
railway:
|
|
83
|
-
serviceName: __SITE_SLUG__-workday-report
|
|
84
66
|
rootDir: .
|
|
85
67
|
buildCommand: npm run build
|
|
86
|
-
startCommand: node ./node_modules/@treeseed/core/dist/services/
|
|
87
|
-
schedule: '5 17 * * 1-5'
|
|
68
|
+
startCommand: node ./node_modules/@treeseed/core/dist/services/worker.js
|
|
88
69
|
plugins:
|
|
89
70
|
- package: '@treeseed/core/plugin-default'
|
|
90
71
|
providers:
|
package/dist/types/agents.d.ts
CHANGED
|
@@ -33,6 +33,33 @@ export interface AgentExecutionConfig {
|
|
|
33
33
|
leaseSeconds: number;
|
|
34
34
|
retryLimit: number;
|
|
35
35
|
branchPrefix: string;
|
|
36
|
+
providerProfile?: AgentProviderProfile;
|
|
37
|
+
}
|
|
38
|
+
export type AgentProviderFallbackPolicy = 'allow_substitution' | 'require_same_model_class' | 'fail_if_unavailable' | 'ask_for_approval';
|
|
39
|
+
export interface AgentProviderLanePreference {
|
|
40
|
+
providerId?: string;
|
|
41
|
+
provider?: string;
|
|
42
|
+
laneId?: string;
|
|
43
|
+
model?: string;
|
|
44
|
+
modelClass?: string;
|
|
45
|
+
weight: number;
|
|
46
|
+
reason?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface AgentProviderFallback {
|
|
49
|
+
providerId?: string;
|
|
50
|
+
provider?: string;
|
|
51
|
+
laneId?: string;
|
|
52
|
+
model?: string;
|
|
53
|
+
modelClass?: string;
|
|
54
|
+
maxQualityPenalty?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface AgentProviderProfile {
|
|
57
|
+
requiredCapabilities: string[];
|
|
58
|
+
preferredLanes: AgentProviderLanePreference[];
|
|
59
|
+
acceptableFallbacks: AgentProviderFallback[];
|
|
60
|
+
disallowedProviders?: string[];
|
|
61
|
+
disallowedRegions?: string[];
|
|
62
|
+
fallbackPolicy: AgentProviderFallbackPolicy;
|
|
36
63
|
}
|
|
37
64
|
export interface AgentTriggerPolicy {
|
|
38
65
|
maxRunsPerCycle?: number;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type ReleaseCandidateReport } from '../operations/services/release-candidate.ts';
|
|
2
|
+
import { type DevTagBranchScope } from '../operations/services/package-reference-policy.ts';
|
|
2
3
|
import { resolveTreeseedWorkflowState, type TreeseedWorkflowStatusOptions } from '../workflow-state.ts';
|
|
3
4
|
import { type TreeseedWorkflowRunCommand, type TreeseedWorkflowRunJournal } from './runs.ts';
|
|
4
5
|
import { type TreeseedWorkflowMode } from './session.ts';
|
|
5
|
-
import type { TreeseedCloseInput, TreeseedCiInput, TreeseedConfigInput, TreeseedDestroyInput, TreeseedExportInput, TreeseedReleaseInput, TreeseedRecoverInput, TreeseedResumeInput, TreeseedSaveInput, TreeseedStageInput, TreeseedSwitchInput, TreeseedTaskBranchMetadata, TreeseedWorkflowContext, TreeseedWorkflowDevInput, TreeseedWorkflowOperationId, TreeseedWorkflowResult, TreeseedWorkflowWorktreeMode } from '../workflow.ts';
|
|
6
|
+
import type { TreeseedCloseInput, TreeseedCiInput, TreeseedConfigInput, TreeseedDestroyInput, TreeseedExportInput, TreeseedReleaseInput, TreeseedRecoverInput, TreeseedResumeInput, TreeseedSaveInput, TreeseedStageInput, TreeseedSwitchInput, TreeseedTagsCleanupInput, TreeseedTaskBranchMetadata, TreeseedWorkflowContext, TreeseedWorkflowDevInput, TreeseedWorkflowOperationId, TreeseedWorkflowResult, TreeseedWorkflowWorktreeMode } from '../workflow.ts';
|
|
6
7
|
type WorkflowWrite = NonNullable<TreeseedWorkflowContext['write']>;
|
|
7
8
|
type WorkflowStatePayload = ReturnType<typeof resolveTreeseedWorkflowState>;
|
|
8
9
|
export type TreeseedWorkflowErrorCode = 'validation_failed' | 'merge_conflict' | 'missing_runtime_auth' | 'deployment_timeout' | 'confirmation_required' | 'unsupported_transport' | 'unsupported_state' | 'workflow_locked' | 'resume_unavailable' | 'workflow_contract_missing' | 'github_workflow_failed' | 'github_auth_unavailable';
|
|
@@ -796,6 +797,54 @@ export declare function workflowStage(helpers: WorkflowOperationHelpers, input:
|
|
|
796
797
|
} & {
|
|
797
798
|
finalState?: WorkflowStatePayload;
|
|
798
799
|
}>>;
|
|
800
|
+
export declare function workflowTagsCleanup(helpers: WorkflowOperationHelpers, input?: TreeseedTagsCleanupInput): Promise<TreeseedWorkflowResult<{
|
|
801
|
+
mode: string;
|
|
802
|
+
status: string;
|
|
803
|
+
branchScope: DevTagBranchScope;
|
|
804
|
+
includePackages: string[];
|
|
805
|
+
repos: ({
|
|
806
|
+
status: string;
|
|
807
|
+
packageName: string;
|
|
808
|
+
currentVersion: string;
|
|
809
|
+
branchScope: DevTagBranchScope;
|
|
810
|
+
candidates: {
|
|
811
|
+
tagName: string;
|
|
812
|
+
reason: string;
|
|
813
|
+
branch: string | null | undefined;
|
|
814
|
+
branchSlug: string | undefined;
|
|
815
|
+
version: string | null | undefined;
|
|
816
|
+
}[];
|
|
817
|
+
candidateCount: number;
|
|
818
|
+
cleaned: string[];
|
|
819
|
+
cleanedCount: number;
|
|
820
|
+
skipped: {
|
|
821
|
+
tagName: string;
|
|
822
|
+
reason: string;
|
|
823
|
+
}[];
|
|
824
|
+
skippedCount: number;
|
|
825
|
+
name: any;
|
|
826
|
+
path: string;
|
|
827
|
+
} | {
|
|
828
|
+
status: string;
|
|
829
|
+
candidateCount: number;
|
|
830
|
+
cleaned: string[];
|
|
831
|
+
cleanedCount: number;
|
|
832
|
+
skippedCount: number;
|
|
833
|
+
packageName: string;
|
|
834
|
+
currentVersion: string;
|
|
835
|
+
branchScope: DevTagBranchScope;
|
|
836
|
+
candidates: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
837
|
+
skipped: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
838
|
+
tags: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
839
|
+
name: any;
|
|
840
|
+
path: string;
|
|
841
|
+
})[];
|
|
842
|
+
candidateCount: number;
|
|
843
|
+
cleanedCount: number;
|
|
844
|
+
skippedCount: number;
|
|
845
|
+
} & {
|
|
846
|
+
finalState?: WorkflowStatePayload;
|
|
847
|
+
}>>;
|
|
799
848
|
export declare function workflowRelease(helpers: WorkflowOperationHelpers, input: TreeseedReleaseInput): Promise<TreeseedWorkflowResult<{
|
|
800
849
|
autoResumeCandidate: {
|
|
801
850
|
runId: string;
|
|
@@ -960,8 +1009,51 @@ export declare function workflowRelease(helpers: WorkflowOperationHelpers, input
|
|
|
960
1009
|
})[];
|
|
961
1010
|
releaseInstalls: Record<string, unknown>[];
|
|
962
1011
|
devTagCleanup: {
|
|
1012
|
+
replacedDevReferenceCount: number;
|
|
1013
|
+
releasedPackageDevTagCount: number;
|
|
963
1014
|
status: string;
|
|
964
|
-
|
|
1015
|
+
branchScope: DevTagBranchScope;
|
|
1016
|
+
includePackages: string[];
|
|
1017
|
+
repos: ({
|
|
1018
|
+
status: string;
|
|
1019
|
+
packageName: string;
|
|
1020
|
+
currentVersion: string;
|
|
1021
|
+
branchScope: DevTagBranchScope;
|
|
1022
|
+
candidates: {
|
|
1023
|
+
tagName: string;
|
|
1024
|
+
reason: string;
|
|
1025
|
+
branch: string | null | undefined;
|
|
1026
|
+
branchSlug: string | undefined;
|
|
1027
|
+
version: string | null | undefined;
|
|
1028
|
+
}[];
|
|
1029
|
+
candidateCount: number;
|
|
1030
|
+
cleaned: string[];
|
|
1031
|
+
cleanedCount: number;
|
|
1032
|
+
skipped: {
|
|
1033
|
+
tagName: string;
|
|
1034
|
+
reason: string;
|
|
1035
|
+
}[];
|
|
1036
|
+
skippedCount: number;
|
|
1037
|
+
name: any;
|
|
1038
|
+
path: string;
|
|
1039
|
+
} | {
|
|
1040
|
+
status: string;
|
|
1041
|
+
candidateCount: number;
|
|
1042
|
+
cleaned: string[];
|
|
1043
|
+
cleanedCount: number;
|
|
1044
|
+
skippedCount: number;
|
|
1045
|
+
packageName: string;
|
|
1046
|
+
currentVersion: string;
|
|
1047
|
+
branchScope: DevTagBranchScope;
|
|
1048
|
+
candidates: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
1049
|
+
skipped: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
1050
|
+
tags: import("../operations/services/package-reference-policy.ts").StaleDevTagClassification[];
|
|
1051
|
+
name: any;
|
|
1052
|
+
path: string;
|
|
1053
|
+
})[];
|
|
1054
|
+
candidateCount: number;
|
|
1055
|
+
cleanedCount: number;
|
|
1056
|
+
skippedCount: number;
|
|
965
1057
|
} | {
|
|
966
1058
|
status: string;
|
|
967
1059
|
reason: string;
|