@code-partner/codepipe-hub 0.14.1-dev.390.g897b6f57 → 0.14.1-dev.392.g2ee041d5

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.
@@ -27,7 +27,7 @@
27
27
  localStorage.getItem('theme') ?? 'dark'
28
28
  );
29
29
  </script>
30
- <script type="module" crossorigin src="/assets/index-Bgq2ZBdp.js"></script>
30
+ <script type="module" crossorigin src="/assets/index-Cpy8aKZb.js"></script>
31
31
  <link rel="stylesheet" crossorigin href="/assets/index-CD8JrtmW.css">
32
32
  </head>
33
33
  <body>
package/hub/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var PLATFORM_VERSION;
13
13
  var init_version_generated = __esm({
14
14
  "../packages/shared/dist/version.generated.js"() {
15
15
  "use strict";
16
- PLATFORM_VERSION = "0.14.1-dev.390.g897b6f57";
16
+ PLATFORM_VERSION = "0.14.1-dev.392.g2ee041d5";
17
17
  }
18
18
  });
19
19
 
@@ -1296,20 +1296,24 @@ var init_project_settings = __esm({
1296
1296
  {
1297
1297
  id: "llm",
1298
1298
  label: "LLM",
1299
- // DEV-510 (ADR-0025): the project's LLM provider a strict enum with a
1300
- // single value for now; the control is the anchor for future providers
1301
- // (codex). Rendered above the per-role model sub-group.
1299
+ // DEV-510 (ADR-0025): the project's LLM provider. DEV-760 made the second
1300
+ // harness real, so this is a genuine choice — the enum carries every
1301
+ // provider the runtime supports. Rendered above the per-role model
1302
+ // sub-group.
1302
1303
  fields: [
1303
1304
  {
1304
1305
  id: "llm.provider",
1305
1306
  file: "local.yaml",
1306
1307
  path: ["llm", "provider"],
1307
1308
  type: "enum",
1308
- label: "Provider",
1309
- // Always a concrete provider (claude) — never cleared.
1309
+ label: "Default provider",
1310
+ // Always a concrete provider — never cleared.
1310
1311
  nullable: false,
1311
1312
  suggestions: [...LLM_PROVIDER_SUGGESTIONS],
1312
- help: "LLM backend for this project. Currently only claude is available."
1313
+ // DEV-896: the old text ("currently only claude is available") outlived
1314
+ // its truth and read as "there is nothing to choose" — the one thing
1315
+ // help must never say about a live control.
1316
+ help: "The agent harness every role runs on by default. Per-role overrides (and the fallback agent) live in the shared config's routing block."
1313
1317
  }
1314
1318
  ],
1315
1319
  groups: [
@@ -2310,6 +2314,13 @@ var init_runner = __esm({
2310
2314
  }
2311
2315
  });
2312
2316
 
2317
+ // ../packages/shared/dist/git-transport.js
2318
+ var init_git_transport = __esm({
2319
+ "../packages/shared/dist/git-transport.js"() {
2320
+ "use strict";
2321
+ }
2322
+ });
2323
+
2313
2324
  // ../packages/shared/dist/delivery-config.js
2314
2325
  function localFirstDefaults() {
2315
2326
  return {
@@ -3036,6 +3047,7 @@ var init_dist = __esm({
3036
3047
  init_farm();
3037
3048
  init_runner();
3038
3049
  init_project();
3050
+ init_git_transport();
3039
3051
  init_delivery_config();
3040
3052
  init_delivery_capabilities();
3041
3053
  init_revision_ref();
@@ -10498,10 +10510,26 @@ async function loadSourceSnapshots(db, projectId) {
10498
10510
  async function dirtySourceRepos(db, projectId) {
10499
10511
  return (await loadSourceSnapshots(db, projectId)).filter((s) => s.dirty).map((s) => s.repoName);
10500
10512
  }
10513
+ async function unpinnedSourceRepos(db, projectId) {
10514
+ const axes = await loadDeliveryAxes(db, projectId, {
10515
+ localFirstDefault: deliveryProfileLocalFirst()
10516
+ });
10517
+ if (axes.source.kind !== "local_snapshot") return [];
10518
+ const row = await db.get(
10519
+ `SELECT repos_json as reposJson FROM projects WHERE id = ?`,
10520
+ projectId
10521
+ );
10522
+ if (!row) return [];
10523
+ const repos = JSON.parse(row.reposJson);
10524
+ const pinned = new Set((await loadSourceSnapshots(db, projectId)).map((s) => s.repoName));
10525
+ return repos.map((r) => r.name).filter((name) => !pinned.has(name));
10526
+ }
10501
10527
  var SHA_RE;
10502
10528
  var init_source_snapshots = __esm({
10503
10529
  "src/projects/source-snapshots.ts"() {
10504
10530
  "use strict";
10531
+ init_delivery_plan();
10532
+ init_publication();
10505
10533
  SHA_RE = /^[0-9a-f]{40}$/;
10506
10534
  }
10507
10535
  });
@@ -10849,18 +10877,32 @@ async function claimNextJob(db, sandboxId, capabilities, lane) {
10849
10877
  // produced a delivery whose diff carried the whole branch's work and could
10850
10878
  // never cherry-pick back. The conflict source (the default branch) is now
10851
10879
  // merged INTO the tip worktree by the SANDBOX (metadata.mergeBranchByRepo).
10880
+ // DEV-875: the SOURCE is re-resolved here too, for every role. A spec is
10881
+ // built when the job is queued, and a local-first project's pin may not
10882
+ // exist yet at that moment (the CLI reports it on start) — the spec then
10883
+ // froze the remote URL, which such a project may have no credential for.
10884
+ // Re-resolving at claim time hands the worker wherever the code actually
10885
+ // is now: the relay pin, or the mirror a project gained since.
10852
10886
  rewriteSpec: async (tx, row) => {
10853
- if (row.role !== "implement" && row.role !== "analyze" && row.role !== "estimate" && row.role !== "resolve-conflict") {
10854
- return null;
10855
- }
10887
+ const roleTakesChainRef = row.role === "implement" || row.role === "analyze" || row.role === "estimate" || row.role === "resolve-conflict";
10856
10888
  const txDb = dbOf(tx);
10857
- const chainRef = await loadChainTaskRef(txDb, row.taskId);
10858
- const baseByRepo = chainRef ? await clusterBranchByRepo(txDb, chainRef) : {};
10859
- const defaultRefByRepo = new Map(
10860
- (await loadProjectRepoRefs(txDb, row.projectId)).map((r) => [r.name, r.ref])
10861
- );
10889
+ const current = await loadProjectRepoRefs(txDb, row.projectId);
10890
+ const currentByRepo = new Map(current.map((r) => [r.name, r]));
10862
10891
  const spec2 = JSON.parse(row.specJson);
10892
+ if (spec2.metadata?.["externalReview"] === true) return null;
10863
10893
  let changed = false;
10894
+ for (const repo of spec2.projectRepos) {
10895
+ const fresh = currentByRepo.get(repo.name);
10896
+ if (!fresh || fresh.cloneUrl === repo.cloneUrl) continue;
10897
+ repo.cloneUrl = fresh.cloneUrl;
10898
+ repo.ref = fresh.ref;
10899
+ if (fresh.defaultBranch) repo.defaultBranch = fresh.defaultBranch;
10900
+ changed = true;
10901
+ }
10902
+ if (!roleTakesChainRef) return changed ? JSON.stringify(spec2) : null;
10903
+ const chainRef = await loadChainTaskRef(txDb, row.taskId);
10904
+ const baseByRepo = chainRef ? await clusterBranchByRepo(txDb, chainRef) : {};
10905
+ const defaultRefByRepo = new Map(current.map((r) => [r.name, r.ref]));
10864
10906
  for (const repo of spec2.projectRepos) {
10865
10907
  const next = baseByRepo[repo.name] ?? defaultRefByRepo.get(repo.name) ?? repo.ref;
10866
10908
  if (next !== repo.ref) {
@@ -18495,6 +18537,17 @@ async function developFromQueue(db, task, log, reason = "queue \u2192 develop")
18495
18537
  async function startTaskWorkflow(db, task, log) {
18496
18538
  await beginPipeline(db, task, log);
18497
18539
  }
18540
+ async function localSourceHold(db, projectId) {
18541
+ const dirty = await dirtySourceRepos(db, projectId);
18542
+ if (dirty.length > 0) {
18543
+ return `LOCAL_SOURCE_DIRTY: uncommitted changes in ${dirty.join(", ")} \u2014 commit or stash them, then retry`;
18544
+ }
18545
+ const unpinned = await unpinnedSourceRepos(db, projectId);
18546
+ if (unpinned.length > 0) {
18547
+ return `LOCAL_SOURCE_UNPINNED: no snapshot of ${unpinned.join(", ")} yet \u2014 start this project's CLI (\`codepipe up\`) so it publishes your current commit, then retry`;
18548
+ }
18549
+ return null;
18550
+ }
18498
18551
  async function beginPipeline(db, task, log) {
18499
18552
  if (task.phase !== "INTAKE") return;
18500
18553
  const deliveryPlan = await resolveProjectDeliveryPlan(db, task.projectId);
@@ -18504,11 +18557,10 @@ async function beginPipeline(db, task, log) {
18504
18557
  return;
18505
18558
  }
18506
18559
  if (deliveryPlan.plan.transport === "context_repo" && deliveryPlan.plan.target === "working_repo") {
18507
- const dirty = await dirtySourceRepos(db, task.projectId);
18508
- if (dirty.length > 0) {
18509
- const reason = `LOCAL_SOURCE_DIRTY: uncommitted changes in ${dirty.join(", ")} \u2014 commit or stash them, then retry`;
18510
- log.warn({ taskId: task.id, dirty }, "local source dirty \u2014 execution held");
18511
- await transitionPhase(db, task.id, "NEEDS_MANUAL", reason);
18560
+ const hold = await localSourceHold(db, task.projectId);
18561
+ if (hold) {
18562
+ log.warn({ taskId: task.id, hold }, "local source not usable \u2014 execution held");
18563
+ await transitionPhase(db, task.id, "NEEDS_MANUAL", hold);
18512
18564
  return;
18513
18565
  }
18514
18566
  }
@@ -19673,11 +19725,10 @@ async function triggerRework(db, task, instruction, source, log) {
19673
19725
  return;
19674
19726
  }
19675
19727
  if (deliveryPlan.plan.target === "working_repo") {
19676
- const dirty = await dirtySourceRepos(db, task.projectId);
19677
- if (dirty.length > 0) {
19678
- const reason = `LOCAL_SOURCE_DIRTY: uncommitted changes in ${dirty.join(", ")} \u2014 commit or stash them, then retry`;
19679
- log.warn({ taskId: task.id, dirty }, "local source dirty \u2014 rework held");
19680
- await transitionPhase(db, task.id, "NEEDS_MANUAL", reason);
19728
+ const hold = await localSourceHold(db, task.projectId);
19729
+ if (hold) {
19730
+ log.warn({ taskId: task.id, hold }, "local source not usable \u2014 rework held");
19731
+ await transitionPhase(db, task.id, "NEEDS_MANUAL", hold);
19681
19732
  return;
19682
19733
  }
19683
19734
  }
@@ -27219,10 +27270,12 @@ async function authRoutes(app, deps) {
27219
27270
  const session = await readSession(db, req);
27220
27271
  if (!session) return reply.status(401).send({ error: "no_session" });
27221
27272
  const user = await findUser(db, session.email);
27273
+ const singleOwner = profileAxes(config.profile ?? "server").authProvider === "single-owner";
27222
27274
  return {
27223
27275
  email: session.email,
27224
27276
  isSuperAdmin: isSuperUser(config, session.email),
27225
- passwordSet: user === null || user.passwordHash !== null
27277
+ passwordSet: singleOwner || user === null || user.passwordHash !== null,
27278
+ singleOwner
27226
27279
  };
27227
27280
  });
27228
27281
  app.post("/auth/logout", async (req, reply) => {
@@ -29637,7 +29690,10 @@ var createProjectBodySchema = {
29637
29690
  type: "object",
29638
29691
  required: ["host", "owner", "name", "cloneUrl", "branch"],
29639
29692
  properties: {
29640
- host: { type: "string", enum: ["github", "bitbucket", "gitlab"] },
29693
+ // DEV-875: `local` = a bare repository on the CLI's machine, reported
29694
+ // with its filesystem path as `cloneUrl`. The HUB records it like any
29695
+ // other location — it has no git access to either kind.
29696
+ host: { type: "string", enum: ["github", "bitbucket", "gitlab", "local"] },
29641
29697
  owner: { type: "string", minLength: 1 },
29642
29698
  name: { type: "string", minLength: 1 },
29643
29699
  cloneUrl: { type: "string", minLength: 1 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-partner/codepipe-hub",
3
- "version": "0.14.1-dev.390.g897b6f57",
3
+ "version": "0.14.1-dev.392.g2ee041d5",
4
4
  "description": "The CodePipe hub, packaged for one machine: the CLI installs it on demand for the local profile.",
5
5
  "type": "module",
6
6
  "main": "./hub/dist/index.js",