@code-partner/codepipe-hub 0.14.1-dev.419.g21c1b62a → 0.14.1-dev.422.gf90f06c4
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/dashboard/dist/assets/{ModelCanvas-WXJ1hqXD.js → ModelCanvas-CKATjkFe.js} +1 -1
- package/dashboard/dist/assets/{index-K3ia3VFf.js → index-D5-hoCRk.js} +72 -72
- package/dashboard/dist/index.html +1 -1
- package/hub/dist/embedded.js +655 -212
- package/hub/dist/index.js +954 -844
- package/package.json +1 -1
package/hub/dist/embedded.js
CHANGED
|
@@ -16,6 +16,15 @@ function humanSubjectOf(actor) {
|
|
|
16
16
|
return actor.principal.delegatedBy ?? null;
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
|
+
function accountOf(actor) {
|
|
20
|
+
const subjectId = humanSubjectOf(actor);
|
|
21
|
+
if (subjectId === null)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
subjectId,
|
|
25
|
+
email: actor.principal.kind === "human" && actor.displayEmail !== void 0 ? actor.displayEmail : null
|
|
26
|
+
};
|
|
27
|
+
}
|
|
19
28
|
function serviceOf(actor) {
|
|
20
29
|
return actor.principal.kind === "service" ? actor.principal.service : null;
|
|
21
30
|
}
|
|
@@ -448,7 +457,7 @@ var PLATFORM_VERSION;
|
|
|
448
457
|
var init_version_generated = __esm({
|
|
449
458
|
"../packages/shared/dist/version.generated.js"() {
|
|
450
459
|
"use strict";
|
|
451
|
-
PLATFORM_VERSION = "0.14.1-dev.
|
|
460
|
+
PLATFORM_VERSION = "0.14.1-dev.422.gf90f06c4";
|
|
452
461
|
}
|
|
453
462
|
});
|
|
454
463
|
|
|
@@ -1227,7 +1236,7 @@ function parseRepoMirror(raw) {
|
|
|
1227
1236
|
function isCredKind(value) {
|
|
1228
1237
|
return typeof value === "string" && CRED_KINDS.includes(value);
|
|
1229
1238
|
}
|
|
1230
|
-
var DEFAULT_BRANCH_PATTERN, BRANCH_SLUG_MAX_LENGTH, CYRILLIC_TRANSLIT, CRED_KINDS;
|
|
1239
|
+
var DEFAULT_BRANCH_PATTERN, BRANCH_SLUG_MAX_LENGTH, CYRILLIC_TRANSLIT, CRED_KINDS, PROJECT_ID_RE;
|
|
1231
1240
|
var init_project = __esm({
|
|
1232
1241
|
"../packages/shared/dist/project.js"() {
|
|
1233
1242
|
"use strict";
|
|
@@ -1269,6 +1278,7 @@ var init_project = __esm({
|
|
|
1269
1278
|
\u044F: "ya"
|
|
1270
1279
|
};
|
|
1271
1280
|
CRED_KINDS = ["github", "gitlab", "bitbucket", "youtrack", "contextWrite"];
|
|
1281
|
+
PROJECT_ID_RE = /^[a-z][a-z0-9-]{1,30}$/;
|
|
1272
1282
|
}
|
|
1273
1283
|
});
|
|
1274
1284
|
|
|
@@ -4231,16 +4241,40 @@ function createReadOperations(port, authorizer) {
|
|
|
4231
4241
|
return { projectId, items: rows, revision: valueRevision(rows) };
|
|
4232
4242
|
}
|
|
4233
4243
|
function projectVisibilityOf(actor) {
|
|
4234
|
-
const
|
|
4235
|
-
if (
|
|
4244
|
+
const account = accountOf(actor);
|
|
4245
|
+
if (account === null)
|
|
4236
4246
|
return null;
|
|
4237
4247
|
return {
|
|
4238
|
-
subjectId,
|
|
4239
|
-
...
|
|
4248
|
+
subjectId: account.subjectId,
|
|
4249
|
+
...account.email === null ? {} : { email: account.email },
|
|
4240
4250
|
...actor.projectScope === void 0 ? {} : { projectScope: actor.projectScope },
|
|
4241
4251
|
includeUnowned: actor.authMethod !== "cli_token"
|
|
4242
4252
|
};
|
|
4243
4253
|
}
|
|
4254
|
+
function farmsView(rows) {
|
|
4255
|
+
return {
|
|
4256
|
+
items: rows.map((r) => ({
|
|
4257
|
+
id: r.farmId,
|
|
4258
|
+
name: r.name,
|
|
4259
|
+
publicKey: r.publicKey,
|
|
4260
|
+
keyFingerprint: r.keyFingerprint,
|
|
4261
|
+
online: r.online,
|
|
4262
|
+
lastSeenAt: toOptionalTimestamp(r.lastSeenAt)
|
|
4263
|
+
}))
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
function projectDeliverablesView(projectId, rows) {
|
|
4267
|
+
return {
|
|
4268
|
+
projectId,
|
|
4269
|
+
items: rows.map((r) => ({
|
|
4270
|
+
taskKey: r.taskKey,
|
|
4271
|
+
title: r.title,
|
|
4272
|
+
phase: r.phase,
|
|
4273
|
+
updatedAt: toTimestamp(r.updatedAt),
|
|
4274
|
+
repos: r.repos.map((x) => ({ repoName: x.repoName, branchName: x.branchName, baseRef: x.baseRef }))
|
|
4275
|
+
}))
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4244
4278
|
async function visibleProjects(actor) {
|
|
4245
4279
|
const visibility = projectVisibilityOf(actor);
|
|
4246
4280
|
return visibility === null ? [] : await port.visibleProjectIds(visibility);
|
|
@@ -4709,6 +4743,15 @@ function createReadOperations(port, authorizer) {
|
|
|
4709
4743
|
const rows = await port.listRepos(query.projectId);
|
|
4710
4744
|
return reposView(query.projectId, rows);
|
|
4711
4745
|
},
|
|
4746
|
+
async listProjectDeliverables(actor, query) {
|
|
4747
|
+
await require2(actor, "delivery.read", { type: "deliverable", projectId: query.projectId });
|
|
4748
|
+
return projectDeliverablesView(query.projectId, await port.listProjectDeliverables(query.projectId));
|
|
4749
|
+
},
|
|
4750
|
+
async listFarms(actor) {
|
|
4751
|
+
await require2(actor, "account.read", { type: "account" });
|
|
4752
|
+
const account = accountOf(actor);
|
|
4753
|
+
return farmsView(account === null ? [] : await port.listFarms(account));
|
|
4754
|
+
},
|
|
4712
4755
|
async listConfigProposals(actor, query) {
|
|
4713
4756
|
await require2(actor, "project.read", { type: "project", id: query.projectId, projectId: query.projectId });
|
|
4714
4757
|
const limit = normalizeLimit(query.limit);
|
|
@@ -4938,8 +4981,16 @@ function createCommandOperations(deps) {
|
|
|
4938
4981
|
});
|
|
4939
4982
|
case "unprocessable":
|
|
4940
4983
|
throw errors.unprocessable({ details: { reason: outcome.reason } });
|
|
4984
|
+
case "not_found":
|
|
4985
|
+
throw errors.notFound();
|
|
4941
4986
|
}
|
|
4942
4987
|
}
|
|
4988
|
+
function accountActorOf(actor) {
|
|
4989
|
+
const account = accountOf(actor);
|
|
4990
|
+
if (account === null)
|
|
4991
|
+
throw errors.forbidden({ details: { reason: "human_only" } });
|
|
4992
|
+
return account;
|
|
4993
|
+
}
|
|
4943
4994
|
async function audited(actor, entry, run) {
|
|
4944
4995
|
const policy = { decision: "not_evaluated" };
|
|
4945
4996
|
const common = {
|
|
@@ -5428,6 +5479,107 @@ function createCommandOperations(deps) {
|
|
|
5428
5479
|
return { value: outcome.result, replayed: outcome.replayed };
|
|
5429
5480
|
});
|
|
5430
5481
|
},
|
|
5482
|
+
async createProject(actor, command) {
|
|
5483
|
+
const { project } = command;
|
|
5484
|
+
const projectId = project.projectId;
|
|
5485
|
+
if (!PROJECT_ID_RE.test(projectId)) {
|
|
5486
|
+
throw errors.invalidInput({
|
|
5487
|
+
message: "A project id is lowercase letters, digits and dashes, starts with a letter, 2\u201331 characters.",
|
|
5488
|
+
details: { field: "projectId" }
|
|
5489
|
+
});
|
|
5490
|
+
}
|
|
5491
|
+
for (const repo of project.repos) {
|
|
5492
|
+
if (repo.defaultBranch !== void 0 && !isValidBranchName(repo.defaultBranch)) {
|
|
5493
|
+
throw errors.invalidInput({ message: "That is not a valid git branch name.", details: { field: "repos.defaultBranch", repo: repo.name } });
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
const key = asIdempotencyKey(command.idempotencyKey);
|
|
5497
|
+
const scope = { subjectId: actor.principal.subjectId, operationId: "project.create", projectId, key };
|
|
5498
|
+
return await audited(actor, { operationId: "project.create", aggregateType: "project", aggregateId: projectId, projectId, data: { name: project.name, tracker: project.trackerProvider } }, async (policy) => {
|
|
5499
|
+
await require2(actor, "account.manage", { type: "account" }, policy);
|
|
5500
|
+
const run = await runIdempotent(idempotency, scope, { project }, async () => unwrap(await write2.createProject({ project, owner: accountActorOf(actor) })), now());
|
|
5501
|
+
return { value: run.result, replayed: run.replayed };
|
|
5502
|
+
});
|
|
5503
|
+
},
|
|
5504
|
+
async provisionOnFarm(actor, command) {
|
|
5505
|
+
const { farmId, projectId } = command;
|
|
5506
|
+
const key = asIdempotencyKey(command.idempotencyKey);
|
|
5507
|
+
const scope = { subjectId: actor.principal.subjectId, operationId: "farm.provision", projectId, key };
|
|
5508
|
+
return await audited(actor, { operationId: "farm.provision", aggregateType: "project", aggregateId: projectId, projectId, data: { farmId } }, async (policy) => {
|
|
5509
|
+
await require2(actor, "project.manage", { type: "project", id: projectId, projectId }, policy);
|
|
5510
|
+
const run = await runIdempotent(idempotency, scope, { farmId, projectId }, async () => {
|
|
5511
|
+
const row = unwrap(await write2.provisionOnFarm({ farmId, projectId, sealedBundle: command.sealedBundle, actor: accountActorOf(actor) }));
|
|
5512
|
+
return { projectId, farmId, ok: row.ok, daemonStatus: row.daemonStatus, error: row.error };
|
|
5513
|
+
}, now());
|
|
5514
|
+
return { value: run.result, replayed: run.replayed };
|
|
5515
|
+
});
|
|
5516
|
+
},
|
|
5517
|
+
async provisionGhRunners(actor, command) {
|
|
5518
|
+
const { runnerId, projectId } = command;
|
|
5519
|
+
const key = asIdempotencyKey(command.idempotencyKey);
|
|
5520
|
+
const scope = { subjectId: actor.principal.subjectId, operationId: "runner.provisionGhRunners", projectId, key };
|
|
5521
|
+
return await audited(actor, { operationId: "runner.provisionGhRunners", aggregateType: "project", aggregateId: projectId, projectId, data: { runnerId } }, async (policy) => {
|
|
5522
|
+
await require2(actor, "project.manage", { type: "project", id: projectId, projectId }, policy);
|
|
5523
|
+
const run = await runIdempotent(idempotency, scope, { runnerId, projectId }, async () => {
|
|
5524
|
+
const row = unwrap(await write2.provisionGhRunners({ runnerId, projectId, sealedBundle: command.sealedBundle, actor: accountActorOf(actor) }));
|
|
5525
|
+
return {
|
|
5526
|
+
projectId,
|
|
5527
|
+
runnerId,
|
|
5528
|
+
ok: row.ok,
|
|
5529
|
+
runners: row.runners.map((r) => ({ owner: r.owner, repo: r.repo, status: r.status, error: r.error, labels: r.labels })),
|
|
5530
|
+
error: row.error
|
|
5531
|
+
};
|
|
5532
|
+
}, now());
|
|
5533
|
+
return { value: run.result, replayed: run.replayed };
|
|
5534
|
+
});
|
|
5535
|
+
},
|
|
5536
|
+
async teardownGhRunners(actor, command) {
|
|
5537
|
+
const { runnerId, projectId } = command;
|
|
5538
|
+
const key = asIdempotencyKey(command.idempotencyKey);
|
|
5539
|
+
const scope = { subjectId: actor.principal.subjectId, operationId: "runner.teardownGhRunners", projectId, key };
|
|
5540
|
+
const repo = command.repo ?? null;
|
|
5541
|
+
return await audited(actor, {
|
|
5542
|
+
operationId: "runner.teardownGhRunners",
|
|
5543
|
+
aggregateType: "project",
|
|
5544
|
+
aggregateId: projectId,
|
|
5545
|
+
projectId,
|
|
5546
|
+
data: { runnerId, ...repo === null ? {} : { repo: `${repo.owner}/${repo.repo}` } }
|
|
5547
|
+
}, async (policy) => {
|
|
5548
|
+
await require2(actor, "project.manage", { type: "project", id: projectId, projectId }, policy);
|
|
5549
|
+
const run = await runIdempotent(idempotency, scope, { runnerId, projectId, repo }, async () => {
|
|
5550
|
+
const row = unwrap(await write2.teardownGhRunners({
|
|
5551
|
+
runnerId,
|
|
5552
|
+
projectId,
|
|
5553
|
+
sealedBundle: command.sealedBundle ?? null,
|
|
5554
|
+
repo,
|
|
5555
|
+
actor: accountActorOf(actor)
|
|
5556
|
+
}));
|
|
5557
|
+
return { projectId, runnerId, ok: row.ok, error: row.error };
|
|
5558
|
+
}, now());
|
|
5559
|
+
return { value: run.result, replayed: run.replayed };
|
|
5560
|
+
});
|
|
5561
|
+
},
|
|
5562
|
+
async requestProjectIndex(actor, command) {
|
|
5563
|
+
const { projectId, kind } = command;
|
|
5564
|
+
const key = asIdempotencyKey(command.idempotencyKey);
|
|
5565
|
+
const scope = { subjectId: actor.principal.subjectId, operationId: "project.index", projectId, key };
|
|
5566
|
+
return await audited(actor, { operationId: "project.index", aggregateType: "project", aggregateId: projectId, projectId, data: { kind } }, async (policy) => {
|
|
5567
|
+
await require2(actor, "project.manage", { type: "project", id: projectId, projectId }, policy);
|
|
5568
|
+
const run = await runIdempotent(idempotency, scope, { kind }, async () => {
|
|
5569
|
+
const { jobId } = unwrap(await write2.enqueueProjectIndex({ projectId, kind }));
|
|
5570
|
+
return { jobId, kind };
|
|
5571
|
+
}, now());
|
|
5572
|
+
return { value: run.result, replayed: run.replayed };
|
|
5573
|
+
});
|
|
5574
|
+
},
|
|
5575
|
+
async reportInitStatus(actor, command) {
|
|
5576
|
+
const { projectId, phase } = command;
|
|
5577
|
+
return await audited(actor, { operationId: "project.reportInitStatus", aggregateType: "project", aggregateId: projectId, projectId, data: { phase, failed: command.error !== void 0 } }, async (policy) => {
|
|
5578
|
+
await require2(actor, "project.manage", { type: "project", id: projectId, projectId }, policy);
|
|
5579
|
+
const { recorded } = unwrap(await write2.reportInitStatus({ projectId, phase, error: command.error ?? null }));
|
|
5580
|
+
return { value: { recorded }, replayed: false };
|
|
5581
|
+
});
|
|
5582
|
+
},
|
|
5431
5583
|
async setRepoDefaultBranch(actor, command) {
|
|
5432
5584
|
const { projectId, name } = command;
|
|
5433
5585
|
const revision = asRevision(command.revision);
|
|
@@ -8623,14 +8775,14 @@ function buildQueueDag(tasks, isSatisfiedOutside = () => false) {
|
|
|
8623
8775
|
function stableTopoOrder(currentOrder, dag) {
|
|
8624
8776
|
const sortable = currentOrder.filter((k) => dag.before.has(k) && !dag.cycleKeys.has(k));
|
|
8625
8777
|
const index = new Map(sortable.map((k, i) => [k, i]));
|
|
8626
|
-
const
|
|
8778
|
+
const pending5 = /* @__PURE__ */ new Map();
|
|
8627
8779
|
const dependents = /* @__PURE__ */ new Map();
|
|
8628
8780
|
for (const key of sortable) {
|
|
8629
8781
|
const preds = [...dag.before.get(key) ?? []].filter(
|
|
8630
8782
|
(p) => index.has(p)
|
|
8631
8783
|
// cycle keys / out-of-order keys don't gate the sort
|
|
8632
8784
|
);
|
|
8633
|
-
|
|
8785
|
+
pending5.set(key, preds.length);
|
|
8634
8786
|
for (const p of preds) {
|
|
8635
8787
|
const d = dependents.get(p) ?? [];
|
|
8636
8788
|
d.push(key);
|
|
@@ -8638,7 +8790,7 @@ function stableTopoOrder(currentOrder, dag) {
|
|
|
8638
8790
|
}
|
|
8639
8791
|
}
|
|
8640
8792
|
const ready = /* @__PURE__ */ new Set();
|
|
8641
|
-
for (const [key, deg] of
|
|
8793
|
+
for (const [key, deg] of pending5) if (deg === 0) ready.add(key);
|
|
8642
8794
|
const out = [];
|
|
8643
8795
|
while (ready.size > 0) {
|
|
8644
8796
|
let best = null;
|
|
@@ -8648,8 +8800,8 @@ function stableTopoOrder(currentOrder, dag) {
|
|
|
8648
8800
|
ready.delete(best);
|
|
8649
8801
|
out.push(best);
|
|
8650
8802
|
for (const dep of dependents.get(best) ?? []) {
|
|
8651
|
-
const deg =
|
|
8652
|
-
|
|
8803
|
+
const deg = pending5.get(dep) - 1;
|
|
8804
|
+
pending5.set(dep, deg);
|
|
8653
8805
|
if (deg === 0) ready.add(dep);
|
|
8654
8806
|
}
|
|
8655
8807
|
}
|
|
@@ -10331,8 +10483,8 @@ async function saveDeliveries(db, taskId2, sourceJobId, deliveries) {
|
|
|
10331
10483
|
if (deliveries.length === 0) return;
|
|
10332
10484
|
const now = Date.now();
|
|
10333
10485
|
for (const d of deliveries) {
|
|
10334
|
-
const
|
|
10335
|
-
const unpublished =
|
|
10486
|
+
const pending5 = pendingRelayRevision(d);
|
|
10487
|
+
const unpublished = pending5 !== null;
|
|
10336
10488
|
await db.run(
|
|
10337
10489
|
`INSERT INTO task_deliveries
|
|
10338
10490
|
(task_id, repo_name, delivery_ref, delivery_sha, base_sha, changed_files_json, source_job_id, created_at,
|
|
@@ -10355,7 +10507,7 @@ async function saveDeliveries(db, taskId2, sourceJobId, deliveries) {
|
|
|
10355
10507
|
JSON.stringify(d.changedFiles ?? []),
|
|
10356
10508
|
sourceJobId,
|
|
10357
10509
|
now,
|
|
10358
|
-
|
|
10510
|
+
pending5 && pending5 !== "unusable" ? JSON.stringify(pending5) : null,
|
|
10359
10511
|
unpublished ? null : now
|
|
10360
10512
|
);
|
|
10361
10513
|
}
|
|
@@ -10886,6 +11038,9 @@ async function getInitStatus(db, projectId) {
|
|
|
10886
11038
|
async function write(db, projectId, status) {
|
|
10887
11039
|
await db.run(`UPDATE projects SET init_status_json = ? WHERE id = ?`, JSON.stringify(status), projectId);
|
|
10888
11040
|
}
|
|
11041
|
+
async function beginInitTracking(db, projectId, phase) {
|
|
11042
|
+
await write(db, projectId, { phase, updatedAt: Date.now() });
|
|
11043
|
+
}
|
|
10889
11044
|
async function advanceInitPhase(db, projectId, phase) {
|
|
10890
11045
|
const cur = await getInitStatus(db, projectId);
|
|
10891
11046
|
if (!cur) return false;
|
|
@@ -11151,8 +11306,94 @@ var init_sandbox2 = __esm({
|
|
|
11151
11306
|
}
|
|
11152
11307
|
});
|
|
11153
11308
|
|
|
11154
|
-
// src/farm
|
|
11309
|
+
// src/routes/farm.ts
|
|
11155
11310
|
import { nanoid as nanoid11 } from "nanoid";
|
|
11311
|
+
function viewerInAssignments(viewer, assignments) {
|
|
11312
|
+
return assignments.some(
|
|
11313
|
+
(a) => viewer.userId !== null && a.userId === viewer.userId || a.email !== null && a.email === viewer.email
|
|
11314
|
+
);
|
|
11315
|
+
}
|
|
11316
|
+
async function viewerOwnsFarm(db, viewer, farmId) {
|
|
11317
|
+
const row = await db.get(
|
|
11318
|
+
`SELECT 1 FROM farm_assignments
|
|
11319
|
+
WHERE farm_id = ? AND (user_id = ? OR email = ?) LIMIT 1`,
|
|
11320
|
+
farmId,
|
|
11321
|
+
viewer.userId,
|
|
11322
|
+
viewer.email
|
|
11323
|
+
);
|
|
11324
|
+
return !!row;
|
|
11325
|
+
}
|
|
11326
|
+
async function listFarms(db, viewer) {
|
|
11327
|
+
const online = new Set(listConnectedFarmIds());
|
|
11328
|
+
const rows = await db.all(`SELECT f.id, f.name, f.public_key as publicKey,
|
|
11329
|
+
f.key_fingerprint as keyFingerprint, f.last_seen_at as lastSeenAt,
|
|
11330
|
+
a.user_id as aUserId, a.email as aEmail
|
|
11331
|
+
FROM farms f LEFT JOIN farm_assignments a ON a.farm_id = f.id
|
|
11332
|
+
ORDER BY f.registered_at ASC`);
|
|
11333
|
+
const byId = /* @__PURE__ */ new Map();
|
|
11334
|
+
for (const r of rows) {
|
|
11335
|
+
let farm = byId.get(r.id);
|
|
11336
|
+
if (!farm) {
|
|
11337
|
+
farm = {
|
|
11338
|
+
farmId: r.id,
|
|
11339
|
+
name: r.name,
|
|
11340
|
+
publicKey: r.publicKey,
|
|
11341
|
+
keyFingerprint: r.keyFingerprint,
|
|
11342
|
+
online: online.has(r.id),
|
|
11343
|
+
lastSeenAt: r.lastSeenAt,
|
|
11344
|
+
assignments: []
|
|
11345
|
+
};
|
|
11346
|
+
byId.set(r.id, farm);
|
|
11347
|
+
}
|
|
11348
|
+
if (r.aUserId !== null || r.aEmail !== null) {
|
|
11349
|
+
farm.assignments.push({ userId: r.aUserId, email: r.aEmail });
|
|
11350
|
+
}
|
|
11351
|
+
}
|
|
11352
|
+
return [...byId.values()].filter((f) => !viewer || viewer.isSuperAdmin || viewerInAssignments(viewer, f.assignments)).map(({ assignments: _assignments, ...f }) => f);
|
|
11353
|
+
}
|
|
11354
|
+
var init_farm2 = __esm({
|
|
11355
|
+
"src/routes/farm.ts"() {
|
|
11356
|
+
"use strict";
|
|
11357
|
+
init_sealedbox();
|
|
11358
|
+
init_abuse_throttle();
|
|
11359
|
+
init_ws();
|
|
11360
|
+
}
|
|
11361
|
+
});
|
|
11362
|
+
|
|
11363
|
+
// src/runner-reg-tokens.ts
|
|
11364
|
+
import { nanoid as nanoid12 } from "nanoid";
|
|
11365
|
+
var init_runner_reg_tokens = __esm({
|
|
11366
|
+
"src/runner-reg-tokens.ts"() {
|
|
11367
|
+
"use strict";
|
|
11368
|
+
}
|
|
11369
|
+
});
|
|
11370
|
+
|
|
11371
|
+
// src/routes/runner.ts
|
|
11372
|
+
import { nanoid as nanoid13 } from "nanoid";
|
|
11373
|
+
async function viewerOwnsRunner(db, viewer, runnerId) {
|
|
11374
|
+
const row = await db.get(
|
|
11375
|
+
`SELECT 1 FROM runners WHERE id = ? AND (owner_user_id = ? OR owner_email = ?) LIMIT 1`,
|
|
11376
|
+
runnerId,
|
|
11377
|
+
viewer.userId,
|
|
11378
|
+
viewer.email
|
|
11379
|
+
);
|
|
11380
|
+
return !!row;
|
|
11381
|
+
}
|
|
11382
|
+
var init_runner2 = __esm({
|
|
11383
|
+
"src/routes/runner.ts"() {
|
|
11384
|
+
"use strict";
|
|
11385
|
+
init_sealedbox();
|
|
11386
|
+
init_abuse_throttle();
|
|
11387
|
+
init_cli_auth();
|
|
11388
|
+
init_auth();
|
|
11389
|
+
init_users();
|
|
11390
|
+
init_runner_reg_tokens();
|
|
11391
|
+
init_ws();
|
|
11392
|
+
}
|
|
11393
|
+
});
|
|
11394
|
+
|
|
11395
|
+
// src/farm/pool-spec.ts
|
|
11396
|
+
import { nanoid as nanoid14 } from "nanoid";
|
|
11156
11397
|
async function computeFarmPoolSpec(db, farmId) {
|
|
11157
11398
|
const rows = await db.all(
|
|
11158
11399
|
`SELECT fa.user_id as userId, fa.max_sandboxes as quota,
|
|
@@ -11184,7 +11425,7 @@ async function computeFarmPoolSpec(db, farmId) {
|
|
|
11184
11425
|
return spec;
|
|
11185
11426
|
}
|
|
11186
11427
|
function buildSyncWorkerPoolMessage(accounts) {
|
|
11187
|
-
return { type: "sync_worker_pool", requestId:
|
|
11428
|
+
return { type: "sync_worker_pool", requestId: nanoid14(16), accounts };
|
|
11188
11429
|
}
|
|
11189
11430
|
var init_pool_spec = __esm({
|
|
11190
11431
|
"src/farm/pool-spec.ts"() {
|
|
@@ -11192,127 +11433,6 @@ var init_pool_spec = __esm({
|
|
|
11192
11433
|
}
|
|
11193
11434
|
});
|
|
11194
11435
|
|
|
11195
|
-
// src/farm/provision.ts
|
|
11196
|
-
import { nanoid as nanoid12 } from "nanoid";
|
|
11197
|
-
function awaitResult(requestId2, send, timeoutMs) {
|
|
11198
|
-
if (!send()) return Promise.reject(new Error("farm_offline"));
|
|
11199
|
-
return new Promise((resolve2, reject) => {
|
|
11200
|
-
const timer = setTimeout(() => {
|
|
11201
|
-
pending.delete(requestId2);
|
|
11202
|
-
reject(new Error("farm_timeout"));
|
|
11203
|
-
}, timeoutMs);
|
|
11204
|
-
pending.set(requestId2, { resolve: resolve2, reject, timer });
|
|
11205
|
-
});
|
|
11206
|
-
}
|
|
11207
|
-
function requestDelete(farmId, projectId, timeoutMs) {
|
|
11208
|
-
const requestId2 = nanoid12(16);
|
|
11209
|
-
return awaitResult(
|
|
11210
|
-
requestId2,
|
|
11211
|
-
() => sendToFarm(farmId, { type: "delete_project", requestId: requestId2, projectId }),
|
|
11212
|
-
timeoutMs
|
|
11213
|
-
);
|
|
11214
|
-
}
|
|
11215
|
-
async function requestFarmPoolSync(db, farmId) {
|
|
11216
|
-
const accounts = await computeFarmPoolSpec(db, farmId);
|
|
11217
|
-
sendToFarm(farmId, buildSyncWorkerPoolMessage(accounts));
|
|
11218
|
-
}
|
|
11219
|
-
var pending;
|
|
11220
|
-
var init_provision = __esm({
|
|
11221
|
-
"src/farm/provision.ts"() {
|
|
11222
|
-
"use strict";
|
|
11223
|
-
init_ws();
|
|
11224
|
-
init_pool_spec();
|
|
11225
|
-
pending = /* @__PURE__ */ new Map();
|
|
11226
|
-
}
|
|
11227
|
-
});
|
|
11228
|
-
|
|
11229
|
-
// src/routes/farm.ts
|
|
11230
|
-
import { nanoid as nanoid13 } from "nanoid";
|
|
11231
|
-
var init_farm2 = __esm({
|
|
11232
|
-
"src/routes/farm.ts"() {
|
|
11233
|
-
"use strict";
|
|
11234
|
-
init_sealedbox();
|
|
11235
|
-
init_abuse_throttle();
|
|
11236
|
-
init_cli_auth();
|
|
11237
|
-
init_auth();
|
|
11238
|
-
init_users();
|
|
11239
|
-
init_init_status();
|
|
11240
|
-
init_ws();
|
|
11241
|
-
init_provision();
|
|
11242
|
-
}
|
|
11243
|
-
});
|
|
11244
|
-
|
|
11245
|
-
// src/runner-reg-tokens.ts
|
|
11246
|
-
import { nanoid as nanoid14 } from "nanoid";
|
|
11247
|
-
var init_runner_reg_tokens = __esm({
|
|
11248
|
-
"src/runner-reg-tokens.ts"() {
|
|
11249
|
-
"use strict";
|
|
11250
|
-
}
|
|
11251
|
-
});
|
|
11252
|
-
|
|
11253
|
-
// src/runner/provision.ts
|
|
11254
|
-
import { nanoid as nanoid15 } from "nanoid";
|
|
11255
|
-
var init_provision2 = __esm({
|
|
11256
|
-
"src/runner/provision.ts"() {
|
|
11257
|
-
"use strict";
|
|
11258
|
-
init_ws();
|
|
11259
|
-
}
|
|
11260
|
-
});
|
|
11261
|
-
|
|
11262
|
-
// src/runner/bindings.ts
|
|
11263
|
-
function parseGhRunnerBindings(json) {
|
|
11264
|
-
if (!json) return [];
|
|
11265
|
-
try {
|
|
11266
|
-
const parsed = JSON.parse(json);
|
|
11267
|
-
return Array.isArray(parsed.bindings) ? parsed.bindings : [];
|
|
11268
|
-
} catch {
|
|
11269
|
-
return [];
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
function serialize(bindings) {
|
|
11273
|
-
return JSON.stringify({ bindings });
|
|
11274
|
-
}
|
|
11275
|
-
async function removeGhRunnerBinding(db, projectId, owner, repo) {
|
|
11276
|
-
return db.tx(async (tx) => {
|
|
11277
|
-
const row = await tx.get(
|
|
11278
|
-
`SELECT gh_runner_bindings_json as bindingsJson FROM projects WHERE id = ?`,
|
|
11279
|
-
projectId
|
|
11280
|
-
);
|
|
11281
|
-
if (!row) return false;
|
|
11282
|
-
const bindings = parseGhRunnerBindings(row.bindingsJson);
|
|
11283
|
-
const kept = bindings.filter((b) => !(b.owner === owner && b.repo === repo));
|
|
11284
|
-
if (kept.length === bindings.length) return false;
|
|
11285
|
-
await tx.run(
|
|
11286
|
-
`UPDATE projects SET gh_runner_bindings_json = ? WHERE id = ?`,
|
|
11287
|
-
serialize(kept),
|
|
11288
|
-
projectId
|
|
11289
|
-
);
|
|
11290
|
-
return true;
|
|
11291
|
-
});
|
|
11292
|
-
}
|
|
11293
|
-
var init_bindings = __esm({
|
|
11294
|
-
"src/runner/bindings.ts"() {
|
|
11295
|
-
"use strict";
|
|
11296
|
-
}
|
|
11297
|
-
});
|
|
11298
|
-
|
|
11299
|
-
// src/routes/runner.ts
|
|
11300
|
-
import { nanoid as nanoid16 } from "nanoid";
|
|
11301
|
-
var init_runner2 = __esm({
|
|
11302
|
-
"src/routes/runner.ts"() {
|
|
11303
|
-
"use strict";
|
|
11304
|
-
init_sealedbox();
|
|
11305
|
-
init_abuse_throttle();
|
|
11306
|
-
init_cli_auth();
|
|
11307
|
-
init_auth();
|
|
11308
|
-
init_users();
|
|
11309
|
-
init_runner_reg_tokens();
|
|
11310
|
-
init_ws();
|
|
11311
|
-
init_provision2();
|
|
11312
|
-
init_bindings();
|
|
11313
|
-
}
|
|
11314
|
-
});
|
|
11315
|
-
|
|
11316
11436
|
// src/routes/ws.ts
|
|
11317
11437
|
function sendToDeliveryFollowers(projectId, message) {
|
|
11318
11438
|
const payload = JSON.stringify(message);
|
|
@@ -11356,6 +11476,13 @@ function sendToFarm(farmId, message) {
|
|
|
11356
11476
|
function listConnectedFarmIds() {
|
|
11357
11477
|
return [...farmConnections.keys()];
|
|
11358
11478
|
}
|
|
11479
|
+
function sendToRunner(runnerId, message) {
|
|
11480
|
+
const conn = runnerConnections.get(runnerId);
|
|
11481
|
+
if (!conn) return false;
|
|
11482
|
+
if (conn.socket.readyState !== conn.socket.OPEN) return false;
|
|
11483
|
+
conn.socket.send(JSON.stringify(message));
|
|
11484
|
+
return true;
|
|
11485
|
+
}
|
|
11359
11486
|
function getProjectCliOrigin(projectId) {
|
|
11360
11487
|
return projectCliConnections.get(projectId)?.origin ?? null;
|
|
11361
11488
|
}
|
|
@@ -11598,7 +11725,7 @@ async function getPresence(db, viewer) {
|
|
|
11598
11725
|
})()
|
|
11599
11726
|
};
|
|
11600
11727
|
}
|
|
11601
|
-
var agentLoginProviders, deliveryFollowers, projectCliConnections, projectLastSeenAt, projectLastDuplicateRegisterAt, sandboxConnections, farmConnections, cliMessageHandlers, cliRegisterHandlers;
|
|
11728
|
+
var agentLoginProviders, deliveryFollowers, projectCliConnections, projectLastSeenAt, projectLastDuplicateRegisterAt, sandboxConnections, farmConnections, runnerConnections, cliMessageHandlers, cliRegisterHandlers;
|
|
11602
11729
|
var init_ws = __esm({
|
|
11603
11730
|
"src/routes/ws.ts"() {
|
|
11604
11731
|
"use strict";
|
|
@@ -11624,6 +11751,7 @@ var init_ws = __esm({
|
|
|
11624
11751
|
projectLastDuplicateRegisterAt = /* @__PURE__ */ new Map();
|
|
11625
11752
|
sandboxConnections = /* @__PURE__ */ new Map();
|
|
11626
11753
|
farmConnections = /* @__PURE__ */ new Map();
|
|
11754
|
+
runnerConnections = /* @__PURE__ */ new Map();
|
|
11627
11755
|
cliMessageHandlers = /* @__PURE__ */ new Set();
|
|
11628
11756
|
cliRegisterHandlers = /* @__PURE__ */ new Set();
|
|
11629
11757
|
}
|
|
@@ -11751,7 +11879,7 @@ var init_policy = __esm({
|
|
|
11751
11879
|
});
|
|
11752
11880
|
|
|
11753
11881
|
// src/apply/merge-branch.ts
|
|
11754
|
-
import { nanoid as
|
|
11882
|
+
import { nanoid as nanoid15 } from "nanoid";
|
|
11755
11883
|
async function enqueueMergeBranch(db, taskId2, log) {
|
|
11756
11884
|
if (await hasInflightMergeBranch(db, taskId2)) {
|
|
11757
11885
|
log.info({ taskId: taskId2 }, "merge_branch ignored \u2014 chain already in flight");
|
|
@@ -11768,7 +11896,7 @@ async function enqueueMergeBranch(db, taskId2, log) {
|
|
|
11768
11896
|
const now = Date.now();
|
|
11769
11897
|
const ids = [];
|
|
11770
11898
|
for (const t of targets) {
|
|
11771
|
-
const id = `wa_${
|
|
11899
|
+
const id = `wa_${nanoid15(12)}`;
|
|
11772
11900
|
const action = {
|
|
11773
11901
|
type: "merge_branch",
|
|
11774
11902
|
taskId: taskId2,
|
|
@@ -11849,7 +11977,7 @@ var init_merge_branch = __esm({
|
|
|
11849
11977
|
});
|
|
11850
11978
|
|
|
11851
11979
|
// src/apply/publish-delivery.ts
|
|
11852
|
-
import { nanoid as
|
|
11980
|
+
import { nanoid as nanoid16 } from "nanoid";
|
|
11853
11981
|
async function enqueuePublishDeliveries(db, taskId2, log) {
|
|
11854
11982
|
const unpublished = await unpublishedDeliveries(db, taskId2);
|
|
11855
11983
|
const unusable = unpublished.filter((p) => p.revision === null);
|
|
@@ -11860,8 +11988,8 @@ async function enqueuePublishDeliveries(db, taskId2, log) {
|
|
|
11860
11988
|
};
|
|
11861
11989
|
}
|
|
11862
11990
|
const inFlight2 = await inFlightPublications(db, taskId2);
|
|
11863
|
-
const
|
|
11864
|
-
if (
|
|
11991
|
+
const pending5 = unpublished.filter((p) => inFlight2.get(p.repoName) !== p.revision?.sha);
|
|
11992
|
+
if (pending5.length === 0) return { queued: 0 };
|
|
11865
11993
|
const projectId = taskId2.split(":", 2)[0] ?? "";
|
|
11866
11994
|
const contextRepoCloneUrl = await contextRepoCloneUrlOf(db, projectId);
|
|
11867
11995
|
if (!contextRepoCloneUrl) {
|
|
@@ -11870,8 +11998,8 @@ async function enqueuePublishDeliveries(db, taskId2, log) {
|
|
|
11870
11998
|
}
|
|
11871
11999
|
const now = Date.now();
|
|
11872
12000
|
const ids = [];
|
|
11873
|
-
for (const p of
|
|
11874
|
-
const id = `wa_${
|
|
12001
|
+
for (const p of pending5) {
|
|
12002
|
+
const id = `wa_${nanoid16(12)}`;
|
|
11875
12003
|
const action = {
|
|
11876
12004
|
type: "publish_delivery",
|
|
11877
12005
|
taskId: taskId2,
|
|
@@ -13132,7 +13260,7 @@ var init_settings = __esm({
|
|
|
13132
13260
|
});
|
|
13133
13261
|
|
|
13134
13262
|
// src/jobs/queue.ts
|
|
13135
|
-
import { nanoid as
|
|
13263
|
+
import { nanoid as nanoid17 } from "nanoid";
|
|
13136
13264
|
async function heldByTrancheDeferral(db) {
|
|
13137
13265
|
const rows = await db.all(
|
|
13138
13266
|
`SELECT t.id AS id FROM tasks t
|
|
@@ -13204,7 +13332,7 @@ async function restampQueuedJobs(db, projectId) {
|
|
|
13204
13332
|
return restamped;
|
|
13205
13333
|
}
|
|
13206
13334
|
async function createJob(db, input) {
|
|
13207
|
-
const id = `job_${
|
|
13335
|
+
const id = `job_${nanoid17(16)}`;
|
|
13208
13336
|
const now = Date.now();
|
|
13209
13337
|
const projectRepos = await loadProjectRepoRefs(db, input.projectId);
|
|
13210
13338
|
const metadata = {
|
|
@@ -13405,7 +13533,7 @@ async function claimNextJob(db, sandboxId, capabilities, lane) {
|
|
|
13405
13533
|
data: e.data
|
|
13406
13534
|
});
|
|
13407
13535
|
},
|
|
13408
|
-
newExecutionId: () => `exe_${
|
|
13536
|
+
newExecutionId: () => `exe_${nanoid17(16)}`,
|
|
13409
13537
|
// DEV-49/DEV-221: stack the worktree on the predecessor's branch so the
|
|
13410
13538
|
// agent (and the resulting meta/bases.json baseSha) start from the chain
|
|
13411
13539
|
// tip, not the repo default. Applies to analyze/estimate/implement — a
|
|
@@ -13671,7 +13799,7 @@ var init_legacy_cred_bundles = __esm({
|
|
|
13671
13799
|
});
|
|
13672
13800
|
|
|
13673
13801
|
// src/db/schema.ts
|
|
13674
|
-
import { nanoid as
|
|
13802
|
+
import { nanoid as nanoid18 } from "nanoid";
|
|
13675
13803
|
async function ensureColumn(db, table, column, ddl) {
|
|
13676
13804
|
const row = await db.get(
|
|
13677
13805
|
`SELECT 1 FROM information_schema.columns WHERE table_name = ? AND column_name = ?`,
|
|
@@ -13980,7 +14108,7 @@ async function initSchema(db) {
|
|
|
13980
14108
|
await ensureColumn(db, "projects", "init_status_json", "init_status_json TEXT");
|
|
13981
14109
|
await ensureColumn(db, "users", "id", "id TEXT");
|
|
13982
14110
|
for (const row of await db.all(`SELECT email FROM users WHERE id IS NULL`)) {
|
|
13983
|
-
await db.run(`UPDATE users SET id = ? WHERE email = ?`, `usr_${
|
|
14111
|
+
await db.run(`UPDATE users SET id = ? WHERE email = ?`, `usr_${nanoid18(12)}`, row.email);
|
|
13984
14112
|
}
|
|
13985
14113
|
await db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_users_id ON users(id)`);
|
|
13986
14114
|
await ensureColumn(db, "projects", "owner_user_id", "owner_user_id TEXT");
|
|
@@ -14064,7 +14192,7 @@ async function initSchema(db) {
|
|
|
14064
14192
|
await db.run(
|
|
14065
14193
|
`INSERT INTO context_outbox (id, project_id, ordinal, files_json, message, state, created_at)
|
|
14066
14194
|
VALUES (?, ?, ?, ?, 'migration: legacy display diffs', 'pending', ?)`,
|
|
14067
|
-
`ctx_${
|
|
14195
|
+
`ctx_${nanoid18(16)}`,
|
|
14068
14196
|
projectId,
|
|
14069
14197
|
next.next,
|
|
14070
14198
|
JSON.stringify(files),
|
|
@@ -15898,27 +16026,27 @@ var init_delivery_decision = __esm({
|
|
|
15898
16026
|
});
|
|
15899
16027
|
|
|
15900
16028
|
// src/projects/context-read.ts
|
|
15901
|
-
import { nanoid as
|
|
16029
|
+
import { nanoid as nanoid19 } from "nanoid";
|
|
15902
16030
|
function requestContextFile(projectId, path, timeoutMs = DEFAULT_TIMEOUT_MS, send = sendToProjectCli) {
|
|
15903
16031
|
const action = { type: "read_context_file", projectId, path };
|
|
15904
|
-
const actionId2 = `rdctx_${
|
|
16032
|
+
const actionId2 = `rdctx_${nanoid19(16)}`;
|
|
15905
16033
|
if (!send(projectId, { type: "dispatch_action", actionId: actionId2, action })) {
|
|
15906
16034
|
return Promise.reject(new Error("cli_offline"));
|
|
15907
16035
|
}
|
|
15908
16036
|
return new Promise((resolve2, reject) => {
|
|
15909
16037
|
const timer = setTimeout(() => {
|
|
15910
|
-
|
|
16038
|
+
pending.delete(actionId2);
|
|
15911
16039
|
reject(new Error("cli_timeout"));
|
|
15912
16040
|
}, timeoutMs);
|
|
15913
|
-
|
|
16041
|
+
pending.set(actionId2, { resolve: resolve2, reject, timer });
|
|
15914
16042
|
});
|
|
15915
16043
|
}
|
|
15916
|
-
var
|
|
16044
|
+
var pending, DEFAULT_TIMEOUT_MS;
|
|
15917
16045
|
var init_context_read = __esm({
|
|
15918
16046
|
"src/projects/context-read.ts"() {
|
|
15919
16047
|
"use strict";
|
|
15920
16048
|
init_ws();
|
|
15921
|
-
|
|
16049
|
+
pending = /* @__PURE__ */ new Map();
|
|
15922
16050
|
DEFAULT_TIMEOUT_MS = 2e4;
|
|
15923
16051
|
}
|
|
15924
16052
|
});
|
|
@@ -15963,14 +16091,14 @@ async function recordVerdicts(db, taskId2, verdicts) {
|
|
|
15963
16091
|
return current;
|
|
15964
16092
|
}
|
|
15965
16093
|
function decidePlan(plan, verdicts) {
|
|
15966
|
-
const
|
|
16094
|
+
const pending5 = plan.items.filter((i) => !(i.id in verdicts)).map((i) => i.id);
|
|
15967
16095
|
const list = plan.items.map((i) => verdicts[i.id]).filter((v) => !!v);
|
|
15968
|
-
const approved =
|
|
16096
|
+
const approved = pending5.length === 0 && list.every((v) => v.approved);
|
|
15969
16097
|
return {
|
|
15970
|
-
complete:
|
|
15971
|
-
pending:
|
|
16098
|
+
complete: pending5.length === 0,
|
|
16099
|
+
pending: pending5,
|
|
15972
16100
|
approved,
|
|
15973
|
-
rework:
|
|
16101
|
+
rework: pending5.length === 0 ? compilePlanRework(plan, list) : ""
|
|
15974
16102
|
};
|
|
15975
16103
|
}
|
|
15976
16104
|
function describeDecision(d) {
|
|
@@ -16108,7 +16236,7 @@ var init_project_access = __esm({
|
|
|
16108
16236
|
});
|
|
16109
16237
|
|
|
16110
16238
|
// src/projects/regimen-write.ts
|
|
16111
|
-
import { nanoid as
|
|
16239
|
+
import { nanoid as nanoid20 } from "nanoid";
|
|
16112
16240
|
function dispatchSetYouTrackConfig(db, projectId, payload, log) {
|
|
16113
16241
|
const action = {
|
|
16114
16242
|
type: "set_youtrack_config",
|
|
@@ -16116,7 +16244,7 @@ function dispatchSetYouTrackConfig(db, projectId, payload, log) {
|
|
|
16116
16244
|
...payload,
|
|
16117
16245
|
commitMessage: `chore(youtrack): update YT config (dashboard)`
|
|
16118
16246
|
};
|
|
16119
|
-
const actionId2 = `ytcfg_${
|
|
16247
|
+
const actionId2 = `ytcfg_${nanoid20(16)}`;
|
|
16120
16248
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
16121
16249
|
if (!sent) {
|
|
16122
16250
|
log.warn({ projectId, actionId: actionId2 }, "dispatchSetYouTrackConfig: CLI not connected");
|
|
@@ -16132,7 +16260,7 @@ function dispatchSetRepoDefaultBranch(db, projectId, repoName, defaultBranch, lo
|
|
|
16132
16260
|
repoName,
|
|
16133
16261
|
defaultBranch
|
|
16134
16262
|
};
|
|
16135
|
-
const actionId2 = `repocfg_${
|
|
16263
|
+
const actionId2 = `repocfg_${nanoid20(16)}`;
|
|
16136
16264
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
16137
16265
|
if (!sent) {
|
|
16138
16266
|
log.warn({ projectId, actionId: actionId2 }, "dispatchSetRepoDefaultBranch: CLI not connected");
|
|
@@ -16152,7 +16280,7 @@ function dispatchSetProjectSetting(db, projectId, edits, log, opts) {
|
|
|
16152
16280
|
edits,
|
|
16153
16281
|
commitMessage: opts?.commitMessage ?? "chore(settings): update project settings (dashboard)"
|
|
16154
16282
|
};
|
|
16155
|
-
const actionId2 = `${prefix}${
|
|
16283
|
+
const actionId2 = `${prefix}${nanoid20(16)}`;
|
|
16156
16284
|
if (prefix === "setg_") rememberSettingsDispatch(actionId2, edits);
|
|
16157
16285
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
16158
16286
|
if (!sent) {
|
|
@@ -16164,7 +16292,7 @@ function dispatchSetProjectSetting(db, projectId, edits, log, opts) {
|
|
|
16164
16292
|
}
|
|
16165
16293
|
function dispatchSetBudgetSource(db, projectId, source, log) {
|
|
16166
16294
|
const action = { type: "set_budget_source", projectId, source };
|
|
16167
|
-
const actionId2 = `bsrc_${
|
|
16295
|
+
const actionId2 = `bsrc_${nanoid20(16)}`;
|
|
16168
16296
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
16169
16297
|
if (!sent) log.warn({ projectId, actionId: actionId2 }, "dispatchSetBudgetSource: CLI not connected");
|
|
16170
16298
|
else log.info({ projectId, actionId: actionId2, kind: source.kind }, "dispatchSetBudgetSource: sent to CLI");
|
|
@@ -16186,7 +16314,7 @@ function dispatchEditTrackerIssue(db, projectId, fields, log) {
|
|
|
16186
16314
|
...fields.title !== void 0 ? { title: fields.title } : {},
|
|
16187
16315
|
...fields.body !== void 0 ? { body: fields.body } : {}
|
|
16188
16316
|
};
|
|
16189
|
-
const actionId2 = `tedit_${
|
|
16317
|
+
const actionId2 = `tedit_${nanoid20(16)}`;
|
|
16190
16318
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
16191
16319
|
if (!sent) {
|
|
16192
16320
|
log.warn({ projectId, actionId: actionId2, issueKey: fields.issueKey }, "dispatchEditTrackerIssue: CLI not connected");
|
|
@@ -16206,21 +16334,21 @@ var init_regimen_write = __esm({
|
|
|
16206
16334
|
});
|
|
16207
16335
|
|
|
16208
16336
|
// src/tasks/context-outbox.ts
|
|
16209
|
-
import { nanoid as
|
|
16337
|
+
import { nanoid as nanoid21 } from "nanoid";
|
|
16210
16338
|
async function nextOrdinal(db, projectId) {
|
|
16211
16339
|
const row = await db.get(`SELECT COALESCE(MAX(ordinal), 0) AS max FROM context_outbox WHERE project_id = ?`, projectId);
|
|
16212
16340
|
return row.max + 1;
|
|
16213
16341
|
}
|
|
16214
16342
|
async function enqueueContextWrite(db, projectId, files, message, log) {
|
|
16215
16343
|
if (files.length === 0) return;
|
|
16216
|
-
const id = `ctx_${
|
|
16344
|
+
const id = `ctx_${nanoid21(16)}`;
|
|
16217
16345
|
await db.run(`INSERT INTO context_outbox (id, project_id, ordinal, files_json, message, state, created_at)
|
|
16218
16346
|
VALUES (?, ?, ?, ?, ?, 'pending', ?)`, id, projectId, await nextOrdinal(db, projectId), JSON.stringify(files), message, Date.now());
|
|
16219
16347
|
await dispatchNextContextWrite(db, projectId, log);
|
|
16220
16348
|
}
|
|
16221
16349
|
async function enqueueAdrPromote(db, projectId, taskKey, slugs, log, source = "task") {
|
|
16222
16350
|
if (slugs.length === 0) return;
|
|
16223
|
-
const id = `ctx_${
|
|
16351
|
+
const id = `ctx_${nanoid21(16)}`;
|
|
16224
16352
|
await db.run(`INSERT INTO context_outbox (id, project_id, ordinal, files_json, message, state, kind, meta_json, created_at)
|
|
16225
16353
|
VALUES (?, ?, ?, ?, ?, 'pending', 'adr_promote', ?, ?)`, id, projectId, await nextOrdinal(db, projectId), JSON.stringify(slugs), `adr(${taskKey}): promote ${slugs.join(", ")}`, JSON.stringify({ taskKey, source }), Date.now());
|
|
16226
16354
|
await dispatchNextContextWrite(db, projectId, log);
|
|
@@ -16625,10 +16753,10 @@ __export(cluster_journal_exports, {
|
|
|
16625
16753
|
clusterJournal: () => clusterJournal,
|
|
16626
16754
|
recordClusterEvent: () => recordClusterEvent
|
|
16627
16755
|
});
|
|
16628
|
-
import { nanoid as
|
|
16756
|
+
import { nanoid as nanoid22 } from "nanoid";
|
|
16629
16757
|
async function recordClusterEvent(db, projectId, anchorKey, kind, detail) {
|
|
16630
16758
|
await engineStore(db).clusters.appendEvent(
|
|
16631
|
-
`ce_${
|
|
16759
|
+
`ce_${nanoid22(12)}`,
|
|
16632
16760
|
projectId,
|
|
16633
16761
|
anchorKey,
|
|
16634
16762
|
kind,
|
|
@@ -16696,7 +16824,7 @@ __export(cluster_recovery_exports, {
|
|
|
16696
16824
|
reconcileClusterRecoveries: () => reconcileClusterRecoveries,
|
|
16697
16825
|
startClusterRecovery: () => startClusterRecovery
|
|
16698
16826
|
});
|
|
16699
|
-
import { nanoid as
|
|
16827
|
+
import { nanoid as nanoid23 } from "nanoid";
|
|
16700
16828
|
async function startClusterRecovery(db, task, instruction, log, opts = {}) {
|
|
16701
16829
|
const anchorKey = await clusterAnchorFor(db, task);
|
|
16702
16830
|
const cluster = await getCluster(db, task.projectId, anchorKey);
|
|
@@ -17054,7 +17182,7 @@ async function dropCluster(db, task, cluster, reason, log, opts = {}) {
|
|
|
17054
17182
|
if (carrier) {
|
|
17055
17183
|
const now = Date.now();
|
|
17056
17184
|
for (const t of await loadCreatePRTargets(db, carrier)) {
|
|
17057
|
-
const id = `wa_${
|
|
17185
|
+
const id = `wa_${nanoid23(12)}`;
|
|
17058
17186
|
const action = {
|
|
17059
17187
|
type: "close_pr",
|
|
17060
17188
|
taskId: carrier,
|
|
@@ -17173,7 +17301,7 @@ __export(tasks_exports, {
|
|
|
17173
17301
|
queueTrackerStatusMove: () => queueTrackerStatusMove,
|
|
17174
17302
|
tasksRoutes: () => tasksRoutes
|
|
17175
17303
|
});
|
|
17176
|
-
import { nanoid as
|
|
17304
|
+
import { nanoid as nanoid24 } from "nanoid";
|
|
17177
17305
|
async function queueTrackerStatusMove(db, log, taskId2, projectId, newStatus, manual = false) {
|
|
17178
17306
|
const projectRow = await db.get(`SELECT automation_level as automationLevel FROM projects WHERE id = ?`, projectId);
|
|
17179
17307
|
const writes = await loadYouTrackWrites(
|
|
@@ -17188,7 +17316,7 @@ async function queueTrackerStatusMove(db, log, taskId2, projectId, newStatus, ma
|
|
|
17188
17316
|
newStatus,
|
|
17189
17317
|
...manual ? { manual: true } : {}
|
|
17190
17318
|
};
|
|
17191
|
-
const statusActionId = `wa_${
|
|
17319
|
+
const statusActionId = `wa_${nanoid24(12)}`;
|
|
17192
17320
|
await engineStore(db).actions.appendPlan(
|
|
17193
17321
|
taskId2,
|
|
17194
17322
|
[{ id: statusActionId, kind: "tracker_status", payloadJson: JSON.stringify(action) }],
|
|
@@ -18037,7 +18165,7 @@ async function tasksRoutes(app, deps) {
|
|
|
18037
18165
|
}
|
|
18038
18166
|
await ingestComment(db, {
|
|
18039
18167
|
taskId: taskId2,
|
|
18040
|
-
trackerCommentId: `${DASHBOARD_PLACEHOLDER_PREFIX}${
|
|
18168
|
+
trackerCommentId: `${DASHBOARD_PLACEHOLDER_PREFIX}${nanoid24(8)}`,
|
|
18041
18169
|
author: session.email,
|
|
18042
18170
|
body: req.body.comment,
|
|
18043
18171
|
origin: "dashboard",
|
|
@@ -18195,7 +18323,7 @@ async function tasksRoutes(app, deps) {
|
|
|
18195
18323
|
if (!task) return reply.status(404).send({ error: "task_not_found" });
|
|
18196
18324
|
const ingest = await ingestComment(db, {
|
|
18197
18325
|
taskId: taskId2,
|
|
18198
|
-
trackerCommentId: `${DASHBOARD_PLACEHOLDER_PREFIX}${
|
|
18326
|
+
trackerCommentId: `${DASHBOARD_PLACEHOLDER_PREFIX}${nanoid24(8)}`,
|
|
18199
18327
|
author: session.email,
|
|
18200
18328
|
body: req.body.body,
|
|
18201
18329
|
origin: "dashboard",
|
|
@@ -18264,7 +18392,7 @@ ${req.body.body}
|
|
|
18264
18392
|
// comments.jsonl; YouTrack ignores it (the token owner authors).
|
|
18265
18393
|
author: session.email
|
|
18266
18394
|
};
|
|
18267
|
-
mirrorActionId = `wa_${
|
|
18395
|
+
mirrorActionId = `wa_${nanoid24(12)}`;
|
|
18268
18396
|
await engineStore(db).actions.appendPlan(
|
|
18269
18397
|
taskId2,
|
|
18270
18398
|
[{ id: mirrorActionId, kind: "tracker_comment", payloadJson: JSON.stringify(action) }],
|
|
@@ -18455,7 +18583,7 @@ __export(observer_exports, {
|
|
|
18455
18583
|
stampTranche: () => stampTranche,
|
|
18456
18584
|
startStagedBatch: () => startStagedBatch
|
|
18457
18585
|
});
|
|
18458
|
-
import { nanoid as
|
|
18586
|
+
import { nanoid as nanoid25 } from "nanoid";
|
|
18459
18587
|
async function loadContextRepo2(db, projectId) {
|
|
18460
18588
|
const row = await db.get(
|
|
18461
18589
|
`SELECT context_repo_json AS json FROM projects WHERE id = ?`,
|
|
@@ -18500,7 +18628,7 @@ async function enqueueObserverJob(db, batch, log, opts = {}) {
|
|
|
18500
18628
|
return spec.jobId;
|
|
18501
18629
|
}
|
|
18502
18630
|
async function stampTranche(db, taskIds) {
|
|
18503
|
-
const trancheId = `tr_${
|
|
18631
|
+
const trancheId = `tr_${nanoid25(12)}`;
|
|
18504
18632
|
for (const id of taskIds) {
|
|
18505
18633
|
await engineStore(db).tasks.stampTrancheMembership(id, trancheId);
|
|
18506
18634
|
}
|
|
@@ -18926,7 +19054,7 @@ var init_queue_view = __esm({
|
|
|
18926
19054
|
});
|
|
18927
19055
|
|
|
18928
19056
|
// src/tasks/cluster-order.ts
|
|
18929
|
-
import { nanoid as
|
|
19057
|
+
import { nanoid as nanoid26 } from "nanoid";
|
|
18930
19058
|
function chainLess2(a, b) {
|
|
18931
19059
|
const aq = a.queuePosition ?? Number.MAX_SAFE_INTEGER;
|
|
18932
19060
|
const bq = b.queuePosition ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -19032,7 +19160,7 @@ function dispatchProposedLinks(db, log, projectId, anchorKey, addLinks, memberKe
|
|
|
19032
19160
|
links.push({ sourceIssueKey: source, rel: "depends_on", targetIssueKey: target });
|
|
19033
19161
|
}
|
|
19034
19162
|
if (links.length === 0) return;
|
|
19035
|
-
const actionId2 = `clnk_${
|
|
19163
|
+
const actionId2 = `clnk_${nanoid26(16)}`;
|
|
19036
19164
|
const action = { type: "apply_tracker_links", draftId: actionId2, projectId, links };
|
|
19037
19165
|
const sent = sendToProjectCli(projectId, { type: "dispatch_action", actionId: actionId2, action });
|
|
19038
19166
|
if (sent) {
|
|
@@ -21020,7 +21148,7 @@ __export(cluster_finalize_exports, {
|
|
|
21020
21148
|
maybeFinalizeCluster: () => maybeFinalizeCluster,
|
|
21021
21149
|
recordClusterPrCreated: () => recordClusterPrCreated
|
|
21022
21150
|
});
|
|
21023
|
-
import { nanoid as
|
|
21151
|
+
import { nanoid as nanoid27 } from "nanoid";
|
|
21024
21152
|
async function isFullAutomation2(db, projectId) {
|
|
21025
21153
|
const row = await db.get(`SELECT automation_level as automationLevel FROM projects WHERE id = ?`, projectId);
|
|
21026
21154
|
return row?.automationLevel === "full";
|
|
@@ -21041,7 +21169,7 @@ async function loadMergePolicy2(db, projectId) {
|
|
|
21041
21169
|
};
|
|
21042
21170
|
}
|
|
21043
21171
|
async function enqueueAndDispatch(db, taskId2, action, log) {
|
|
21044
|
-
const id = `wa_${
|
|
21172
|
+
const id = `wa_${nanoid27(12)}`;
|
|
21045
21173
|
await engineStore(db).actions.appendPlan(
|
|
21046
21174
|
taskId2,
|
|
21047
21175
|
[{ id, kind: action.type, payloadJson: JSON.stringify(action) }],
|
|
@@ -21771,7 +21899,7 @@ __export(tracking_commands_exports, {
|
|
|
21771
21899
|
handleUpdatePr: () => handleUpdatePr,
|
|
21772
21900
|
loadCreatePRTargets: () => loadCreatePRTargets
|
|
21773
21901
|
});
|
|
21774
|
-
import { nanoid as
|
|
21902
|
+
import { nanoid as nanoid28 } from "nanoid";
|
|
21775
21903
|
async function loadCreatePRTargets(db, taskId2) {
|
|
21776
21904
|
const rows = await db.all(`SELECT payload_json AS payloadJson, result_json AS resultJson
|
|
21777
21905
|
FROM write_actions
|
|
@@ -21871,7 +21999,7 @@ async function handleApprove(db, task, log) {
|
|
|
21871
21999
|
const now = Date.now();
|
|
21872
22000
|
const ids = [];
|
|
21873
22001
|
for (const t of targets) {
|
|
21874
|
-
const id = `wa_${
|
|
22002
|
+
const id = `wa_${nanoid28(12)}`;
|
|
21875
22003
|
const action = {
|
|
21876
22004
|
type: "merge_pr",
|
|
21877
22005
|
taskId: chainTaskId,
|
|
@@ -22059,7 +22187,7 @@ async function handleClusterReject(db, task, comment, log) {
|
|
|
22059
22187
|
if (carrier) {
|
|
22060
22188
|
const now = Date.now();
|
|
22061
22189
|
for (const t of await loadCreatePRTargets(db, carrier)) {
|
|
22062
|
-
const id = `wa_${
|
|
22190
|
+
const id = `wa_${nanoid28(12)}`;
|
|
22063
22191
|
const action = {
|
|
22064
22192
|
type: "close_pr",
|
|
22065
22193
|
taskId: carrier,
|
|
@@ -22084,7 +22212,7 @@ async function handleClusterReject(db, task, comment, log) {
|
|
|
22084
22212
|
if (deliveredRepos.size > 0) {
|
|
22085
22213
|
const now = Date.now();
|
|
22086
22214
|
for (const repoName of deliveredRepos) {
|
|
22087
|
-
const id = `wa_${
|
|
22215
|
+
const id = `wa_${nanoid28(12)}`;
|
|
22088
22216
|
const action = {
|
|
22089
22217
|
type: "rewrite_cluster_branch",
|
|
22090
22218
|
taskId: owner.id,
|
|
@@ -24645,25 +24773,25 @@ async function requestGrantsFrom(spec, recipient, bindingVersion, log, timeoutMs
|
|
|
24645
24773
|
const requestId2 = msg.requestId;
|
|
24646
24774
|
return await new Promise((resolve2) => {
|
|
24647
24775
|
const timer = setTimeout(() => {
|
|
24648
|
-
|
|
24776
|
+
pending2.delete(requestId2);
|
|
24649
24777
|
log.warn({ projectId: spec.projectId, jobId: spec.jobId, sandboxId: recipient.sandboxId }, "credential grants: CLI did not answer in time");
|
|
24650
24778
|
resolve2(null);
|
|
24651
24779
|
}, timeoutMs);
|
|
24652
|
-
|
|
24780
|
+
pending2.set(requestId2, {
|
|
24653
24781
|
projectId: spec.projectId,
|
|
24654
24782
|
jobId: spec.jobId,
|
|
24655
24783
|
sandboxId: recipient.sandboxId,
|
|
24656
24784
|
holder,
|
|
24657
24785
|
resolve: (answer) => {
|
|
24658
24786
|
clearTimeout(timer);
|
|
24659
|
-
|
|
24787
|
+
pending2.delete(requestId2);
|
|
24660
24788
|
resolve2(answer);
|
|
24661
24789
|
}
|
|
24662
24790
|
});
|
|
24663
24791
|
const sent = holder.kind === "provider" ? sendToAgentLoginProvider(holder.ownerKey, holder.providerId, msg) : sendToProjectCli(spec.projectId, msg);
|
|
24664
24792
|
if (!sent) {
|
|
24665
24793
|
clearTimeout(timer);
|
|
24666
|
-
|
|
24794
|
+
pending2.delete(requestId2);
|
|
24667
24795
|
resolve2(null);
|
|
24668
24796
|
}
|
|
24669
24797
|
});
|
|
@@ -24775,7 +24903,7 @@ async function sweepExpiredGrants(db, now = Date.now()) {
|
|
|
24775
24903
|
const { changes } = await db.run(`DELETE FROM credential_grants WHERE not_after <= ?`, now);
|
|
24776
24904
|
return changes;
|
|
24777
24905
|
}
|
|
24778
|
-
var GRANT_REQUEST_TIMEOUT_MS,
|
|
24906
|
+
var GRANT_REQUEST_TIMEOUT_MS, pending2, LATE_ANSWER_TTL_MS, lateAnswers;
|
|
24779
24907
|
var init_credential_grants = __esm({
|
|
24780
24908
|
"src/tracker/credential-grants.ts"() {
|
|
24781
24909
|
"use strict";
|
|
@@ -24786,7 +24914,7 @@ var init_credential_grants = __esm({
|
|
|
24786
24914
|
init_ws();
|
|
24787
24915
|
init_sandbox_keys();
|
|
24788
24916
|
GRANT_REQUEST_TIMEOUT_MS = 6e3;
|
|
24789
|
-
|
|
24917
|
+
pending2 = /* @__PURE__ */ new Map();
|
|
24790
24918
|
LATE_ANSWER_TTL_MS = 6e4;
|
|
24791
24919
|
lateAnswers = /* @__PURE__ */ new Map();
|
|
24792
24920
|
}
|
|
@@ -24840,7 +24968,7 @@ init_regimen_config();
|
|
|
24840
24968
|
// src/projects/cred-updates.ts
|
|
24841
24969
|
init_dist();
|
|
24842
24970
|
init_ws();
|
|
24843
|
-
import { nanoid as
|
|
24971
|
+
import { nanoid as nanoid29 } from "nanoid";
|
|
24844
24972
|
async function latestCredUpdates(db, projectId) {
|
|
24845
24973
|
const rows = await db.all(
|
|
24846
24974
|
`SELECT id, kind, state, detail, updated_at as updatedAt
|
|
@@ -24865,6 +24993,7 @@ async function latestCredUpdates(db, projectId) {
|
|
|
24865
24993
|
|
|
24866
24994
|
// src/application/read-model.ts
|
|
24867
24995
|
init_ws();
|
|
24996
|
+
init_farm2();
|
|
24868
24997
|
init_queue_view();
|
|
24869
24998
|
init_state_machine();
|
|
24870
24999
|
init_delivery_decision();
|
|
@@ -25076,7 +25205,7 @@ function configProposalRow(stored) {
|
|
|
25076
25205
|
}
|
|
25077
25206
|
|
|
25078
25207
|
// src/config-proposals/state-machine.ts
|
|
25079
|
-
import { nanoid as
|
|
25208
|
+
import { nanoid as nanoid30 } from "nanoid";
|
|
25080
25209
|
function rowToProposal(raw) {
|
|
25081
25210
|
return {
|
|
25082
25211
|
id: raw["id"],
|
|
@@ -25101,7 +25230,7 @@ var PROPOSAL_COLUMNS = `id, project_id as projectId, user_input as userInput, st
|
|
|
25101
25230
|
created_at as createdAt, updated_at as updatedAt`;
|
|
25102
25231
|
var PROPOSAL_SELECT = `SELECT ${PROPOSAL_COLUMNS} FROM config_proposals`;
|
|
25103
25232
|
async function createProposal(db, projectId, userInput) {
|
|
25104
|
-
const id = `cfp_${
|
|
25233
|
+
const id = `cfp_${nanoid30(16)}`;
|
|
25105
25234
|
const now = Date.now();
|
|
25106
25235
|
await db.run(
|
|
25107
25236
|
`INSERT INTO config_proposals
|
|
@@ -25803,7 +25932,7 @@ async function projectDetailOf(db, projectId, raw) {
|
|
|
25803
25932
|
actionErrors: getProjectActions(projectId)?.actionErrors ?? []
|
|
25804
25933
|
};
|
|
25805
25934
|
}
|
|
25806
|
-
function createReadModel(db) {
|
|
25935
|
+
function createReadModel(db, options = {}) {
|
|
25807
25936
|
const SUBMISSION_COLUMNS = `${DRAFT_COLUMNS},
|
|
25808
25937
|
(SELECT COUNT(*) FROM draft_tasks c WHERE c.parent_draft_id = draft_tasks.id) as childCount,
|
|
25809
25938
|
EXISTS (SELECT 1 FROM tasks t
|
|
@@ -26495,6 +26624,35 @@ function createReadModel(db) {
|
|
|
26495
26624
|
const p = await getPresence(db, { email: viewer.email ?? "", userId: viewer.subjectId });
|
|
26496
26625
|
return presenceRowOf(p, new Set(viewer.projectIds));
|
|
26497
26626
|
},
|
|
26627
|
+
// --- the account's fleet (DEV-927) ---
|
|
26628
|
+
async listFarms(viewer) {
|
|
26629
|
+
const isOperator = viewer.email !== null && (options.isOperator?.(viewer.email) ?? false);
|
|
26630
|
+
return await listFarms(db, { email: viewer.email ?? "", userId: viewer.subjectId, isSuperAdmin: isOperator });
|
|
26631
|
+
},
|
|
26632
|
+
async listProjectDeliverables(projectId) {
|
|
26633
|
+
const rows = await db.all(
|
|
26634
|
+
`SELECT id, tracker_issue_key AS "taskKey", title, phase, updated_at AS "updatedAt"
|
|
26635
|
+
FROM tasks
|
|
26636
|
+
WHERE project_id = ? AND (archived IS NULL OR archived = 0)
|
|
26637
|
+
AND phase IN ('TRACKING', 'AWAITING_CLUSTER', 'DONE', 'NEEDS_MANUAL')
|
|
26638
|
+
ORDER BY CASE WHEN phase IN ('TRACKING', 'AWAITING_CLUSTER') THEN 0 ELSE 1 END,
|
|
26639
|
+
updated_at DESC
|
|
26640
|
+
LIMIT 50`,
|
|
26641
|
+
projectId
|
|
26642
|
+
);
|
|
26643
|
+
const out = [];
|
|
26644
|
+
for (const r of rows) {
|
|
26645
|
+
const byRepo = await loadTaskBranchesByRepo(db, r.id);
|
|
26646
|
+
const repos = Object.entries(byRepo).map(([repoName, b]) => ({
|
|
26647
|
+
repoName,
|
|
26648
|
+
branchName: b.branchName,
|
|
26649
|
+
baseRef: b.baseRef
|
|
26650
|
+
}));
|
|
26651
|
+
if (repos.length === 0) continue;
|
|
26652
|
+
out.push({ taskKey: r.taskKey, title: r.title, phase: r.phase, updatedAt: Number(r.updatedAt), repos });
|
|
26653
|
+
}
|
|
26654
|
+
return out;
|
|
26655
|
+
},
|
|
26498
26656
|
async readNotificationSettings(subjectId) {
|
|
26499
26657
|
const user = await db.get(`SELECT 1 as hit FROM users WHERE id = ?`, subjectId);
|
|
26500
26658
|
if (!user) return null;
|
|
@@ -26635,7 +26793,7 @@ function createAuthorizer(db, config) {
|
|
|
26635
26793
|
// src/jobs/apply-result.ts
|
|
26636
26794
|
init_apply_overrides();
|
|
26637
26795
|
init_publish_delivery();
|
|
26638
|
-
import { nanoid as
|
|
26796
|
+
import { nanoid as nanoid31 } from "nanoid";
|
|
26639
26797
|
|
|
26640
26798
|
// src/drafts/result-handler.ts
|
|
26641
26799
|
init_dist();
|
|
@@ -27251,7 +27409,7 @@ async function applyJobResult(fx, job, result) {
|
|
|
27251
27409
|
size_bytes = excluded.size_bytes,
|
|
27252
27410
|
source_job_id = excluded.source_job_id,
|
|
27253
27411
|
created_at = excluded.created_at`,
|
|
27254
|
-
`art_${
|
|
27412
|
+
`art_${nanoid31(16)}`,
|
|
27255
27413
|
job.taskId,
|
|
27256
27414
|
a.path,
|
|
27257
27415
|
encoding,
|
|
@@ -28018,7 +28176,7 @@ init_state_machine2();
|
|
|
28018
28176
|
// src/drafts/publish.ts
|
|
28019
28177
|
init_ws();
|
|
28020
28178
|
init_state_machine2();
|
|
28021
|
-
import { nanoid as
|
|
28179
|
+
import { nanoid as nanoid32 } from "nanoid";
|
|
28022
28180
|
function firstNonBlank(...vals) {
|
|
28023
28181
|
for (const v of vals) {
|
|
28024
28182
|
if (v != null && v.trim() !== "") return v;
|
|
@@ -28026,7 +28184,7 @@ function firstNonBlank(...vals) {
|
|
|
28026
28184
|
return "";
|
|
28027
28185
|
}
|
|
28028
28186
|
async function dispatchPublish(db, draft, targetStatus, log) {
|
|
28029
|
-
const publishActionId = `dpub_${
|
|
28187
|
+
const publishActionId = `dpub_${nanoid32(16)}`;
|
|
28030
28188
|
const title = firstNonBlank(draft.editedTitle, draft.generatedTitle);
|
|
28031
28189
|
const body = firstNonBlank(draft.editedBody, draft.generatedBody);
|
|
28032
28190
|
const project = await db.get(`SELECT youtrack_project_key as youtrackProjectKey FROM projects WHERE id = ?`, draft.projectId);
|
|
@@ -28172,7 +28330,7 @@ async function maybeAdvancePackage(db, parentDraftId, log) {
|
|
|
28172
28330
|
log.info({ draftId: parentDraftId }, "package published (no links) \u2192 DONE");
|
|
28173
28331
|
return;
|
|
28174
28332
|
}
|
|
28175
|
-
const linkActionId = `dpkg_${
|
|
28333
|
+
const linkActionId = `dpkg_${nanoid32(16)}`;
|
|
28176
28334
|
const action = {
|
|
28177
28335
|
type: "apply_tracker_links",
|
|
28178
28336
|
draftId: parentDraftId,
|
|
@@ -28252,7 +28410,83 @@ async function discardDraft(db, draft, log) {
|
|
|
28252
28410
|
init_dist2();
|
|
28253
28411
|
init_regimen_write();
|
|
28254
28412
|
init_ws();
|
|
28255
|
-
|
|
28413
|
+
|
|
28414
|
+
// src/farm/provision.ts
|
|
28415
|
+
init_ws();
|
|
28416
|
+
init_pool_spec();
|
|
28417
|
+
import { nanoid as nanoid33 } from "nanoid";
|
|
28418
|
+
var pending3 = /* @__PURE__ */ new Map();
|
|
28419
|
+
function awaitResult(requestId2, send, timeoutMs) {
|
|
28420
|
+
if (!send()) return Promise.reject(new Error("farm_offline"));
|
|
28421
|
+
return new Promise((resolve2, reject) => {
|
|
28422
|
+
const timer = setTimeout(() => {
|
|
28423
|
+
pending3.delete(requestId2);
|
|
28424
|
+
reject(new Error("farm_timeout"));
|
|
28425
|
+
}, timeoutMs);
|
|
28426
|
+
pending3.set(requestId2, { resolve: resolve2, reject, timer });
|
|
28427
|
+
});
|
|
28428
|
+
}
|
|
28429
|
+
function requestProvision(farmId, projectId, sealedBundle, timeoutMs) {
|
|
28430
|
+
const requestId2 = nanoid33(16);
|
|
28431
|
+
return awaitResult(
|
|
28432
|
+
requestId2,
|
|
28433
|
+
() => sendToFarm(farmId, { type: "provision_project", requestId: requestId2, projectId, sealedBundle }),
|
|
28434
|
+
timeoutMs
|
|
28435
|
+
);
|
|
28436
|
+
}
|
|
28437
|
+
function requestDelete(farmId, projectId, timeoutMs) {
|
|
28438
|
+
const requestId2 = nanoid33(16);
|
|
28439
|
+
return awaitResult(
|
|
28440
|
+
requestId2,
|
|
28441
|
+
() => sendToFarm(farmId, { type: "delete_project", requestId: requestId2, projectId }),
|
|
28442
|
+
timeoutMs
|
|
28443
|
+
);
|
|
28444
|
+
}
|
|
28445
|
+
async function requestFarmPoolSync(db, farmId) {
|
|
28446
|
+
const accounts = await computeFarmPoolSpec(db, farmId);
|
|
28447
|
+
sendToFarm(farmId, buildSyncWorkerPoolMessage(accounts));
|
|
28448
|
+
}
|
|
28449
|
+
|
|
28450
|
+
// src/runner/provision.ts
|
|
28451
|
+
init_ws();
|
|
28452
|
+
import { nanoid as nanoid34 } from "nanoid";
|
|
28453
|
+
var pending4 = /* @__PURE__ */ new Map();
|
|
28454
|
+
function awaitResult2(requestId2, send, timeoutMs) {
|
|
28455
|
+
if (!send()) return Promise.reject(new Error("runner_offline"));
|
|
28456
|
+
return new Promise((resolve2, reject) => {
|
|
28457
|
+
const timer = setTimeout(() => {
|
|
28458
|
+
pending4.delete(requestId2);
|
|
28459
|
+
reject(new Error("runner_timeout"));
|
|
28460
|
+
}, timeoutMs);
|
|
28461
|
+
pending4.set(requestId2, { resolve: resolve2, reject, timer });
|
|
28462
|
+
});
|
|
28463
|
+
}
|
|
28464
|
+
function requestGhRunnersProvision(runnerId, projectId, sealedBundle, timeoutMs) {
|
|
28465
|
+
const requestId2 = nanoid34(16);
|
|
28466
|
+
return awaitResult2(
|
|
28467
|
+
requestId2,
|
|
28468
|
+
() => sendToRunner(runnerId, { type: "provision_gh_runners", requestId: requestId2, projectId, sealedBundle }),
|
|
28469
|
+
timeoutMs
|
|
28470
|
+
);
|
|
28471
|
+
}
|
|
28472
|
+
function requestGhRunnersDelete(runnerId, projectId, sealedBundle, timeoutMs) {
|
|
28473
|
+
const requestId2 = nanoid34(16);
|
|
28474
|
+
return awaitResult2(
|
|
28475
|
+
requestId2,
|
|
28476
|
+
() => sendToRunner(runnerId, {
|
|
28477
|
+
type: "delete_gh_runners",
|
|
28478
|
+
requestId: requestId2,
|
|
28479
|
+
projectId,
|
|
28480
|
+
...sealedBundle ? { sealedBundle } : {}
|
|
28481
|
+
}),
|
|
28482
|
+
timeoutMs
|
|
28483
|
+
);
|
|
28484
|
+
}
|
|
28485
|
+
|
|
28486
|
+
// src/application/write-model.ts
|
|
28487
|
+
init_farm2();
|
|
28488
|
+
init_runner2();
|
|
28489
|
+
init_init_status();
|
|
28256
28490
|
|
|
28257
28491
|
// src/projects/forget.ts
|
|
28258
28492
|
init_legacy_cred_bundles();
|
|
@@ -28296,8 +28530,57 @@ async function deleteProjectData(db, projectId) {
|
|
|
28296
28530
|
});
|
|
28297
28531
|
}
|
|
28298
28532
|
|
|
28533
|
+
// src/runner/bindings.ts
|
|
28534
|
+
function parseGhRunnerBindings(json) {
|
|
28535
|
+
if (!json) return [];
|
|
28536
|
+
try {
|
|
28537
|
+
const parsed = JSON.parse(json);
|
|
28538
|
+
return Array.isArray(parsed.bindings) ? parsed.bindings : [];
|
|
28539
|
+
} catch {
|
|
28540
|
+
return [];
|
|
28541
|
+
}
|
|
28542
|
+
}
|
|
28543
|
+
function serialize(bindings) {
|
|
28544
|
+
return JSON.stringify({ bindings });
|
|
28545
|
+
}
|
|
28546
|
+
async function upsertGhRunnerBinding(db, projectId, binding) {
|
|
28547
|
+
await db.tx(async (tx) => {
|
|
28548
|
+
const row = await tx.get(
|
|
28549
|
+
`SELECT gh_runner_bindings_json as bindingsJson FROM projects WHERE id = ?`,
|
|
28550
|
+
projectId
|
|
28551
|
+
);
|
|
28552
|
+
if (!row) return;
|
|
28553
|
+
const bindings = parseGhRunnerBindings(row.bindingsJson).filter(
|
|
28554
|
+
(b) => !(b.owner === binding.owner && b.repo === binding.repo)
|
|
28555
|
+
);
|
|
28556
|
+
bindings.push(binding);
|
|
28557
|
+
await tx.run(
|
|
28558
|
+
`UPDATE projects SET gh_runner_bindings_json = ? WHERE id = ?`,
|
|
28559
|
+
serialize(bindings),
|
|
28560
|
+
projectId
|
|
28561
|
+
);
|
|
28562
|
+
});
|
|
28563
|
+
}
|
|
28564
|
+
async function removeGhRunnerBinding(db, projectId, owner, repo) {
|
|
28565
|
+
return db.tx(async (tx) => {
|
|
28566
|
+
const row = await tx.get(
|
|
28567
|
+
`SELECT gh_runner_bindings_json as bindingsJson FROM projects WHERE id = ?`,
|
|
28568
|
+
projectId
|
|
28569
|
+
);
|
|
28570
|
+
if (!row) return false;
|
|
28571
|
+
const bindings = parseGhRunnerBindings(row.bindingsJson);
|
|
28572
|
+
const kept = bindings.filter((b) => !(b.owner === owner && b.repo === repo));
|
|
28573
|
+
if (kept.length === bindings.length) return false;
|
|
28574
|
+
await tx.run(
|
|
28575
|
+
`UPDATE projects SET gh_runner_bindings_json = ? WHERE id = ?`,
|
|
28576
|
+
serialize(kept),
|
|
28577
|
+
projectId
|
|
28578
|
+
);
|
|
28579
|
+
return true;
|
|
28580
|
+
});
|
|
28581
|
+
}
|
|
28582
|
+
|
|
28299
28583
|
// src/application/write-model.ts
|
|
28300
|
-
init_bindings();
|
|
28301
28584
|
init_tracking_commands();
|
|
28302
28585
|
|
|
28303
28586
|
// src/config-proposals/apply.ts
|
|
@@ -28373,10 +28656,26 @@ function conflict(reason) {
|
|
|
28373
28656
|
function stale() {
|
|
28374
28657
|
return { ok: false, refusal: "stale_revision" };
|
|
28375
28658
|
}
|
|
28659
|
+
function notFound() {
|
|
28660
|
+
return { ok: false, refusal: "not_found" };
|
|
28661
|
+
}
|
|
28662
|
+
var FARM_PROVISION_TIMEOUT_MS = 6e4;
|
|
28663
|
+
var GH_RUNNER_RELAY_TIMEOUT_MS = 12e4;
|
|
28664
|
+
function nodeUnavailable(capability, err) {
|
|
28665
|
+
const reason = err instanceof Error ? err.message : `${capability}_error`;
|
|
28666
|
+
const what = capability === "farm" ? "The farm" : "The runner host";
|
|
28667
|
+
return {
|
|
28668
|
+
ok: false,
|
|
28669
|
+
refusal: "capability_unavailable",
|
|
28670
|
+
capability,
|
|
28671
|
+
message: reason === `${capability}_timeout` ? `${what} did not answer in time. Try again once it is responsive.` : reason === `${capability}_offline` ? `${what} is offline. Start it and try again.` : `${what} could not be reached for this. Try again in a moment.`,
|
|
28672
|
+
retryable: true
|
|
28673
|
+
};
|
|
28674
|
+
}
|
|
28376
28675
|
function taskState(row) {
|
|
28377
28676
|
return { ...row, revision: String(row.updatedAt) };
|
|
28378
28677
|
}
|
|
28379
|
-
function createWriteModel(db, log) {
|
|
28678
|
+
function createWriteModel(db, log, hooks = {}) {
|
|
28380
28679
|
async function readTaskRow(projectId, key) {
|
|
28381
28680
|
const row = await db.get(
|
|
28382
28681
|
`SELECT project_id as projectId, tracker_issue_key as key, phase,
|
|
@@ -28451,6 +28750,13 @@ function createWriteModel(db, log) {
|
|
|
28451
28750
|
if (sandboxId !== null) sendToSandbox(sandboxId, { type: "pause_job", jobId: stored.sourceJobId });
|
|
28452
28751
|
await cancelActiveJobs(db, `config::${stored.id}`);
|
|
28453
28752
|
}
|
|
28753
|
+
function isOperator(actor) {
|
|
28754
|
+
return actor.email !== null && (hooks.isOperator?.(actor.email) ?? false);
|
|
28755
|
+
}
|
|
28756
|
+
async function ghRunnerHost(runnerId, actor) {
|
|
28757
|
+
const owns = isOperator(actor) ? await db.get(`SELECT id FROM runners WHERE id = ?`, runnerId) !== void 0 : await viewerOwnsRunner(db, { email: actor.email ?? "", userId: actor.subjectId }, runnerId);
|
|
28758
|
+
return owns ? ok(true) : notFound();
|
|
28759
|
+
}
|
|
28454
28760
|
return {
|
|
28455
28761
|
async readTask(projectId, taskKey) {
|
|
28456
28762
|
const row = await readTaskRow(projectId, taskKey);
|
|
@@ -28922,6 +29228,143 @@ function createWriteModel(db, log) {
|
|
|
28922
29228
|
* daemon behind it. The Context Repo remote is not touched: what the
|
|
28923
29229
|
* pipeline wrote about the work outlives the record of the project.
|
|
28924
29230
|
*/
|
|
29231
|
+
// --- registration and the account's fleet (DEV-927) ---
|
|
29232
|
+
async createProject({ project, owner }) {
|
|
29233
|
+
const trackerProvider = project.trackerProvider;
|
|
29234
|
+
const inserted = await db.tx(async (tx) => {
|
|
29235
|
+
const existing = await tx.get(`SELECT id FROM projects WHERE id = ?`, project.projectId);
|
|
29236
|
+
if (existing) return conflict("project_already_exists");
|
|
29237
|
+
const now = Date.now();
|
|
29238
|
+
await tx.run(
|
|
29239
|
+
`INSERT INTO projects (id, name, automation_level, youtrack_project_key, tracker_provider,
|
|
29240
|
+
repos_json, owner_email, owner_user_id, language_json,
|
|
29241
|
+
provisioning_status, context_repo_json, provisioned_at, provisioning_error, created_at)
|
|
29242
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'provisioned', ?, ?, NULL, ?)`,
|
|
29243
|
+
project.projectId,
|
|
29244
|
+
project.name,
|
|
29245
|
+
project.automationLevel,
|
|
29246
|
+
project.trackerProjectKey,
|
|
29247
|
+
trackerProvider,
|
|
29248
|
+
JSON.stringify(
|
|
29249
|
+
project.repos.map((r) => ({
|
|
29250
|
+
name: r.name,
|
|
29251
|
+
host: r.host,
|
|
29252
|
+
owner: r.owner,
|
|
29253
|
+
repoName: r.repoName,
|
|
29254
|
+
...r.defaultBranch ? { defaultBranch: r.defaultBranch } : {},
|
|
29255
|
+
...r.mountAs ? { mountAs: r.mountAs } : {},
|
|
29256
|
+
...r.mirror ? { mirror: { host: r.mirror.host, owner: r.mirror.owner, repoName: r.mirror.repoName } } : {}
|
|
29257
|
+
}))
|
|
29258
|
+
),
|
|
29259
|
+
owner.email,
|
|
29260
|
+
owner.subjectId,
|
|
29261
|
+
project.language === null ? null : JSON.stringify(project.language),
|
|
29262
|
+
JSON.stringify(project.contextRepo),
|
|
29263
|
+
now,
|
|
29264
|
+
now
|
|
29265
|
+
);
|
|
29266
|
+
return ok({ projectId: project.projectId });
|
|
29267
|
+
});
|
|
29268
|
+
if (!inserted.ok) return inserted;
|
|
29269
|
+
await beginInitTracking(db, project.projectId, "registered");
|
|
29270
|
+
log.info({ projectId: project.projectId, contextRepo: project.contextRepo }, "project registered, Context Repo provisioned by the client");
|
|
29271
|
+
hooks.projectRegistered?.({
|
|
29272
|
+
projectId: project.projectId,
|
|
29273
|
+
name: project.name,
|
|
29274
|
+
automationLevel: project.automationLevel,
|
|
29275
|
+
ownerEmail: owner.email,
|
|
29276
|
+
repoCount: project.repos.length
|
|
29277
|
+
});
|
|
29278
|
+
return inserted;
|
|
29279
|
+
},
|
|
29280
|
+
async provisionOnFarm({ farmId, projectId, sealedBundle, actor }) {
|
|
29281
|
+
const usable = isOperator(actor) ? await db.get(`SELECT id FROM farms WHERE id = ?`, farmId) !== void 0 : await viewerOwnsFarm(db, { email: actor.email ?? "", userId: actor.subjectId }, farmId);
|
|
29282
|
+
if (!usable) return notFound();
|
|
29283
|
+
await beginInitTracking(db, projectId, "farm_provisioning");
|
|
29284
|
+
let result;
|
|
29285
|
+
try {
|
|
29286
|
+
result = await requestProvision(farmId, projectId, sealedBundle, FARM_PROVISION_TIMEOUT_MS);
|
|
29287
|
+
} catch (err) {
|
|
29288
|
+
const reason = err instanceof Error ? err.message : "provision_failed";
|
|
29289
|
+
await recordInitError(
|
|
29290
|
+
db,
|
|
29291
|
+
projectId,
|
|
29292
|
+
reason === "farm_offline" ? "the hosting farm is offline" : reason === "farm_timeout" ? "the hosting farm did not respond in time" : `farm provisioning failed: ${reason}`
|
|
29293
|
+
);
|
|
29294
|
+
return nodeUnavailable("farm", err);
|
|
29295
|
+
}
|
|
29296
|
+
if (result.ok) {
|
|
29297
|
+
await db.run(`UPDATE projects SET farm_id = ? WHERE id = ?`, farmId, projectId);
|
|
29298
|
+
await advanceInitPhase(db, projectId, "daemon_starting");
|
|
29299
|
+
requestFarmPoolSync(db, farmId).catch(
|
|
29300
|
+
(err) => log.warn({ farmId, err }, "farm pool sync after provision failed")
|
|
29301
|
+
);
|
|
29302
|
+
} else {
|
|
29303
|
+
await recordInitError(db, projectId, `farm provisioning failed: ${result.error ?? "unknown error"}`);
|
|
29304
|
+
}
|
|
29305
|
+
return ok({ ok: result.ok, daemonStatus: result.daemonStatus ?? null, error: result.error ?? null });
|
|
29306
|
+
},
|
|
29307
|
+
async provisionGhRunners({ runnerId, projectId, sealedBundle, actor }) {
|
|
29308
|
+
const guard = await ghRunnerHost(runnerId, actor);
|
|
29309
|
+
if (!guard.ok) return guard;
|
|
29310
|
+
let result;
|
|
29311
|
+
try {
|
|
29312
|
+
result = await requestGhRunnersProvision(runnerId, projectId, sealedBundle, GH_RUNNER_RELAY_TIMEOUT_MS);
|
|
29313
|
+
} catch (err) {
|
|
29314
|
+
return nodeUnavailable("runner", err);
|
|
29315
|
+
}
|
|
29316
|
+
const runners = result.runners ?? [];
|
|
29317
|
+
if (result.ok) {
|
|
29318
|
+
const connectedAt = Date.now();
|
|
29319
|
+
for (const r of runners) {
|
|
29320
|
+
await upsertGhRunnerBinding(db, projectId, {
|
|
29321
|
+
owner: r.owner,
|
|
29322
|
+
repo: r.repo,
|
|
29323
|
+
runnerId,
|
|
29324
|
+
connectedAt,
|
|
29325
|
+
status: r.status,
|
|
29326
|
+
...r.error ? { error: r.error } : {},
|
|
29327
|
+
...r.labels ? { labels: r.labels } : {}
|
|
29328
|
+
});
|
|
29329
|
+
}
|
|
29330
|
+
}
|
|
29331
|
+
return ok({
|
|
29332
|
+
ok: result.ok,
|
|
29333
|
+
runners: runners.map((r) => ({ owner: r.owner, repo: r.repo, status: r.status, error: r.error ?? null, labels: r.labels ?? [] })),
|
|
29334
|
+
error: result.error ?? null
|
|
29335
|
+
});
|
|
29336
|
+
},
|
|
29337
|
+
async teardownGhRunners({ runnerId, projectId, sealedBundle, repo, actor }) {
|
|
29338
|
+
const guard = await ghRunnerHost(runnerId, actor);
|
|
29339
|
+
if (!guard.ok) return guard;
|
|
29340
|
+
let result;
|
|
29341
|
+
try {
|
|
29342
|
+
result = await requestGhRunnersDelete(runnerId, projectId, sealedBundle ?? void 0, GH_RUNNER_RELAY_TIMEOUT_MS);
|
|
29343
|
+
} catch (err) {
|
|
29344
|
+
return nodeUnavailable("runner", err);
|
|
29345
|
+
}
|
|
29346
|
+
if (result.ok && repo !== null) {
|
|
29347
|
+
await removeGhRunnerBinding(db, projectId, repo.owner, repo.repo);
|
|
29348
|
+
}
|
|
29349
|
+
return ok({ ok: result.ok, error: result.error ?? null });
|
|
29350
|
+
},
|
|
29351
|
+
async enqueueProjectIndex({ projectId, kind }) {
|
|
29352
|
+
const p = await db.get(`SELECT id, context_repo_json as contextRepoJson FROM projects WHERE id = ?`, projectId);
|
|
29353
|
+
if (!p) return notFound();
|
|
29354
|
+
if (!p.contextRepoJson) return conflict("context_repo_not_provisioned");
|
|
29355
|
+
const jobId = kind === "backfill" ? await enqueueBackfillJob(db, projectId) : await enqueueIndexJob(db, projectId);
|
|
29356
|
+
await advanceInitPhase(db, projectId, "indexing");
|
|
29357
|
+
log.info({ projectId, jobId, kind }, kind === "backfill" ? "backfill job enqueued" : "index job enqueued");
|
|
29358
|
+
return ok({ jobId });
|
|
29359
|
+
},
|
|
29360
|
+
async reportInitStatus({ projectId, phase, error }) {
|
|
29361
|
+
const recorded = await advanceInitPhase(db, projectId, phase);
|
|
29362
|
+
if (error !== null) {
|
|
29363
|
+
const cur = await getInitStatus(db, projectId);
|
|
29364
|
+
if (cur && cur.phase === phase) await recordInitError(db, projectId, error);
|
|
29365
|
+
}
|
|
29366
|
+
return ok({ recorded });
|
|
29367
|
+
},
|
|
28925
29368
|
async deleteProject({ projectId }) {
|
|
28926
29369
|
const row = await db.get(`SELECT farm_id as farmId FROM projects WHERE id = ?`, projectId);
|
|
28927
29370
|
if (!row) return conflict("project_not_found");
|
|
@@ -29289,9 +29732,9 @@ function createDirectPipeline(deps) {
|
|
|
29289
29732
|
const materialization = {
|
|
29290
29733
|
async next(projectId) {
|
|
29291
29734
|
for (; ; ) {
|
|
29292
|
-
const
|
|
29293
|
-
if (!
|
|
29294
|
-
const prepared = await prepareDispatch(db,
|
|
29735
|
+
const pending5 = await nextPendingProjectAction(db, projectId);
|
|
29736
|
+
if (!pending5) return null;
|
|
29737
|
+
const prepared = await prepareDispatch(db, pending5.id, log);
|
|
29295
29738
|
if (prepared.kind === "settled") continue;
|
|
29296
29739
|
if (prepared.kind !== "ready") return null;
|
|
29297
29740
|
await markDispatched(db, prepared.actionId);
|
|
@@ -29498,7 +29941,7 @@ async function compose(db, config, log, issueGrants) {
|
|
|
29498
29941
|
const notify2 = { db, config, channels: [new EmailChannel(mailer)], log };
|
|
29499
29942
|
setNotifyDeps(notify2);
|
|
29500
29943
|
const authorizer = createAuthorizer(db, config);
|
|
29501
|
-
const reads = createReadOperations(createReadModel(db), authorizer);
|
|
29944
|
+
const reads = createReadOperations(createReadModel(db, { isOperator: (email) => isSuperUser(config, email) }), authorizer);
|
|
29502
29945
|
const pipeline = createDirectPipeline({
|
|
29503
29946
|
db,
|
|
29504
29947
|
log,
|