@staff0rd/assist 0.953.0 → 0.954.0
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/README.md +3 -3
- package/dist/commands/sessions/web/bundle.js +446 -446
- package/dist/index.js +614 -538
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.954.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -3755,29 +3755,19 @@ function getRepoLocation(req, res) {
|
|
|
3755
3755
|
respondJson(res, 200, resolveRepoLocation(origin, void 0, baseDir));
|
|
3756
3756
|
}
|
|
3757
3757
|
|
|
3758
|
+
// src/commands/backlog/updateStarred.ts
|
|
3759
|
+
import { eq as eq12 } from "drizzle-orm";
|
|
3760
|
+
async function updateStarred(orm, id, starred) {
|
|
3761
|
+
const [row3] = await orm.update(items).set({ starred }).where(eq12(items.id, id)).returning({ name: items.name });
|
|
3762
|
+
return row3?.name;
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3758
3765
|
// src/commands/backlog/web/applyCwdFromReq.ts
|
|
3759
3766
|
function applyCwdFromReq(req) {
|
|
3760
3767
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
3761
3768
|
setBacklogDir(url.searchParams.get("cwd") ?? void 0);
|
|
3762
3769
|
}
|
|
3763
3770
|
|
|
3764
|
-
// src/commands/backlog/deleteComment.ts
|
|
3765
|
-
import { and as and4, eq as eq12 } from "drizzle-orm";
|
|
3766
|
-
async function deleteComment(orm, itemId2, commentId) {
|
|
3767
|
-
const [row3] = await orm.select({ type: comments.type }).from(comments).where(and4(eq12(comments.id, commentId), eq12(comments.itemId, itemId2)));
|
|
3768
|
-
if (!row3) return "not-found";
|
|
3769
|
-
if (row3.type === "summary") return "is-summary";
|
|
3770
|
-
await orm.delete(comments).where(and4(eq12(comments.id, commentId), eq12(comments.itemId, itemId2)));
|
|
3771
|
-
return "deleted";
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
// src/commands/backlog/updateStarred.ts
|
|
3775
|
-
import { eq as eq13 } from "drizzle-orm";
|
|
3776
|
-
async function updateStarred(orm, id, starred) {
|
|
3777
|
-
const [row3] = await orm.update(items).set({ starred }).where(eq13(items.id, id)).returning({ name: items.name });
|
|
3778
|
-
return row3?.name;
|
|
3779
|
-
}
|
|
3780
|
-
|
|
3781
3771
|
// src/commands/sessions/shared/lookupSessionsById.ts
|
|
3782
3772
|
import * as path8 from "path";
|
|
3783
3773
|
|
|
@@ -4518,212 +4508,6 @@ async function patchItemStar(req, res, id) {
|
|
|
4518
4508
|
respondJson(res, 200, updated2);
|
|
4519
4509
|
}
|
|
4520
4510
|
|
|
4521
|
-
// src/commands/backlog/web/deleteItemComment.ts
|
|
4522
|
-
async function deleteItemComment(res, itemId2, commentId) {
|
|
4523
|
-
const result = await findItemOr404(res, itemId2);
|
|
4524
|
-
if (!result) return;
|
|
4525
|
-
const outcome = await deleteComment(result.orm, itemId2, commentId);
|
|
4526
|
-
if (outcome === "not-found") {
|
|
4527
|
-
respondJson(res, 404, {
|
|
4528
|
-
error: `Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
4529
|
-
});
|
|
4530
|
-
return;
|
|
4531
|
-
}
|
|
4532
|
-
if (outcome === "is-summary") {
|
|
4533
|
-
respondJson(res, 400, {
|
|
4534
|
-
error: `Comment #${commentId} is a phase summary and cannot be deleted.`
|
|
4535
|
-
});
|
|
4536
|
-
return;
|
|
4537
|
-
}
|
|
4538
|
-
respondJson(res, 200, await loadItem(result.orm, itemId2));
|
|
4539
|
-
}
|
|
4540
|
-
|
|
4541
|
-
// src/commands/backlog/appendComment.ts
|
|
4542
|
-
import { sql as sql15 } from "drizzle-orm";
|
|
4543
|
-
async function appendComment(orm, itemId2, text18, opts = {}) {
|
|
4544
|
-
await orm.insert(comments).values({
|
|
4545
|
-
itemId: itemId2,
|
|
4546
|
-
idx: sql15`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
|
|
4547
|
-
text: text18,
|
|
4548
|
-
phase: opts.phase ?? null,
|
|
4549
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4550
|
-
type: opts.type ?? "comment"
|
|
4551
|
-
});
|
|
4552
|
-
}
|
|
4553
|
-
|
|
4554
|
-
// src/commands/backlog/writeSignal.ts
|
|
4555
|
-
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
4556
|
-
import { homedir as homedir9 } from "os";
|
|
4557
|
-
import { dirname as dirname7, join as join17 } from "path";
|
|
4558
|
-
import chalk8 from "chalk";
|
|
4559
|
-
|
|
4560
|
-
// src/commands/backlog/recordSignalOwner.ts
|
|
4561
|
-
import {
|
|
4562
|
-
existsSync as existsSync13,
|
|
4563
|
-
mkdirSync as mkdirSync3,
|
|
4564
|
-
readFileSync as readFileSync6,
|
|
4565
|
-
rmSync as rmSync3,
|
|
4566
|
-
writeFileSync as writeFileSync3
|
|
4567
|
-
} from "fs";
|
|
4568
|
-
import { homedir as homedir8 } from "os";
|
|
4569
|
-
import { dirname as dirname6, join as join16 } from "path";
|
|
4570
|
-
function getOwnerPath(itemId2) {
|
|
4571
|
-
return join16(homedir8(), ".assist", "signals", `owner-${itemId2}.json`);
|
|
4572
|
-
}
|
|
4573
|
-
function recordSignalOwner(itemId2) {
|
|
4574
|
-
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
4575
|
-
if (!sessionId) return;
|
|
4576
|
-
const path108 = getOwnerPath(itemId2);
|
|
4577
|
-
mkdirSync3(dirname6(path108), { recursive: true });
|
|
4578
|
-
writeFileSync3(path108, JSON.stringify({ sessionId }));
|
|
4579
|
-
}
|
|
4580
|
-
function readSignalOwner(itemId2) {
|
|
4581
|
-
const path108 = getOwnerPath(itemId2);
|
|
4582
|
-
if (!existsSync13(path108)) return void 0;
|
|
4583
|
-
try {
|
|
4584
|
-
const parsed = JSON.parse(readFileSync6(path108, "utf8"));
|
|
4585
|
-
return parsed.sessionId;
|
|
4586
|
-
} catch {
|
|
4587
|
-
return void 0;
|
|
4588
|
-
}
|
|
4589
|
-
}
|
|
4590
|
-
function clearSignalOwner(itemId2) {
|
|
4591
|
-
const path108 = getOwnerPath(itemId2);
|
|
4592
|
-
try {
|
|
4593
|
-
rmSync3(path108);
|
|
4594
|
-
} catch {
|
|
4595
|
-
}
|
|
4596
|
-
}
|
|
4597
|
-
|
|
4598
|
-
// src/commands/backlog/writeSignal.ts
|
|
4599
|
-
function getSignalPath(sessionId = process.env.ASSIST_SESSION_ID) {
|
|
4600
|
-
if (!sessionId) return void 0;
|
|
4601
|
-
return join17(homedir9(), ".assist", "signals", `signal-${sessionId}.json`);
|
|
4602
|
-
}
|
|
4603
|
-
function resolveSignalTarget(event, data) {
|
|
4604
|
-
const caller = process.env.ASSIST_SESSION_ID;
|
|
4605
|
-
const itemId2 = typeof data?.itemId === "number" ? data.itemId : void 0;
|
|
4606
|
-
if (itemId2 === void 0) return caller;
|
|
4607
|
-
const owner = readSignalOwner(itemId2);
|
|
4608
|
-
if (owner) {
|
|
4609
|
-
if (caller && owner !== caller) {
|
|
4610
|
-
console.log(
|
|
4611
|
-
chalk8.dim(
|
|
4612
|
-
`Routing ${event} signal for item ${itemId2} to its run (${owner}), not this session (${caller}).`
|
|
4613
|
-
)
|
|
4614
|
-
);
|
|
4615
|
-
}
|
|
4616
|
-
return owner;
|
|
4617
|
-
}
|
|
4618
|
-
if (caller) {
|
|
4619
|
-
console.log(
|
|
4620
|
-
chalk8.yellow(
|
|
4621
|
-
`Not signalling ${event} for item ${itemId2}: no live run owns it, and this session (${caller}) does not either.`
|
|
4622
|
-
)
|
|
4623
|
-
);
|
|
4624
|
-
}
|
|
4625
|
-
return void 0;
|
|
4626
|
-
}
|
|
4627
|
-
function writeSignal(event, data) {
|
|
4628
|
-
const target = resolveSignalTarget(event, data);
|
|
4629
|
-
if (!target) return;
|
|
4630
|
-
const path108 = getSignalPath(target);
|
|
4631
|
-
if (!path108) return;
|
|
4632
|
-
const signal = { event, sessionId: target, ...data };
|
|
4633
|
-
mkdirSync4(dirname7(path108), { recursive: true });
|
|
4634
|
-
writeFileSync4(path108, JSON.stringify(signal));
|
|
4635
|
-
}
|
|
4636
|
-
|
|
4637
|
-
// src/commands/backlog/rewindItemToPhase.ts
|
|
4638
|
-
async function rewindItemToPhase(orm, item, phaseNumber, reason4) {
|
|
4639
|
-
const plan2 = resolveRewindPlan(item);
|
|
4640
|
-
if (phaseNumber < 1 || phaseNumber > plan2.length) {
|
|
4641
|
-
return {
|
|
4642
|
-
ok: false,
|
|
4643
|
-
error: `Phase ${phaseNumber} does not exist. Valid range: 1\u2013${plan2.length}.`
|
|
4644
|
-
};
|
|
4645
|
-
}
|
|
4646
|
-
const currentPhase = item.currentPhase ?? 1;
|
|
4647
|
-
if (phaseNumber >= currentPhase) {
|
|
4648
|
-
return {
|
|
4649
|
-
ok: false,
|
|
4650
|
-
error: `Phase ${phaseNumber} is not earlier than the current phase (${currentPhase}).`
|
|
4651
|
-
};
|
|
4652
|
-
}
|
|
4653
|
-
const phaseName = plan2[phaseNumber - 1].name;
|
|
4654
|
-
await appendComment(
|
|
4655
|
-
orm,
|
|
4656
|
-
item.id,
|
|
4657
|
-
`Rewound to phase ${phaseNumber} (${phaseName}): ${reason4}`,
|
|
4658
|
-
{ phase: phaseNumber }
|
|
4659
|
-
);
|
|
4660
|
-
await updateCurrentPhase(orm, item.id, phaseNumber);
|
|
4661
|
-
await updateStatus(orm, item.id, "in-progress");
|
|
4662
|
-
writeSignal("rewind", { itemId: item.id, targetPhase: phaseNumber - 1 });
|
|
4663
|
-
return { ok: true, phaseName };
|
|
4664
|
-
}
|
|
4665
|
-
|
|
4666
|
-
// src/commands/backlog/web/rewindItemPhase.ts
|
|
4667
|
-
async function rewindItemPhase(req, res, id) {
|
|
4668
|
-
const { phase, reason: reason4 } = await parseRewindBody(req);
|
|
4669
|
-
const result = await findItemOr404(res, id);
|
|
4670
|
-
if (!result) return;
|
|
4671
|
-
const { orm, item } = result;
|
|
4672
|
-
const outcome = await rewindItemToPhase(orm, item, phase, reason4);
|
|
4673
|
-
if (!outcome.ok) {
|
|
4674
|
-
respondJson(res, 400, { error: outcome.error });
|
|
4675
|
-
return;
|
|
4676
|
-
}
|
|
4677
|
-
respondJson(res, 200, await loadItem(orm, id));
|
|
4678
|
-
}
|
|
4679
|
-
|
|
4680
|
-
// src/commands/backlog/web/handleItemRoute.ts
|
|
4681
|
-
var itemId = (match, group = 1) => parseItemId(match[group]);
|
|
4682
|
-
var subId = (match, group) => Number.parseInt(match[group], 10);
|
|
4683
|
-
var routes = [
|
|
4684
|
-
{
|
|
4685
|
-
pattern: /^\/api\/items\/(a?\d+)\/rewind$/,
|
|
4686
|
-
method: "POST",
|
|
4687
|
-
run: (req, res, m) => rewindItemPhase(req, res, itemId(m))
|
|
4688
|
-
},
|
|
4689
|
-
{
|
|
4690
|
-
pattern: /^\/api\/items\/(a?\d+)\/star$/,
|
|
4691
|
-
method: "POST",
|
|
4692
|
-
run: (req, res, m) => patchItemStar(req, res, itemId(m))
|
|
4693
|
-
},
|
|
4694
|
-
{
|
|
4695
|
-
pattern: /^\/api\/items\/(a?\d+)\/comments\/(\d+)$/,
|
|
4696
|
-
method: "DELETE",
|
|
4697
|
-
run: (_req, res, m) => deleteItemComment(res, itemId(m), subId(m, 2))
|
|
4698
|
-
},
|
|
4699
|
-
{
|
|
4700
|
-
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
4701
|
-
method: "GET",
|
|
4702
|
-
run: (_req, res, m) => getItemById(res, itemId(m))
|
|
4703
|
-
},
|
|
4704
|
-
{
|
|
4705
|
-
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
4706
|
-
method: "PATCH",
|
|
4707
|
-
run: (req, res, m) => patchItemStatus(req, res, itemId(m))
|
|
4708
|
-
},
|
|
4709
|
-
{
|
|
4710
|
-
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
4711
|
-
method: "DELETE",
|
|
4712
|
-
run: (_req, res, m) => deleteItem2(res, itemId(m))
|
|
4713
|
-
}
|
|
4714
|
-
];
|
|
4715
|
-
async function handleItemRoute(req, res, pathname) {
|
|
4716
|
-
for (const route of routes) {
|
|
4717
|
-
const match = pathname.match(route.pattern);
|
|
4718
|
-
if (match && req.method === route.method) {
|
|
4719
|
-
applyCwdFromReq(req);
|
|
4720
|
-
await route.run(req, res, match);
|
|
4721
|
-
return true;
|
|
4722
|
-
}
|
|
4723
|
-
}
|
|
4724
|
-
return false;
|
|
4725
|
-
}
|
|
4726
|
-
|
|
4727
4511
|
// src/commands/sessions/web/readJsonBody.ts
|
|
4728
4512
|
async function readJsonBody(req) {
|
|
4729
4513
|
const chunks = [];
|
|
@@ -4920,20 +4704,20 @@ function includesCommittedChanges(cwd) {
|
|
|
4920
4704
|
}
|
|
4921
4705
|
|
|
4922
4706
|
// src/shared/db/findItemBySessionId.ts
|
|
4923
|
-
import { asc as asc6, eq as
|
|
4707
|
+
import { asc as asc6, eq as eq13 } from "drizzle-orm";
|
|
4924
4708
|
async function findItemBySessionId(db, claudeSessionId) {
|
|
4925
|
-
const rows = await db.select({ itemId: phaseUsage.itemId }).from(phaseUsage).where(
|
|
4709
|
+
const rows = await db.select({ itemId: phaseUsage.itemId }).from(phaseUsage).where(eq13(phaseUsage.claudeSessionId, claudeSessionId)).orderBy(asc6(phaseUsage.itemId), asc6(phaseUsage.phaseIdx)).limit(1);
|
|
4926
4710
|
return rows[0]?.itemId;
|
|
4927
4711
|
}
|
|
4928
4712
|
|
|
4929
4713
|
// src/shared/db/listCommitRefs.ts
|
|
4930
|
-
import { and as
|
|
4714
|
+
import { and as and4, asc as asc7, eq as eq14 } from "drizzle-orm";
|
|
4931
4715
|
async function listCommitRefs(db, itemId2) {
|
|
4932
4716
|
const rows = await db.select({
|
|
4933
4717
|
ref: itemGitRefs.ref,
|
|
4934
4718
|
title: itemGitRefs.title,
|
|
4935
4719
|
url: itemGitRefs.url
|
|
4936
|
-
}).from(itemGitRefs).where(
|
|
4720
|
+
}).from(itemGitRefs).where(and4(eq14(itemGitRefs.itemId, itemId2), eq14(itemGitRefs.kind, "commit"))).orderBy(asc7(itemGitRefs.createdAt), asc7(itemGitRefs.ref));
|
|
4937
4721
|
return rows.map((row3) => ({
|
|
4938
4722
|
sha: row3.ref,
|
|
4939
4723
|
...row3.title ? { title: row3.title } : {},
|
|
@@ -5182,12 +4966,12 @@ async function diffScopes(req, res) {
|
|
|
5182
4966
|
}
|
|
5183
4967
|
|
|
5184
4968
|
// src/commands/sessions/daemon/recordPromptJournal.ts
|
|
5185
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
5186
|
-
import { dirname as
|
|
4969
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
4970
|
+
import { dirname as dirname6 } from "path";
|
|
5187
4971
|
function recordPromptJournal(entry) {
|
|
5188
4972
|
const line = JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
|
|
5189
4973
|
try {
|
|
5190
|
-
|
|
4974
|
+
mkdirSync3(dirname6(daemonPaths.promptJournal), { recursive: true });
|
|
5191
4975
|
appendFileSync2(daemonPaths.promptJournal, `${line}
|
|
5192
4976
|
`);
|
|
5193
4977
|
} catch {
|
|
@@ -5360,11 +5144,11 @@ async function fileContent(req, res) {
|
|
|
5360
5144
|
import { basename as basename6 } from "path";
|
|
5361
5145
|
|
|
5362
5146
|
// src/commands/sessions/web/fromGithub/listClones.ts
|
|
5363
|
-
import { existsSync as
|
|
5364
|
-
import { join as
|
|
5147
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2 } from "fs";
|
|
5148
|
+
import { join as join16 } from "path";
|
|
5365
5149
|
function listClones(baseDir) {
|
|
5366
|
-
if (!
|
|
5367
|
-
return readdirSync2(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
5150
|
+
if (!existsSync13(baseDir)) return [];
|
|
5151
|
+
return readdirSync2(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join16(baseDir, entry.name)).filter((dir) => existsSync13(join16(dir, ".git"))).sort();
|
|
5368
5152
|
}
|
|
5369
5153
|
|
|
5370
5154
|
// src/commands/sessions/web/fromGithub/getClones.ts
|
|
@@ -5551,8 +5335,8 @@ function azureDevopsRepoUrl({ org, project, repo }) {
|
|
|
5551
5335
|
}
|
|
5552
5336
|
|
|
5553
5337
|
// src/commands/sessions/web/fromGithub/findAzureDevopsClone.ts
|
|
5554
|
-
import { existsSync as
|
|
5555
|
-
import { join as
|
|
5338
|
+
import { existsSync as existsSync14, readdirSync as readdirSync3 } from "fs";
|
|
5339
|
+
import { join as join17 } from "path";
|
|
5556
5340
|
|
|
5557
5341
|
// src/shared/azureDevops/parseAzureDevopsRemote.ts
|
|
5558
5342
|
var HTTPS_DEV = /^https?:\/\/(?:[^@/]+@)?dev\.azure\.com\/([^/]+)\/([^/]+)\/_git\/([^/]+)$/i;
|
|
@@ -5573,15 +5357,15 @@ function sameAzureDevopsRepo(a, b) {
|
|
|
5573
5357
|
|
|
5574
5358
|
// src/commands/sessions/web/fromGithub/findAzureDevopsClone.ts
|
|
5575
5359
|
function clonesRepo(dir, repo) {
|
|
5576
|
-
if (!
|
|
5360
|
+
if (!existsSync14(join17(dir, ".git"))) return false;
|
|
5577
5361
|
const url = remoteUrl(dir).url;
|
|
5578
5362
|
const remote = url ? parseAzureDevopsRemote(url) : null;
|
|
5579
5363
|
return remote !== null && sameAzureDevopsRepo(remote, repo);
|
|
5580
5364
|
}
|
|
5581
5365
|
function findAzureDevopsClone(repo, baseDir) {
|
|
5582
|
-
if (!
|
|
5583
|
-
const named =
|
|
5584
|
-
const others = readdirSync3(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) =>
|
|
5366
|
+
if (!existsSync14(baseDir)) return void 0;
|
|
5367
|
+
const named = join17(baseDir, repo.repo);
|
|
5368
|
+
const others = readdirSync3(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join17(baseDir, entry.name)).filter((dir) => dir !== named).sort();
|
|
5585
5369
|
return [named, ...others].find((dir) => clonesRepo(dir, repo));
|
|
5586
5370
|
}
|
|
5587
5371
|
|
|
@@ -6674,11 +6458,11 @@ var reviewConfigHelp = [
|
|
|
6674
6458
|
];
|
|
6675
6459
|
|
|
6676
6460
|
// src/commands/sync/printAutoConfirmHint.ts
|
|
6677
|
-
import
|
|
6461
|
+
import chalk8 from "chalk";
|
|
6678
6462
|
var autoConfirmHintCommand = "assist config set sync.autoConfirm true --global";
|
|
6679
6463
|
function printAutoConfirmHint() {
|
|
6680
6464
|
console.log(
|
|
6681
|
-
|
|
6465
|
+
chalk8.dim(
|
|
6682
6466
|
`Tip: run \`${autoConfirmHintCommand}\` to overwrite automatically next time`
|
|
6683
6467
|
)
|
|
6684
6468
|
);
|
|
@@ -7221,7 +7005,7 @@ function getHtml() {
|
|
|
7221
7005
|
}
|
|
7222
7006
|
|
|
7223
7007
|
// src/shared/resolveRunConfigs.ts
|
|
7224
|
-
import { dirname as
|
|
7008
|
+
import { dirname as dirname7, relative as relative2, resolve as resolve7 } from "path";
|
|
7225
7009
|
|
|
7226
7010
|
// src/shared/assertNoDuplicateRunNames.ts
|
|
7227
7011
|
function findDuplicateNames(configs) {
|
|
@@ -7243,14 +7027,14 @@ function assertNoDuplicateRunNames(configs) {
|
|
|
7243
7027
|
}
|
|
7244
7028
|
|
|
7245
7029
|
// src/shared/findLinkedConfigPath.ts
|
|
7246
|
-
import { existsSync as
|
|
7247
|
-
import { join as
|
|
7030
|
+
import { existsSync as existsSync15 } from "fs";
|
|
7031
|
+
import { join as join18, resolve as resolve5 } from "path";
|
|
7248
7032
|
function findLinkedConfigPath(linkPath, fromDir) {
|
|
7249
7033
|
const resolved = resolve5(fromDir, linkPath);
|
|
7250
|
-
const claudePath =
|
|
7251
|
-
if (
|
|
7252
|
-
const rootPath =
|
|
7253
|
-
if (
|
|
7034
|
+
const claudePath = join18(resolved, ".claude", "assist.yml");
|
|
7035
|
+
if (existsSync15(claudePath)) return claudePath;
|
|
7036
|
+
const rootPath = join18(resolved, "assist.yml");
|
|
7037
|
+
if (existsSync15(rootPath)) return rootPath;
|
|
7254
7038
|
throw new Error(`No assist.yml found in linked project: ${resolved}`);
|
|
7255
7039
|
}
|
|
7256
7040
|
|
|
@@ -7292,7 +7076,7 @@ function loadAndResolveLink(linkPath, configDir, ctx) {
|
|
|
7292
7076
|
const entries = loadLinkedEntries(configPath, ctx.visited);
|
|
7293
7077
|
const defaultCwd = relativeToRoot(ctx, resolve7(configDir, linkPath));
|
|
7294
7078
|
return setDefaultCwd(
|
|
7295
|
-
resolveRecursive(entries,
|
|
7079
|
+
resolveRecursive(entries, dirname7(configPath), ctx),
|
|
7296
7080
|
defaultCwd
|
|
7297
7081
|
);
|
|
7298
7082
|
}
|
|
@@ -7343,20 +7127,20 @@ function handleServerRuns(req, res) {
|
|
|
7343
7127
|
|
|
7344
7128
|
// src/commands/sessions/web/getReviewSynthesis.ts
|
|
7345
7129
|
import { execFile as execFile4 } from "child_process";
|
|
7346
|
-
import { readFileSync as
|
|
7347
|
-
import { homedir as
|
|
7348
|
-
import { basename as basename9, join as
|
|
7130
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
7131
|
+
import { homedir as homedir8 } from "os";
|
|
7132
|
+
import { basename as basename9, join as join20 } from "path";
|
|
7349
7133
|
import { promisify as promisify4 } from "util";
|
|
7350
7134
|
|
|
7351
7135
|
// src/commands/sessions/web/findSynthesisForBranch.ts
|
|
7352
|
-
import { existsSync as
|
|
7353
|
-
import { basename as basename8, dirname as
|
|
7136
|
+
import { existsSync as existsSync16, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
7137
|
+
import { basename as basename8, dirname as dirname8, join as join19 } from "path";
|
|
7354
7138
|
function findSynthesisForBranch(repoReviewsDir, branch2) {
|
|
7355
|
-
const branchKeyPath =
|
|
7356
|
-
const parent =
|
|
7139
|
+
const branchKeyPath = join19(repoReviewsDir, `${branch2}-`);
|
|
7140
|
+
const parent = dirname8(branchKeyPath);
|
|
7357
7141
|
const branchPrefix = basename8(branchKeyPath);
|
|
7358
|
-
if (!
|
|
7359
|
-
const synthesisFiles = readdirSync4(parent).filter((name) => name.startsWith(branchPrefix)).map((name) =>
|
|
7142
|
+
if (!existsSync16(parent)) return null;
|
|
7143
|
+
const synthesisFiles = readdirSync4(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join19(parent, name, "synthesis.md")).filter((path108) => existsSync16(path108)).map((path108) => ({ path: path108, mtime: statSync4(path108).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
7360
7144
|
return synthesisFiles[0]?.path ?? null;
|
|
7361
7145
|
}
|
|
7362
7146
|
|
|
@@ -7374,8 +7158,8 @@ async function resolveSynthesisPath(cwd) {
|
|
|
7374
7158
|
runGit2(cwd, ["rev-parse", "--show-toplevel"]),
|
|
7375
7159
|
runGit2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])
|
|
7376
7160
|
]);
|
|
7377
|
-
const repoReviewsDir =
|
|
7378
|
-
|
|
7161
|
+
const repoReviewsDir = join20(
|
|
7162
|
+
homedir8(),
|
|
7379
7163
|
".assist",
|
|
7380
7164
|
"reviews",
|
|
7381
7165
|
basename9(repoRoot2)
|
|
@@ -7391,7 +7175,7 @@ async function getReviewSynthesis(req, res) {
|
|
|
7391
7175
|
respondJson(res, 404, { error: "No synthesis found" });
|
|
7392
7176
|
return;
|
|
7393
7177
|
}
|
|
7394
|
-
respondJson(res, 200, { synthesis:
|
|
7178
|
+
respondJson(res, 200, { synthesis: readFileSync6(path108, "utf8") });
|
|
7395
7179
|
} catch {
|
|
7396
7180
|
respondJson(res, 404, { error: "No synthesis found" });
|
|
7397
7181
|
}
|
|
@@ -7683,6 +7467,416 @@ async function gitStatus(req, res) {
|
|
|
7683
7467
|
}
|
|
7684
7468
|
}
|
|
7685
7469
|
|
|
7470
|
+
// src/commands/backlog/deleteComment.ts
|
|
7471
|
+
import { and as and5, eq as eq15 } from "drizzle-orm";
|
|
7472
|
+
async function deleteComment(orm, itemId2, commentId) {
|
|
7473
|
+
const [row3] = await orm.select({ type: comments.type }).from(comments).where(and5(eq15(comments.id, commentId), eq15(comments.itemId, itemId2)));
|
|
7474
|
+
if (!row3) return "not-found";
|
|
7475
|
+
if (row3.type === "summary") return "is-summary";
|
|
7476
|
+
await orm.delete(comments).where(and5(eq15(comments.id, commentId), eq15(comments.itemId, itemId2)));
|
|
7477
|
+
return "deleted";
|
|
7478
|
+
}
|
|
7479
|
+
|
|
7480
|
+
// src/commands/backlog/web/deleteItemComment.ts
|
|
7481
|
+
async function deleteItemComment(res, itemId2, commentId) {
|
|
7482
|
+
const result = await findItemOr404(res, itemId2);
|
|
7483
|
+
if (!result) return;
|
|
7484
|
+
const outcome = await deleteComment(result.orm, itemId2, commentId);
|
|
7485
|
+
if (outcome === "not-found") {
|
|
7486
|
+
respondJson(res, 404, {
|
|
7487
|
+
error: `Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
7488
|
+
});
|
|
7489
|
+
return;
|
|
7490
|
+
}
|
|
7491
|
+
if (outcome === "is-summary") {
|
|
7492
|
+
respondJson(res, 400, {
|
|
7493
|
+
error: `Comment #${commentId} is a phase summary and cannot be deleted.`
|
|
7494
|
+
});
|
|
7495
|
+
return;
|
|
7496
|
+
}
|
|
7497
|
+
respondJson(res, 200, await loadItem(result.orm, itemId2));
|
|
7498
|
+
}
|
|
7499
|
+
|
|
7500
|
+
// src/commands/backlog/appendComment.ts
|
|
7501
|
+
import { sql as sql15 } from "drizzle-orm";
|
|
7502
|
+
async function appendComment(orm, itemId2, text18, opts = {}) {
|
|
7503
|
+
await orm.insert(comments).values({
|
|
7504
|
+
itemId: itemId2,
|
|
7505
|
+
idx: sql15`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
|
|
7506
|
+
text: text18,
|
|
7507
|
+
phase: opts.phase ?? null,
|
|
7508
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7509
|
+
type: opts.type ?? "comment"
|
|
7510
|
+
});
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7513
|
+
// src/commands/backlog/writeSignal.ts
|
|
7514
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
7515
|
+
import { homedir as homedir10 } from "os";
|
|
7516
|
+
import { dirname as dirname10, join as join22 } from "path";
|
|
7517
|
+
import chalk9 from "chalk";
|
|
7518
|
+
|
|
7519
|
+
// src/commands/backlog/recordSignalOwner.ts
|
|
7520
|
+
import {
|
|
7521
|
+
existsSync as existsSync17,
|
|
7522
|
+
mkdirSync as mkdirSync4,
|
|
7523
|
+
readFileSync as readFileSync7,
|
|
7524
|
+
rmSync as rmSync3,
|
|
7525
|
+
writeFileSync as writeFileSync3
|
|
7526
|
+
} from "fs";
|
|
7527
|
+
import { homedir as homedir9 } from "os";
|
|
7528
|
+
import { dirname as dirname9, join as join21 } from "path";
|
|
7529
|
+
function getOwnerPath(itemId2) {
|
|
7530
|
+
return join21(homedir9(), ".assist", "signals", `owner-${itemId2}.json`);
|
|
7531
|
+
}
|
|
7532
|
+
function recordSignalOwner(itemId2) {
|
|
7533
|
+
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
7534
|
+
if (!sessionId) return;
|
|
7535
|
+
const path108 = getOwnerPath(itemId2);
|
|
7536
|
+
mkdirSync4(dirname9(path108), { recursive: true });
|
|
7537
|
+
writeFileSync3(path108, JSON.stringify({ sessionId }));
|
|
7538
|
+
}
|
|
7539
|
+
function readSignalOwner(itemId2) {
|
|
7540
|
+
const path108 = getOwnerPath(itemId2);
|
|
7541
|
+
if (!existsSync17(path108)) return void 0;
|
|
7542
|
+
try {
|
|
7543
|
+
const parsed = JSON.parse(readFileSync7(path108, "utf8"));
|
|
7544
|
+
return parsed.sessionId;
|
|
7545
|
+
} catch {
|
|
7546
|
+
return void 0;
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
function clearSignalOwner(itemId2) {
|
|
7550
|
+
const path108 = getOwnerPath(itemId2);
|
|
7551
|
+
try {
|
|
7552
|
+
rmSync3(path108);
|
|
7553
|
+
} catch {
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
|
|
7557
|
+
// src/commands/backlog/writeSignal.ts
|
|
7558
|
+
function getSignalPath(sessionId = process.env.ASSIST_SESSION_ID) {
|
|
7559
|
+
if (!sessionId) return void 0;
|
|
7560
|
+
return join22(homedir10(), ".assist", "signals", `signal-${sessionId}.json`);
|
|
7561
|
+
}
|
|
7562
|
+
function resolveSignalTarget(event, data) {
|
|
7563
|
+
const caller = process.env.ASSIST_SESSION_ID;
|
|
7564
|
+
const itemId2 = typeof data?.itemId === "number" ? data.itemId : void 0;
|
|
7565
|
+
if (itemId2 === void 0) return caller;
|
|
7566
|
+
const owner = readSignalOwner(itemId2);
|
|
7567
|
+
if (owner) {
|
|
7568
|
+
if (caller && owner !== caller) {
|
|
7569
|
+
console.log(
|
|
7570
|
+
chalk9.dim(
|
|
7571
|
+
`Routing ${event} signal for item ${itemId2} to its run (${owner}), not this session (${caller}).`
|
|
7572
|
+
)
|
|
7573
|
+
);
|
|
7574
|
+
}
|
|
7575
|
+
return owner;
|
|
7576
|
+
}
|
|
7577
|
+
if (caller) {
|
|
7578
|
+
console.log(
|
|
7579
|
+
chalk9.yellow(
|
|
7580
|
+
`Not signalling ${event} for item ${itemId2}: no live run owns it, and this session (${caller}) does not either.`
|
|
7581
|
+
)
|
|
7582
|
+
);
|
|
7583
|
+
}
|
|
7584
|
+
return void 0;
|
|
7585
|
+
}
|
|
7586
|
+
function writeSignal(event, data) {
|
|
7587
|
+
const target = resolveSignalTarget(event, data);
|
|
7588
|
+
if (!target) return;
|
|
7589
|
+
const path108 = getSignalPath(target);
|
|
7590
|
+
if (!path108) return;
|
|
7591
|
+
const signal = { event, sessionId: target, ...data };
|
|
7592
|
+
mkdirSync5(dirname10(path108), { recursive: true });
|
|
7593
|
+
writeFileSync4(path108, JSON.stringify(signal));
|
|
7594
|
+
}
|
|
7595
|
+
|
|
7596
|
+
// src/commands/backlog/rewindItemToPhase.ts
|
|
7597
|
+
async function rewindItemToPhase(orm, item, phaseNumber, reason4) {
|
|
7598
|
+
const plan2 = resolveRewindPlan(item);
|
|
7599
|
+
if (phaseNumber < 1 || phaseNumber > plan2.length) {
|
|
7600
|
+
return {
|
|
7601
|
+
ok: false,
|
|
7602
|
+
error: `Phase ${phaseNumber} does not exist. Valid range: 1\u2013${plan2.length}.`
|
|
7603
|
+
};
|
|
7604
|
+
}
|
|
7605
|
+
const currentPhase = item.currentPhase ?? 1;
|
|
7606
|
+
if (phaseNumber >= currentPhase) {
|
|
7607
|
+
return {
|
|
7608
|
+
ok: false,
|
|
7609
|
+
error: `Phase ${phaseNumber} is not earlier than the current phase (${currentPhase}).`
|
|
7610
|
+
};
|
|
7611
|
+
}
|
|
7612
|
+
const phaseName = plan2[phaseNumber - 1].name;
|
|
7613
|
+
await appendComment(
|
|
7614
|
+
orm,
|
|
7615
|
+
item.id,
|
|
7616
|
+
`Rewound to phase ${phaseNumber} (${phaseName}): ${reason4}`,
|
|
7617
|
+
{ phase: phaseNumber }
|
|
7618
|
+
);
|
|
7619
|
+
await updateCurrentPhase(orm, item.id, phaseNumber);
|
|
7620
|
+
await updateStatus(orm, item.id, "in-progress");
|
|
7621
|
+
writeSignal("rewind", { itemId: item.id, targetPhase: phaseNumber - 1 });
|
|
7622
|
+
return { ok: true, phaseName };
|
|
7623
|
+
}
|
|
7624
|
+
|
|
7625
|
+
// src/commands/backlog/web/rewindItemPhase.ts
|
|
7626
|
+
async function rewindItemPhase(req, res, id) {
|
|
7627
|
+
const { phase, reason: reason4 } = await parseRewindBody(req);
|
|
7628
|
+
const result = await findItemOr404(res, id);
|
|
7629
|
+
if (!result) return;
|
|
7630
|
+
const { orm, item } = result;
|
|
7631
|
+
const outcome = await rewindItemToPhase(orm, item, phase, reason4);
|
|
7632
|
+
if (!outcome.ok) {
|
|
7633
|
+
respondJson(res, 400, { error: outcome.error });
|
|
7634
|
+
return;
|
|
7635
|
+
}
|
|
7636
|
+
respondJson(res, 200, await loadItem(orm, id));
|
|
7637
|
+
}
|
|
7638
|
+
|
|
7639
|
+
// src/commands/backlog/web/handleItemRoute.ts
|
|
7640
|
+
var itemId = (match, group = 1) => parseItemId(match[group]);
|
|
7641
|
+
var subId = (match, group) => Number.parseInt(match[group], 10);
|
|
7642
|
+
var routes = [
|
|
7643
|
+
{
|
|
7644
|
+
pattern: /^\/api\/items\/(a?\d+)\/rewind$/,
|
|
7645
|
+
method: "POST",
|
|
7646
|
+
run: (req, res, m) => rewindItemPhase(req, res, itemId(m))
|
|
7647
|
+
},
|
|
7648
|
+
{
|
|
7649
|
+
pattern: /^\/api\/items\/(a?\d+)\/star$/,
|
|
7650
|
+
method: "POST",
|
|
7651
|
+
run: (req, res, m) => patchItemStar(req, res, itemId(m))
|
|
7652
|
+
},
|
|
7653
|
+
{
|
|
7654
|
+
pattern: /^\/api\/items\/(a?\d+)\/comments\/(\d+)$/,
|
|
7655
|
+
method: "DELETE",
|
|
7656
|
+
run: (_req, res, m) => deleteItemComment(res, itemId(m), subId(m, 2))
|
|
7657
|
+
},
|
|
7658
|
+
{
|
|
7659
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
7660
|
+
method: "GET",
|
|
7661
|
+
run: (_req, res, m) => getItemById(res, itemId(m))
|
|
7662
|
+
},
|
|
7663
|
+
{
|
|
7664
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
7665
|
+
method: "PATCH",
|
|
7666
|
+
run: (req, res, m) => patchItemStatus(req, res, itemId(m))
|
|
7667
|
+
},
|
|
7668
|
+
{
|
|
7669
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
7670
|
+
method: "DELETE",
|
|
7671
|
+
run: (_req, res, m) => deleteItem2(res, itemId(m))
|
|
7672
|
+
}
|
|
7673
|
+
];
|
|
7674
|
+
async function handleItemRoute(req, res, pathname) {
|
|
7675
|
+
for (const route of routes) {
|
|
7676
|
+
const match = pathname.match(route.pattern);
|
|
7677
|
+
if (match && req.method === route.method) {
|
|
7678
|
+
applyCwdFromReq(req);
|
|
7679
|
+
await route.run(req, res, match);
|
|
7680
|
+
return true;
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
return false;
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7686
|
+
// src/shared/db/dismissInboxMessage.ts
|
|
7687
|
+
import { and as and6, eq as eq16, isNull } from "drizzle-orm";
|
|
7688
|
+
async function dismissInboxMessage(orm, source, messageId) {
|
|
7689
|
+
const dismissed = await orm.update(inboxMessages).set({ dismissedAt: /* @__PURE__ */ new Date() }).where(
|
|
7690
|
+
and6(
|
|
7691
|
+
eq16(inboxMessages.source, source),
|
|
7692
|
+
eq16(inboxMessages.messageId, messageId),
|
|
7693
|
+
isNull(inboxMessages.dismissedAt)
|
|
7694
|
+
)
|
|
7695
|
+
).returning({ messageId: inboxMessages.messageId });
|
|
7696
|
+
return dismissed.length > 0;
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7699
|
+
// src/commands/sessions/web/inbox/inboxMessageKey.ts
|
|
7700
|
+
function inboxMessageKey(source, messageId) {
|
|
7701
|
+
return `${source}:${messageId}`;
|
|
7702
|
+
}
|
|
7703
|
+
function parseInboxMessageKey(key) {
|
|
7704
|
+
const separator = key.indexOf(":");
|
|
7705
|
+
if (separator <= 0 || separator === key.length - 1) return void 0;
|
|
7706
|
+
return {
|
|
7707
|
+
source: key.slice(0, separator),
|
|
7708
|
+
messageId: key.slice(separator + 1)
|
|
7709
|
+
};
|
|
7710
|
+
}
|
|
7711
|
+
|
|
7712
|
+
// src/commands/sessions/web/inbox/handleInboxRoute.ts
|
|
7713
|
+
var DISMISS = /^\/api\/inbox\/([^/]+)\/dismiss$/;
|
|
7714
|
+
async function handleInboxRoute(req, res, pathname) {
|
|
7715
|
+
const match = DISMISS.exec(pathname);
|
|
7716
|
+
if (!match || req.method !== "POST") return false;
|
|
7717
|
+
const key = parseInboxMessageKey(decodeURIComponent(match[1]));
|
|
7718
|
+
if (!key) {
|
|
7719
|
+
respondJson(res, 400, { error: "Invalid inbox message id" });
|
|
7720
|
+
return true;
|
|
7721
|
+
}
|
|
7722
|
+
const dismissed = await dismissInboxMessage(
|
|
7723
|
+
await getDb(),
|
|
7724
|
+
key.source,
|
|
7725
|
+
key.messageId
|
|
7726
|
+
);
|
|
7727
|
+
respondJson(
|
|
7728
|
+
res,
|
|
7729
|
+
dismissed ? 200 : 404,
|
|
7730
|
+
dismissed ? { ok: true } : { error: "Not found" }
|
|
7731
|
+
);
|
|
7732
|
+
return true;
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
// src/commands/sessions/shared/loadLinkSpecs.ts
|
|
7736
|
+
function loadLinkSpecs() {
|
|
7737
|
+
return loadConfig().sessions?.links ?? [];
|
|
7738
|
+
}
|
|
7739
|
+
function findLinkSpec(name) {
|
|
7740
|
+
return loadLinkSpecs().find((spec) => spec.name === name);
|
|
7741
|
+
}
|
|
7742
|
+
|
|
7743
|
+
// src/commands/sessions/shared/newTraceId.ts
|
|
7744
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
7745
|
+
var TRACE_HEADER = "x-assist-trace-id";
|
|
7746
|
+
function newTraceId() {
|
|
7747
|
+
return randomUUID2().slice(0, 8);
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
// src/commands/sessions/web/forwardToPeer.ts
|
|
7751
|
+
import {
|
|
7752
|
+
request as httpRequest
|
|
7753
|
+
} from "http";
|
|
7754
|
+
import { request as httpsRequest } from "https";
|
|
7755
|
+
|
|
7756
|
+
// src/commands/sessions/web/boundUpstream.ts
|
|
7757
|
+
function boundUpstream(upstream, res, timeoutMs) {
|
|
7758
|
+
upstream.setTimeout(
|
|
7759
|
+
timeoutMs,
|
|
7760
|
+
() => upstream.destroy(new Error(`no response after ${timeoutMs}ms`))
|
|
7761
|
+
);
|
|
7762
|
+
res.on("close", () => {
|
|
7763
|
+
if (!res.writableFinished) upstream.destroy();
|
|
7764
|
+
});
|
|
7765
|
+
}
|
|
7766
|
+
|
|
7767
|
+
// src/commands/sessions/web/peerTimeoutMs.ts
|
|
7768
|
+
var peerTimeoutMs = 2e4;
|
|
7769
|
+
|
|
7770
|
+
// src/commands/sessions/web/peerTimeoutFor.ts
|
|
7771
|
+
var ASSIST_UPDATE_DURATION_MS = 6e5;
|
|
7772
|
+
function peerTimeoutFor(pathname) {
|
|
7773
|
+
return pathname === "/api/self-update" ? ASSIST_UPDATE_DURATION_MS : peerTimeoutMs;
|
|
7774
|
+
}
|
|
7775
|
+
|
|
7776
|
+
// src/commands/sessions/web/isQuietPoll.ts
|
|
7777
|
+
var POLLED_PATHS = /* @__PURE__ */ new Set(["/api/git-status", "/api/updates"]);
|
|
7778
|
+
function isQuietPoll(pathname, status2) {
|
|
7779
|
+
return POLLED_PATHS.has(pathname) && status2 >= 200 && status2 < 300;
|
|
7780
|
+
}
|
|
7781
|
+
|
|
7782
|
+
// src/commands/sessions/web/relayPeerResponse.ts
|
|
7783
|
+
function relayPeerResponse(peer, res, pathname, log2, done2) {
|
|
7784
|
+
const status2 = peer.statusCode ?? 502;
|
|
7785
|
+
if (!isQuietPoll(pathname, status2)) log2(String(status2));
|
|
7786
|
+
res.writeHead(status2, peer.headers);
|
|
7787
|
+
peer.pipe(res);
|
|
7788
|
+
peer.on("end", done2);
|
|
7789
|
+
peer.on("error", done2);
|
|
7790
|
+
}
|
|
7791
|
+
|
|
7792
|
+
// src/commands/sessions/web/forwardToPeer.ts
|
|
7793
|
+
var LINKED_FROM_HEADER = "x-assist-linked-from";
|
|
7794
|
+
function forwardToPeer(req, res, node, url) {
|
|
7795
|
+
const started = Date.now();
|
|
7796
|
+
const traceId = newTraceId();
|
|
7797
|
+
const label = `link ${node} http: ${req.method} ${url.pathname} trace=${traceId}`;
|
|
7798
|
+
const log2 = (outcome) => console.log(`${label} -> ${outcome} (${Date.now() - started}ms)`);
|
|
7799
|
+
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
7800
|
+
return new Promise((resolve31) => {
|
|
7801
|
+
const upstream = send(
|
|
7802
|
+
url,
|
|
7803
|
+
{
|
|
7804
|
+
method: req.method,
|
|
7805
|
+
headers: {
|
|
7806
|
+
...req.headers,
|
|
7807
|
+
host: url.host,
|
|
7808
|
+
[LINKED_FROM_HEADER]: resolveNodeName(),
|
|
7809
|
+
[TRACE_HEADER]: traceId
|
|
7810
|
+
}
|
|
7811
|
+
},
|
|
7812
|
+
(peer) => relayPeerResponse(peer, res, url.pathname, log2, resolve31)
|
|
7813
|
+
);
|
|
7814
|
+
boundUpstream(upstream, res, peerTimeoutFor(url.pathname));
|
|
7815
|
+
upstream.on("error", (error) => {
|
|
7816
|
+
log2(`failed: ${error.message}`);
|
|
7817
|
+
if (res.headersSent) res.destroy();
|
|
7818
|
+
else
|
|
7819
|
+
respondJson(res, 502, {
|
|
7820
|
+
error: `${node} unreachable: ${error.message}`
|
|
7821
|
+
});
|
|
7822
|
+
resolve31();
|
|
7823
|
+
});
|
|
7824
|
+
req.pipe(upstream);
|
|
7825
|
+
});
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
// src/commands/sessions/web/proxyToNode.ts
|
|
7829
|
+
var LINKED_FROM_HEADER2 = "x-assist-linked-from";
|
|
7830
|
+
function arrivedOverLink(req) {
|
|
7831
|
+
return Boolean(req.headers[LINKED_FROM_HEADER2]);
|
|
7832
|
+
}
|
|
7833
|
+
function proxyTarget(req) {
|
|
7834
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
7835
|
+
const node = url.searchParams.get("node");
|
|
7836
|
+
if (!url.pathname.startsWith("/api/") || !node) return { kind: "local" };
|
|
7837
|
+
if (arrivedOverLink(req) || node === resolveNodeName())
|
|
7838
|
+
return { kind: "local" };
|
|
7839
|
+
const link3 = findLinkSpec(node);
|
|
7840
|
+
if (!link3) return { kind: "unknown", node };
|
|
7841
|
+
url.searchParams.delete("node");
|
|
7842
|
+
return {
|
|
7843
|
+
kind: "link",
|
|
7844
|
+
node,
|
|
7845
|
+
url: new URL(`${url.pathname}${url.search}`, link3.url)
|
|
7846
|
+
};
|
|
7847
|
+
}
|
|
7848
|
+
function logLinkedRequest(req, res) {
|
|
7849
|
+
const started = Date.now();
|
|
7850
|
+
const from = req.headers[LINKED_FROM_HEADER2];
|
|
7851
|
+
const trace = req.headers[TRACE_HEADER] ?? "none";
|
|
7852
|
+
const path108 = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
7853
|
+
res.once("finish", () => {
|
|
7854
|
+
if (isQuietPoll(path108, res.statusCode)) return;
|
|
7855
|
+
console.log(
|
|
7856
|
+
`link-from ${from} http: ${req.method} ${path108} trace=${trace} -> ${res.statusCode} (${Date.now() - started}ms)`
|
|
7857
|
+
);
|
|
7858
|
+
});
|
|
7859
|
+
}
|
|
7860
|
+
async function proxyToNode(req, res) {
|
|
7861
|
+
if (arrivedOverLink(req)) logLinkedRequest(req, res);
|
|
7862
|
+
const target = proxyTarget(req);
|
|
7863
|
+
if (target.kind === "local") return false;
|
|
7864
|
+
if (target.kind === "unknown") {
|
|
7865
|
+
console.log(
|
|
7866
|
+
`link ${target.node} http: ${req.method} ${req.url} -> no such link`
|
|
7867
|
+
);
|
|
7868
|
+
respondJson(res, 404, { error: `No link to node ${target.node}` });
|
|
7869
|
+
return true;
|
|
7870
|
+
}
|
|
7871
|
+
await forwardToPeer(req, res, target.node, target.url);
|
|
7872
|
+
return true;
|
|
7873
|
+
}
|
|
7874
|
+
|
|
7875
|
+
// src/commands/sessions/web/handleDynamicRoute.ts
|
|
7876
|
+
async function handleDynamicRoute(req, res, pathname) {
|
|
7877
|
+
return await proxyToNode(req, res) || await handleInboxRoute(req, res, pathname) || handleItemRoute(req, res, pathname);
|
|
7878
|
+
}
|
|
7879
|
+
|
|
7686
7880
|
// src/commands/sessions/web/harnessCapabilities.ts
|
|
7687
7881
|
function harnessCapabilities(_req, res) {
|
|
7688
7882
|
const config = loadConfig().harness;
|
|
@@ -7781,6 +7975,33 @@ async function historySearch(req, res) {
|
|
|
7781
7975
|
respondJson(res, 200, { sessions: reply2.sessions ?? [] });
|
|
7782
7976
|
}
|
|
7783
7977
|
|
|
7978
|
+
// src/shared/db/listInboxMessages.ts
|
|
7979
|
+
import { desc as desc2, isNull as isNull2, sql as sql16 } from "drizzle-orm";
|
|
7980
|
+
function listInboxMessages(orm) {
|
|
7981
|
+
return orm.select().from(inboxMessages).where(isNull2(inboxMessages.dismissedAt)).orderBy(
|
|
7982
|
+
desc2(
|
|
7983
|
+
sql16`coalesce(${inboxMessages.receivedAt}, ${inboxMessages.flaggedAt})`
|
|
7984
|
+
)
|
|
7985
|
+
);
|
|
7986
|
+
}
|
|
7987
|
+
|
|
7988
|
+
// src/commands/sessions/web/inbox/listInbox.ts
|
|
7989
|
+
async function listInbox(_req, res) {
|
|
7990
|
+
const rows = await listInboxMessages(await getDb());
|
|
7991
|
+
respondJson(
|
|
7992
|
+
res,
|
|
7993
|
+
200,
|
|
7994
|
+
rows.map((row3) => ({
|
|
7995
|
+
id: inboxMessageKey(row3.source, row3.messageId),
|
|
7996
|
+
source: row3.source,
|
|
7997
|
+
sender: row3.sender,
|
|
7998
|
+
summary: row3.summary,
|
|
7999
|
+
link: row3.link,
|
|
8000
|
+
receivedAt: (row3.receivedAt ?? row3.flaggedAt).toISOString()
|
|
8001
|
+
}))
|
|
8002
|
+
);
|
|
8003
|
+
}
|
|
8004
|
+
|
|
7784
8005
|
// src/shared/hotkeys/resolveHotkeys.ts
|
|
7785
8006
|
function resolveHotkeys(overrides) {
|
|
7786
8007
|
const bindings = { ...defaultHotkeys };
|
|
@@ -8895,22 +9116,22 @@ function listScopedRules(req, res) {
|
|
|
8895
9116
|
}
|
|
8896
9117
|
|
|
8897
9118
|
// src/shared/db/countUsagePeaks.ts
|
|
8898
|
-
import { count, eq as
|
|
9119
|
+
import { count, eq as eq17 } from "drizzle-orm";
|
|
8899
9120
|
async function countUsagePeaks(db, window) {
|
|
8900
|
-
const [row3] = await db.select({ value: count() }).from(usagePeaks).where(window ?
|
|
9121
|
+
const [row3] = await db.select({ value: count() }).from(usagePeaks).where(window ? eq17(usagePeaks.window, window) : void 0);
|
|
8901
9122
|
return row3?.value ?? 0;
|
|
8902
9123
|
}
|
|
8903
9124
|
|
|
8904
9125
|
// src/shared/db/listUsagePeaks.ts
|
|
8905
|
-
import { and as
|
|
9126
|
+
import { and as and7, desc as desc3, eq as eq18, getTableColumns } from "drizzle-orm";
|
|
8906
9127
|
|
|
8907
9128
|
// src/shared/db/avgContextPerCycle.ts
|
|
8908
|
-
import { count as count2, sql as
|
|
9129
|
+
import { count as count2, sql as sql17 } from "drizzle-orm";
|
|
8909
9130
|
function avgContextPerCycle(db) {
|
|
8910
9131
|
return db.select({
|
|
8911
9132
|
window: phaseCycleContext.window,
|
|
8912
9133
|
resetsAt: phaseCycleContext.resetsAt,
|
|
8913
|
-
avgContextPct:
|
|
9134
|
+
avgContextPct: sql17`cast(avg(${phaseCycleContext.peakContextPct}) as double precision)`.as(
|
|
8914
9135
|
"avg_context_pct"
|
|
8915
9136
|
),
|
|
8916
9137
|
phaseCount: count2().as("phase_count")
|
|
@@ -8926,15 +9147,15 @@ async function listUsagePeaks(db, options2) {
|
|
|
8926
9147
|
phaseCount: avgContext.phaseCount
|
|
8927
9148
|
}).from(usagePeaks).leftJoin(
|
|
8928
9149
|
avgContext,
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
9150
|
+
and7(
|
|
9151
|
+
eq18(usagePeaks.window, avgContext.window),
|
|
9152
|
+
eq18(usagePeaks.resetsAt, avgContext.resetsAt)
|
|
8932
9153
|
)
|
|
8933
|
-
).where(options2?.window ?
|
|
8934
|
-
|
|
9154
|
+
).where(options2?.window ? eq18(usagePeaks.window, options2.window) : void 0).orderBy(
|
|
9155
|
+
desc3(usagePeaks.resetsAt),
|
|
8935
9156
|
usagePeaks.window,
|
|
8936
|
-
|
|
8937
|
-
|
|
9157
|
+
desc3(usagePeaks.createdAt),
|
|
9158
|
+
desc3(usagePeaks.segment)
|
|
8938
9159
|
);
|
|
8939
9160
|
if (options2?.limit !== void 0) {
|
|
8940
9161
|
return query.limit(options2.limit).offset(options2.offset ?? 0);
|
|
@@ -8999,26 +9220,26 @@ function listUsageHistory(req, res) {
|
|
|
8999
9220
|
}
|
|
9000
9221
|
|
|
9001
9222
|
// src/shared/db/countItemUsageByOrigin.ts
|
|
9002
|
-
import { asc as asc8, desc as
|
|
9223
|
+
import { asc as asc8, desc as desc4, eq as eq19, sql as sql18 } from "drizzle-orm";
|
|
9003
9224
|
async function countItemUsageByOrigin(db) {
|
|
9004
|
-
const count10 =
|
|
9005
|
-
const rows = await db.select({ origin: items.origin, count: count10 }).from(items).innerJoin(phaseUsage,
|
|
9225
|
+
const count10 = sql18`count(distinct ${phaseUsage.itemId})::int`;
|
|
9226
|
+
const rows = await db.select({ origin: items.origin, count: count10 }).from(items).innerJoin(phaseUsage, eq19(phaseUsage.itemId, items.id)).groupBy(items.origin).orderBy(desc4(count10), asc8(items.origin));
|
|
9006
9227
|
return rows.map((row3) => ({ origin: row3.origin, count: Number(row3.count) }));
|
|
9007
9228
|
}
|
|
9008
9229
|
|
|
9009
9230
|
// src/shared/db/itemUsageWhere.ts
|
|
9010
|
-
import { and as
|
|
9231
|
+
import { and as and8, eq as eq20, notInArray } from "drizzle-orm";
|
|
9011
9232
|
function parseItemUsageStatus(value) {
|
|
9012
9233
|
return value === "done" || value === "running" ? value : void 0;
|
|
9013
9234
|
}
|
|
9014
9235
|
function statusClause(status2) {
|
|
9015
|
-
if (status2 === "done") return
|
|
9236
|
+
if (status2 === "done") return eq20(items.status, "done");
|
|
9016
9237
|
if (status2 === "running") return notInArray(items.status, ["done", "wontdo"]);
|
|
9017
9238
|
return void 0;
|
|
9018
9239
|
}
|
|
9019
9240
|
function itemUsageWhere(filter) {
|
|
9020
|
-
return
|
|
9021
|
-
filter?.origin ?
|
|
9241
|
+
return and8(
|
|
9242
|
+
filter?.origin ? eq20(items.origin, filter.origin) : void 0,
|
|
9022
9243
|
statusClause(filter?.status)
|
|
9023
9244
|
);
|
|
9024
9245
|
}
|
|
@@ -9043,23 +9264,23 @@ function parseItemUsageSort(field, direction) {
|
|
|
9043
9264
|
}
|
|
9044
9265
|
|
|
9045
9266
|
// src/shared/db/itemUsageStats.ts
|
|
9046
|
-
import { eq as
|
|
9267
|
+
import { eq as eq21, sql as sql19 } from "drizzle-orm";
|
|
9047
9268
|
function median(value) {
|
|
9048
|
-
return
|
|
9269
|
+
return sql19`coalesce(percentile_cont(0.5) within group (order by (${value})::double precision), 0)`;
|
|
9049
9270
|
}
|
|
9050
9271
|
async function itemUsageStats(db, options2) {
|
|
9051
9272
|
const totals = phaseUsageTotals(db);
|
|
9052
9273
|
const planned = planPhaseCounts(db);
|
|
9053
9274
|
const [row3] = await db.select({
|
|
9054
|
-
itemCount:
|
|
9055
|
-
doneCount:
|
|
9056
|
-
repoCount:
|
|
9275
|
+
itemCount: sql19`count(*)::int`,
|
|
9276
|
+
doneCount: sql19`count(*) filter (where ${items.status} = 'done')::int`,
|
|
9277
|
+
repoCount: sql19`count(distinct ${items.origin})::int`,
|
|
9057
9278
|
medianPhases: median(
|
|
9058
|
-
|
|
9279
|
+
sql19`coalesce(${planned.count}, ${totals.recordedPhases})`
|
|
9059
9280
|
),
|
|
9060
9281
|
medianActiveMs: median(totals.activeMs),
|
|
9061
|
-
medianTokens: median(
|
|
9062
|
-
}).from(items).innerJoin(totals,
|
|
9282
|
+
medianTokens: median(sql19`${totals.tokensUp} + ${totals.tokensDown}`)
|
|
9283
|
+
}).from(items).innerJoin(totals, eq21(totals.itemId, items.id)).leftJoin(planned, eq21(planned.itemId, items.id)).where(itemUsageWhere(options2));
|
|
9063
9284
|
return {
|
|
9064
9285
|
itemCount: Number(row3?.itemCount ?? 0),
|
|
9065
9286
|
doneCount: Number(row3?.doneCount ?? 0),
|
|
@@ -9071,30 +9292,30 @@ async function itemUsageStats(db, options2) {
|
|
|
9071
9292
|
}
|
|
9072
9293
|
|
|
9073
9294
|
// src/shared/db/listItemUsageSummaries.ts
|
|
9074
|
-
import { eq as
|
|
9295
|
+
import { eq as eq22 } from "drizzle-orm";
|
|
9075
9296
|
|
|
9076
9297
|
// src/shared/db/itemUsageOrderBy.ts
|
|
9077
|
-
import { desc as
|
|
9298
|
+
import { desc as desc5, sql as sql20 } from "drizzle-orm";
|
|
9078
9299
|
function itemUsageOrderBy(joins, sort = defaultItemUsageSort) {
|
|
9079
9300
|
const { totals, planned, lastPhase } = joins;
|
|
9080
9301
|
const sortable = {
|
|
9081
|
-
phases:
|
|
9082
|
-
active:
|
|
9083
|
-
tokens:
|
|
9084
|
-
peakContext:
|
|
9085
|
-
lastPhase:
|
|
9302
|
+
phases: sql20`coalesce(${planned.count}, ${totals.recordedPhases})`,
|
|
9303
|
+
active: sql20`${totals.activeMs}`,
|
|
9304
|
+
tokens: sql20`${totals.tokensUp} + ${totals.tokensDown}`,
|
|
9305
|
+
peakContext: sql20`${totals.peakContextPct}`,
|
|
9306
|
+
lastPhase: sql20`${lastPhase.at}`
|
|
9086
9307
|
};
|
|
9087
|
-
const order = sort.direction === "asc" ?
|
|
9088
|
-
return [
|
|
9308
|
+
const order = sort.direction === "asc" ? sql20`asc` : sql20`desc`;
|
|
9309
|
+
return [sql20`${sortable[sort.field]} ${order} nulls last`, desc5(items.id)];
|
|
9089
9310
|
}
|
|
9090
9311
|
|
|
9091
9312
|
// src/shared/db/lastPhaseActivity.ts
|
|
9092
|
-
import { sql as
|
|
9313
|
+
import { sql as sql21 } from "drizzle-orm";
|
|
9093
9314
|
function lastPhaseActivity(db) {
|
|
9094
9315
|
return db.select({
|
|
9095
9316
|
itemId: phaseSessions.itemId,
|
|
9096
|
-
at:
|
|
9097
|
-
atIso:
|
|
9317
|
+
at: sql21`max(${phaseSessions.createdAt})`.as("last_phase_at"),
|
|
9318
|
+
atIso: sql21`to_char(max(${phaseSessions.createdAt}) at time zone 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as(
|
|
9098
9319
|
"last_phase_at_iso"
|
|
9099
9320
|
)
|
|
9100
9321
|
}).from(phaseSessions).groupBy(phaseSessions.itemId).as("last_phase_activity");
|
|
@@ -9132,7 +9353,7 @@ async function listItemUsageSummaries(db, options2) {
|
|
|
9132
9353
|
activeMs: totals.activeMs,
|
|
9133
9354
|
peakContextPct: totals.peakContextPct,
|
|
9134
9355
|
lastPhaseAt: lastPhase.atIso
|
|
9135
|
-
}).from(items).innerJoin(totals,
|
|
9356
|
+
}).from(items).innerJoin(totals, eq22(totals.itemId, items.id)).leftJoin(planned, eq22(planned.itemId, items.id)).leftJoin(lastPhase, eq22(lastPhase.itemId, items.id)).where(itemUsageWhere(options2)).orderBy(
|
|
9136
9357
|
...itemUsageOrderBy({ totals, planned, lastPhase }, options2?.sort)
|
|
9137
9358
|
);
|
|
9138
9359
|
const rows = options2?.limit === void 0 ? await query : await query.limit(options2.limit).offset(options2.offset ?? 0);
|
|
@@ -9236,146 +9457,6 @@ async function prList(req, res) {
|
|
|
9236
9457
|
respondJson(res, 200, { prs: await getOpenPrs(cwd) });
|
|
9237
9458
|
}
|
|
9238
9459
|
|
|
9239
|
-
// src/commands/sessions/shared/loadLinkSpecs.ts
|
|
9240
|
-
function loadLinkSpecs() {
|
|
9241
|
-
return loadConfig().sessions?.links ?? [];
|
|
9242
|
-
}
|
|
9243
|
-
function findLinkSpec(name) {
|
|
9244
|
-
return loadLinkSpecs().find((spec) => spec.name === name);
|
|
9245
|
-
}
|
|
9246
|
-
|
|
9247
|
-
// src/commands/sessions/shared/newTraceId.ts
|
|
9248
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
9249
|
-
var TRACE_HEADER = "x-assist-trace-id";
|
|
9250
|
-
function newTraceId() {
|
|
9251
|
-
return randomUUID2().slice(0, 8);
|
|
9252
|
-
}
|
|
9253
|
-
|
|
9254
|
-
// src/commands/sessions/web/forwardToPeer.ts
|
|
9255
|
-
import {
|
|
9256
|
-
request as httpRequest
|
|
9257
|
-
} from "http";
|
|
9258
|
-
import { request as httpsRequest } from "https";
|
|
9259
|
-
|
|
9260
|
-
// src/commands/sessions/web/boundUpstream.ts
|
|
9261
|
-
function boundUpstream(upstream, res, timeoutMs) {
|
|
9262
|
-
upstream.setTimeout(
|
|
9263
|
-
timeoutMs,
|
|
9264
|
-
() => upstream.destroy(new Error(`no response after ${timeoutMs}ms`))
|
|
9265
|
-
);
|
|
9266
|
-
res.on("close", () => {
|
|
9267
|
-
if (!res.writableFinished) upstream.destroy();
|
|
9268
|
-
});
|
|
9269
|
-
}
|
|
9270
|
-
|
|
9271
|
-
// src/commands/sessions/web/peerTimeoutMs.ts
|
|
9272
|
-
var peerTimeoutMs = 2e4;
|
|
9273
|
-
|
|
9274
|
-
// src/commands/sessions/web/peerTimeoutFor.ts
|
|
9275
|
-
var ASSIST_UPDATE_DURATION_MS = 6e5;
|
|
9276
|
-
function peerTimeoutFor(pathname) {
|
|
9277
|
-
return pathname === "/api/self-update" ? ASSIST_UPDATE_DURATION_MS : peerTimeoutMs;
|
|
9278
|
-
}
|
|
9279
|
-
|
|
9280
|
-
// src/commands/sessions/web/isQuietPoll.ts
|
|
9281
|
-
var POLLED_PATHS = /* @__PURE__ */ new Set(["/api/git-status", "/api/updates"]);
|
|
9282
|
-
function isQuietPoll(pathname, status2) {
|
|
9283
|
-
return POLLED_PATHS.has(pathname) && status2 >= 200 && status2 < 300;
|
|
9284
|
-
}
|
|
9285
|
-
|
|
9286
|
-
// src/commands/sessions/web/relayPeerResponse.ts
|
|
9287
|
-
function relayPeerResponse(peer, res, pathname, log2, done2) {
|
|
9288
|
-
const status2 = peer.statusCode ?? 502;
|
|
9289
|
-
if (!isQuietPoll(pathname, status2)) log2(String(status2));
|
|
9290
|
-
res.writeHead(status2, peer.headers);
|
|
9291
|
-
peer.pipe(res);
|
|
9292
|
-
peer.on("end", done2);
|
|
9293
|
-
peer.on("error", done2);
|
|
9294
|
-
}
|
|
9295
|
-
|
|
9296
|
-
// src/commands/sessions/web/forwardToPeer.ts
|
|
9297
|
-
var LINKED_FROM_HEADER = "x-assist-linked-from";
|
|
9298
|
-
function forwardToPeer(req, res, node, url) {
|
|
9299
|
-
const started = Date.now();
|
|
9300
|
-
const traceId = newTraceId();
|
|
9301
|
-
const label = `link ${node} http: ${req.method} ${url.pathname} trace=${traceId}`;
|
|
9302
|
-
const log2 = (outcome) => console.log(`${label} -> ${outcome} (${Date.now() - started}ms)`);
|
|
9303
|
-
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
9304
|
-
return new Promise((resolve31) => {
|
|
9305
|
-
const upstream = send(
|
|
9306
|
-
url,
|
|
9307
|
-
{
|
|
9308
|
-
method: req.method,
|
|
9309
|
-
headers: {
|
|
9310
|
-
...req.headers,
|
|
9311
|
-
host: url.host,
|
|
9312
|
-
[LINKED_FROM_HEADER]: resolveNodeName(),
|
|
9313
|
-
[TRACE_HEADER]: traceId
|
|
9314
|
-
}
|
|
9315
|
-
},
|
|
9316
|
-
(peer) => relayPeerResponse(peer, res, url.pathname, log2, resolve31)
|
|
9317
|
-
);
|
|
9318
|
-
boundUpstream(upstream, res, peerTimeoutFor(url.pathname));
|
|
9319
|
-
upstream.on("error", (error) => {
|
|
9320
|
-
log2(`failed: ${error.message}`);
|
|
9321
|
-
if (res.headersSent) res.destroy();
|
|
9322
|
-
else
|
|
9323
|
-
respondJson(res, 502, {
|
|
9324
|
-
error: `${node} unreachable: ${error.message}`
|
|
9325
|
-
});
|
|
9326
|
-
resolve31();
|
|
9327
|
-
});
|
|
9328
|
-
req.pipe(upstream);
|
|
9329
|
-
});
|
|
9330
|
-
}
|
|
9331
|
-
|
|
9332
|
-
// src/commands/sessions/web/proxyToNode.ts
|
|
9333
|
-
var LINKED_FROM_HEADER2 = "x-assist-linked-from";
|
|
9334
|
-
function arrivedOverLink(req) {
|
|
9335
|
-
return Boolean(req.headers[LINKED_FROM_HEADER2]);
|
|
9336
|
-
}
|
|
9337
|
-
function proxyTarget(req) {
|
|
9338
|
-
const url = new URL(req.url ?? "/", "http://localhost");
|
|
9339
|
-
const node = url.searchParams.get("node");
|
|
9340
|
-
if (!url.pathname.startsWith("/api/") || !node) return { kind: "local" };
|
|
9341
|
-
if (arrivedOverLink(req) || node === resolveNodeName())
|
|
9342
|
-
return { kind: "local" };
|
|
9343
|
-
const link3 = findLinkSpec(node);
|
|
9344
|
-
if (!link3) return { kind: "unknown", node };
|
|
9345
|
-
url.searchParams.delete("node");
|
|
9346
|
-
return {
|
|
9347
|
-
kind: "link",
|
|
9348
|
-
node,
|
|
9349
|
-
url: new URL(`${url.pathname}${url.search}`, link3.url)
|
|
9350
|
-
};
|
|
9351
|
-
}
|
|
9352
|
-
function logLinkedRequest(req, res) {
|
|
9353
|
-
const started = Date.now();
|
|
9354
|
-
const from = req.headers[LINKED_FROM_HEADER2];
|
|
9355
|
-
const trace = req.headers[TRACE_HEADER] ?? "none";
|
|
9356
|
-
const path108 = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
9357
|
-
res.once("finish", () => {
|
|
9358
|
-
if (isQuietPoll(path108, res.statusCode)) return;
|
|
9359
|
-
console.log(
|
|
9360
|
-
`link-from ${from} http: ${req.method} ${path108} trace=${trace} -> ${res.statusCode} (${Date.now() - started}ms)`
|
|
9361
|
-
);
|
|
9362
|
-
});
|
|
9363
|
-
}
|
|
9364
|
-
async function proxyToNode(req, res) {
|
|
9365
|
-
if (arrivedOverLink(req)) logLinkedRequest(req, res);
|
|
9366
|
-
const target = proxyTarget(req);
|
|
9367
|
-
if (target.kind === "local") return false;
|
|
9368
|
-
if (target.kind === "unknown") {
|
|
9369
|
-
console.log(
|
|
9370
|
-
`link ${target.node} http: ${req.method} ${req.url} -> no such link`
|
|
9371
|
-
);
|
|
9372
|
-
respondJson(res, 404, { error: `No link to node ${target.node}` });
|
|
9373
|
-
return true;
|
|
9374
|
-
}
|
|
9375
|
-
await forwardToPeer(req, res, target.node, target.url);
|
|
9376
|
-
return true;
|
|
9377
|
-
}
|
|
9378
|
-
|
|
9379
9460
|
// src/commands/sessions/web/previewImage.ts
|
|
9380
9461
|
import { createReadStream as createReadStream2 } from "fs";
|
|
9381
9462
|
import { stat as stat2 } from "fs/promises";
|
|
@@ -11059,10 +11140,10 @@ function currentRepoLabel(shared, origin) {
|
|
|
11059
11140
|
}
|
|
11060
11141
|
|
|
11061
11142
|
// src/shared/db/saveRepoConfig.ts
|
|
11062
|
-
import { eq as
|
|
11143
|
+
import { eq as eq23 } from "drizzle-orm";
|
|
11063
11144
|
async function saveRepoConfig(orm, repoKey, config) {
|
|
11064
11145
|
if (Object.keys(config).length === 0) {
|
|
11065
|
-
await orm.delete(repoConfigs).where(
|
|
11146
|
+
await orm.delete(repoConfigs).where(eq23(repoConfigs.repoKey, repoKey));
|
|
11066
11147
|
return;
|
|
11067
11148
|
}
|
|
11068
11149
|
await orm.insert(repoConfigs).values({ repoKey, config }).onConflictDoUpdate({
|
|
@@ -11935,6 +12016,7 @@ var routes2 = {
|
|
|
11935
12016
|
"GET /api/pr-list": prList,
|
|
11936
12017
|
"GET /api/next": nextItems,
|
|
11937
12018
|
"POST /api/next/pickup": nextPickup,
|
|
12019
|
+
"GET /api/inbox": listInbox,
|
|
11938
12020
|
"GET /api/news/items": listNewsItems,
|
|
11939
12021
|
"GET /api/news/nav": newsNavConfig,
|
|
11940
12022
|
"GET /api/releases/state": releasesState,
|
|
@@ -11954,7 +12036,7 @@ var routes2 = {
|
|
|
11954
12036
|
var handleRequest = createFallbackHandler(
|
|
11955
12037
|
routes2,
|
|
11956
12038
|
htmlHandler,
|
|
11957
|
-
|
|
12039
|
+
handleDynamicRoute
|
|
11958
12040
|
);
|
|
11959
12041
|
|
|
11960
12042
|
// src/commands/sessions/web/restartMenu/installRestartMenu.ts
|
|
@@ -15292,11 +15374,11 @@ function registerActivityCommands(cmd) {
|
|
|
15292
15374
|
|
|
15293
15375
|
// src/commands/backlog/associateGithub.ts
|
|
15294
15376
|
import chalk43 from "chalk";
|
|
15295
|
-
import { eq as
|
|
15377
|
+
import { eq as eq25 } from "drizzle-orm";
|
|
15296
15378
|
|
|
15297
15379
|
// src/commands/backlog/beginAssociation.ts
|
|
15298
15380
|
import chalk42 from "chalk";
|
|
15299
|
-
import { eq as
|
|
15381
|
+
import { eq as eq24 } from "drizzle-orm";
|
|
15300
15382
|
async function beginAssociation(id, options2, clearPatch, label) {
|
|
15301
15383
|
const found = await findOneItem(id);
|
|
15302
15384
|
if (!found) {
|
|
@@ -15306,7 +15388,7 @@ async function beginAssociation(id, options2, clearPatch, label) {
|
|
|
15306
15388
|
const { orm } = found;
|
|
15307
15389
|
const itemId2 = found.item.id;
|
|
15308
15390
|
if (options2.clear) {
|
|
15309
|
-
await orm.update(items).set(clearPatch).where(
|
|
15391
|
+
await orm.update(items).set(clearPatch).where(eq24(items.id, itemId2));
|
|
15310
15392
|
console.log(
|
|
15311
15393
|
chalk42.green(
|
|
15312
15394
|
`Cleared ${label} association on item ${formatItemId(itemId2)}.`
|
|
@@ -15374,7 +15456,7 @@ async function associateGithub(id, issue, options2) {
|
|
|
15374
15456
|
return;
|
|
15375
15457
|
}
|
|
15376
15458
|
const title = fetchGithubIssueTitle(normalized);
|
|
15377
|
-
await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(
|
|
15459
|
+
await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(eq25(items.id, itemId2));
|
|
15378
15460
|
console.log(
|
|
15379
15461
|
chalk43.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
|
|
15380
15462
|
title ? chalk43.dim(`(${title})`) : ""
|
|
@@ -15388,7 +15470,7 @@ function registerAssociateGithubCommand(cmd) {
|
|
|
15388
15470
|
|
|
15389
15471
|
// src/commands/backlog/associateJira.ts
|
|
15390
15472
|
import chalk45 from "chalk";
|
|
15391
|
-
import { eq as
|
|
15473
|
+
import { eq as eq26 } from "drizzle-orm";
|
|
15392
15474
|
|
|
15393
15475
|
// src/commands/jira/fetchIssue.ts
|
|
15394
15476
|
import { execSync as execSync17 } from "child_process";
|
|
@@ -15453,7 +15535,7 @@ async function associateJira(id, key, options2) {
|
|
|
15453
15535
|
const parsed = fetchIssue(normalized, "summary");
|
|
15454
15536
|
const fields = parsed?.fields;
|
|
15455
15537
|
const summary = fields?.summary;
|
|
15456
|
-
await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(
|
|
15538
|
+
await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(eq26(items.id, itemId2));
|
|
15457
15539
|
console.log(
|
|
15458
15540
|
chalk45.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
|
|
15459
15541
|
summary ? chalk45.dim(`(${summary})`) : ""
|
|
@@ -15838,15 +15920,15 @@ function isClaudeCode() {
|
|
|
15838
15920
|
}
|
|
15839
15921
|
|
|
15840
15922
|
// src/commands/backlog/insertPhaseAt.ts
|
|
15841
|
-
import { eq as
|
|
15923
|
+
import { eq as eq28 } from "drizzle-orm";
|
|
15842
15924
|
|
|
15843
15925
|
// src/commands/backlog/shiftPhasesUp.ts
|
|
15844
|
-
import { and as
|
|
15926
|
+
import { and as and9, desc as desc6, eq as eq27, gte } from "drizzle-orm";
|
|
15845
15927
|
async function shiftPhasesUp(db, itemId2, fromIdx) {
|
|
15846
|
-
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(
|
|
15928
|
+
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and9(eq27(planPhases.itemId, itemId2), gte(planPhases.idx, fromIdx))).orderBy(desc6(planPhases.idx));
|
|
15847
15929
|
for (const p of toShift) {
|
|
15848
|
-
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(
|
|
15849
|
-
await db.update(planPhases).set({ idx: p.idx + 1 }).where(
|
|
15930
|
+
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and9(eq27(planTasks.itemId, itemId2), eq27(planTasks.phaseIdx, p.idx)));
|
|
15931
|
+
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and9(eq27(planPhases.itemId, itemId2), eq27(planPhases.idx, p.idx)));
|
|
15850
15932
|
}
|
|
15851
15933
|
}
|
|
15852
15934
|
|
|
@@ -15859,7 +15941,7 @@ async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks,
|
|
|
15859
15941
|
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId: itemId2, phaseIdx, idx: i, task })));
|
|
15860
15942
|
}
|
|
15861
15943
|
if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
|
|
15862
|
-
await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(
|
|
15944
|
+
await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(eq28(items.id, itemId2));
|
|
15863
15945
|
}
|
|
15864
15946
|
});
|
|
15865
15947
|
}
|
|
@@ -16016,9 +16098,9 @@ import chalk56 from "chalk";
|
|
|
16016
16098
|
|
|
16017
16099
|
// src/commands/backlog/resolveInsertPosition.ts
|
|
16018
16100
|
import chalk55 from "chalk";
|
|
16019
|
-
import { count as count3, eq as
|
|
16101
|
+
import { count as count3, eq as eq29 } from "drizzle-orm";
|
|
16020
16102
|
async function resolveInsertPosition(orm, itemId2, position) {
|
|
16021
|
-
const [row3] = await orm.select({ cnt: count3() }).from(planPhases).where(
|
|
16103
|
+
const [row3] = await orm.select({ cnt: count3() }).from(planPhases).where(eq29(planPhases.itemId, itemId2));
|
|
16022
16104
|
const phaseCount = row3?.cnt ?? 0;
|
|
16023
16105
|
if (position === void 0) return phaseCount;
|
|
16024
16106
|
const pos = Number.parseInt(position, 10);
|
|
@@ -16438,9 +16520,9 @@ function hasCycle(adjacency, fromId, toId) {
|
|
|
16438
16520
|
}
|
|
16439
16521
|
|
|
16440
16522
|
// src/commands/backlog/loadDependencyGraph.ts
|
|
16441
|
-
import { eq as
|
|
16523
|
+
import { eq as eq30 } from "drizzle-orm";
|
|
16442
16524
|
async function loadDependencyGraph(orm) {
|
|
16443
|
-
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(
|
|
16525
|
+
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq30(links.type, "depends-on"));
|
|
16444
16526
|
const graph = /* @__PURE__ */ new Map();
|
|
16445
16527
|
for (const { itemId: itemId2, targetId } of rows) {
|
|
16446
16528
|
const bucket = graph.get(itemId2);
|
|
@@ -16507,7 +16589,7 @@ async function link(fromId, toId, opts) {
|
|
|
16507
16589
|
|
|
16508
16590
|
// src/commands/backlog/unlink.ts
|
|
16509
16591
|
import chalk64 from "chalk";
|
|
16510
|
-
import { and as
|
|
16592
|
+
import { and as and10, eq as eq31 } from "drizzle-orm";
|
|
16511
16593
|
async function unlink(fromId, toId) {
|
|
16512
16594
|
const fromNum = parseItemId(fromId);
|
|
16513
16595
|
const toNum = parseItemId(toId);
|
|
@@ -16531,7 +16613,7 @@ async function unlink(fromId, toId) {
|
|
|
16531
16613
|
);
|
|
16532
16614
|
return;
|
|
16533
16615
|
}
|
|
16534
|
-
await orm.delete(links).where(
|
|
16616
|
+
await orm.delete(links).where(and10(eq31(links.itemId, fromNum), eq31(links.targetId, toNum)));
|
|
16535
16617
|
console.log(
|
|
16536
16618
|
chalk64.green(
|
|
16537
16619
|
`Removed link from ${formatItemId(fromNum)} to ${formatItemId(toNum)}.`
|
|
@@ -16551,7 +16633,7 @@ function registerLinkCommands(cmd) {
|
|
|
16551
16633
|
|
|
16552
16634
|
// src/commands/backlog/moveRepo.ts
|
|
16553
16635
|
import chalk66 from "chalk";
|
|
16554
|
-
import { eq as
|
|
16636
|
+
import { eq as eq33 } from "drizzle-orm";
|
|
16555
16637
|
|
|
16556
16638
|
// src/commands/backlog/move-repo/confirmMove.ts
|
|
16557
16639
|
import chalk65 from "chalk";
|
|
@@ -16566,9 +16648,9 @@ async function confirmMove(cnt, oldOrigin, newOrigin) {
|
|
|
16566
16648
|
}
|
|
16567
16649
|
|
|
16568
16650
|
// src/commands/backlog/move-repo/countByOrigin.ts
|
|
16569
|
-
import { count as count4, eq as
|
|
16651
|
+
import { count as count4, eq as eq32 } from "drizzle-orm";
|
|
16570
16652
|
async function countByOrigin(orm, origin) {
|
|
16571
|
-
const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(
|
|
16653
|
+
const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(eq32(items.origin, origin));
|
|
16572
16654
|
return cnt;
|
|
16573
16655
|
}
|
|
16574
16656
|
|
|
@@ -16611,7 +16693,7 @@ async function moveRepo(oldOriginRaw, newOriginRaw, options2 = {}) {
|
|
|
16611
16693
|
console.log(chalk66.yellow("Move cancelled; no changes made."));
|
|
16612
16694
|
return;
|
|
16613
16695
|
}
|
|
16614
|
-
await orm.update(items).set({ origin: newOrigin }).where(
|
|
16696
|
+
await orm.update(items).set({ origin: newOrigin }).where(eq33(items.origin, oldOrigin));
|
|
16615
16697
|
console.log(
|
|
16616
16698
|
chalk66.green(
|
|
16617
16699
|
`Moved ${pluralItems(cnt)} from "${oldOrigin}" to "${newOrigin}".`
|
|
@@ -17193,12 +17275,12 @@ async function ensureDone(id) {
|
|
|
17193
17275
|
import chalk70 from "chalk";
|
|
17194
17276
|
|
|
17195
17277
|
// src/shared/db/findPhaseBySessionId.ts
|
|
17196
|
-
import { and as
|
|
17278
|
+
import { and as and11, eq as eq34 } from "drizzle-orm";
|
|
17197
17279
|
async function findPhaseBySessionId(db, itemId2, claudeSessionId) {
|
|
17198
17280
|
const rows = await db.select({ phaseIdx: phaseUsage.phaseIdx }).from(phaseUsage).where(
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17281
|
+
and11(
|
|
17282
|
+
eq34(phaseUsage.itemId, itemId2),
|
|
17283
|
+
eq34(phaseUsage.claudeSessionId, claudeSessionId)
|
|
17202
17284
|
)
|
|
17203
17285
|
).limit(1);
|
|
17204
17286
|
return rows[0]?.phaseIdx;
|
|
@@ -17665,7 +17747,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
17665
17747
|
import os4 from "os";
|
|
17666
17748
|
|
|
17667
17749
|
// src/shared/db/recordPhaseSession.ts
|
|
17668
|
-
import { sql as
|
|
17750
|
+
import { sql as sql22 } from "drizzle-orm";
|
|
17669
17751
|
async function recordPhaseSession(db, itemId2, phaseIdx, claudeSessionId, hostname2, osUser) {
|
|
17670
17752
|
await db.insert(phaseSessions).values({ itemId: itemId2, phaseIdx, claudeSessionId, hostname: hostname2, osUser }).onConflictDoUpdate({
|
|
17671
17753
|
target: [
|
|
@@ -17674,7 +17756,7 @@ async function recordPhaseSession(db, itemId2, phaseIdx, claudeSessionId, hostna
|
|
|
17674
17756
|
phaseSessions.claudeSessionId
|
|
17675
17757
|
],
|
|
17676
17758
|
set: { hostname: hostname2, osUser },
|
|
17677
|
-
setWhere:
|
|
17759
|
+
setWhere: sql22`${phaseSessions.hostname} = 'unknown'`
|
|
17678
17760
|
});
|
|
17679
17761
|
}
|
|
17680
17762
|
|
|
@@ -18545,10 +18627,10 @@ async function start(id) {
|
|
|
18545
18627
|
|
|
18546
18628
|
// src/commands/backlog/stop.ts
|
|
18547
18629
|
import chalk92 from "chalk";
|
|
18548
|
-
import { and as
|
|
18630
|
+
import { and as and12, eq as eq35 } from "drizzle-orm";
|
|
18549
18631
|
async function stop() {
|
|
18550
18632
|
const { orm } = await getReady();
|
|
18551
|
-
const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(
|
|
18633
|
+
const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(and12(eq35(items.status, "in-progress"), eq35(items.origin, getOrigin()))).returning({ id: items.id, name: items.name });
|
|
18552
18634
|
if (stopped.length === 0) {
|
|
18553
18635
|
console.log(chalk92.yellow("No in-progress items to stop."));
|
|
18554
18636
|
return;
|
|
@@ -18606,20 +18688,20 @@ function registerStatusCommands(cmd) {
|
|
|
18606
18688
|
|
|
18607
18689
|
// src/commands/backlog/movePhase.ts
|
|
18608
18690
|
import chalk95 from "chalk";
|
|
18609
|
-
import { count as count6, eq as
|
|
18691
|
+
import { count as count6, eq as eq38 } from "drizzle-orm";
|
|
18610
18692
|
|
|
18611
18693
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
18612
|
-
import { and as
|
|
18694
|
+
import { and as and14, asc as asc10, eq as eq37 } from "drizzle-orm";
|
|
18613
18695
|
|
|
18614
18696
|
// src/commands/backlog/reindexPhases.ts
|
|
18615
|
-
import { and as
|
|
18697
|
+
import { and as and13, asc as asc9, count as count5, eq as eq36 } from "drizzle-orm";
|
|
18616
18698
|
async function reindexPhases(db, itemId2) {
|
|
18617
|
-
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(
|
|
18699
|
+
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq36(planPhases.itemId, itemId2)).orderBy(asc9(planPhases.idx));
|
|
18618
18700
|
for (let i = 0; i < remaining.length; i++) {
|
|
18619
18701
|
const oldIdx = remaining[i].idx;
|
|
18620
18702
|
if (oldIdx === i) continue;
|
|
18621
|
-
await db.update(planTasks).set({ phaseIdx: i }).where(
|
|
18622
|
-
await db.update(planPhases).set({ idx: i }).where(
|
|
18703
|
+
await db.update(planTasks).set({ phaseIdx: i }).where(and13(eq36(planTasks.itemId, itemId2), eq36(planTasks.phaseIdx, oldIdx)));
|
|
18704
|
+
await db.update(planPhases).set({ idx: i }).where(and13(eq36(planPhases.itemId, itemId2), eq36(planPhases.idx, oldIdx)));
|
|
18623
18705
|
}
|
|
18624
18706
|
}
|
|
18625
18707
|
async function adjustCurrentPhase(db, item, removedIdx) {
|
|
@@ -18627,24 +18709,24 @@ async function adjustCurrentPhase(db, item, removedIdx) {
|
|
|
18627
18709
|
if (currentPhase === void 0) return;
|
|
18628
18710
|
const currentIdx = currentPhase - 1;
|
|
18629
18711
|
if (removedIdx < currentIdx) {
|
|
18630
|
-
await db.update(items).set({ currentPhase: currentPhase - 1 }).where(
|
|
18712
|
+
await db.update(items).set({ currentPhase: currentPhase - 1 }).where(eq36(items.id, item.id));
|
|
18631
18713
|
return;
|
|
18632
18714
|
}
|
|
18633
18715
|
if (removedIdx !== currentIdx) return;
|
|
18634
|
-
const [row3] = await db.select({ cnt: count5() }).from(planPhases).where(
|
|
18716
|
+
const [row3] = await db.select({ cnt: count5() }).from(planPhases).where(eq36(planPhases.itemId, item.id));
|
|
18635
18717
|
const cnt = row3?.cnt ?? 0;
|
|
18636
|
-
await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(
|
|
18718
|
+
await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(eq36(items.id, item.id));
|
|
18637
18719
|
}
|
|
18638
18720
|
|
|
18639
18721
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
18640
18722
|
async function reorderPhaseRows(orm, itemId2, fromIdx, toIdx) {
|
|
18641
18723
|
await orm.transaction(async (tx) => {
|
|
18642
|
-
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(
|
|
18643
|
-
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(
|
|
18724
|
+
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and14(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, fromIdx)));
|
|
18725
|
+
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and14(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, fromIdx))).orderBy(asc10(planTasks.idx));
|
|
18644
18726
|
await tx.delete(planTasks).where(
|
|
18645
|
-
|
|
18727
|
+
and14(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, fromIdx))
|
|
18646
18728
|
);
|
|
18647
|
-
await tx.delete(planPhases).where(
|
|
18729
|
+
await tx.delete(planPhases).where(and14(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, fromIdx)));
|
|
18648
18730
|
await reindexPhases(tx, itemId2);
|
|
18649
18731
|
await shiftPhasesUp(tx, itemId2, toIdx);
|
|
18650
18732
|
await tx.insert(planPhases).values({
|
|
@@ -18685,7 +18767,7 @@ async function movePhase(id, from, to) {
|
|
|
18685
18767
|
if (!found) return;
|
|
18686
18768
|
const { orm, item } = found;
|
|
18687
18769
|
const itemId2 = item.id;
|
|
18688
|
-
const [row3] = await orm.select({ cnt: count6() }).from(planPhases).where(
|
|
18770
|
+
const [row3] = await orm.select({ cnt: count6() }).from(planPhases).where(eq38(planPhases.itemId, itemId2));
|
|
18689
18771
|
const phaseCount = row3?.cnt ?? 0;
|
|
18690
18772
|
const fromIdx = toIndex2(from, phaseCount);
|
|
18691
18773
|
if (fromIdx === void 0) return;
|
|
@@ -18701,22 +18783,22 @@ async function movePhase(id, from, to) {
|
|
|
18701
18783
|
import chalk97 from "chalk";
|
|
18702
18784
|
|
|
18703
18785
|
// src/commands/backlog/applyPhaseUpdate.ts
|
|
18704
|
-
import { and as
|
|
18786
|
+
import { and as and15, eq as eq39 } from "drizzle-orm";
|
|
18705
18787
|
async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
18706
18788
|
await orm.transaction(async (tx) => {
|
|
18707
18789
|
if (fields.name) {
|
|
18708
18790
|
await tx.update(planPhases).set({ name: fields.name }).where(
|
|
18709
|
-
|
|
18791
|
+
and15(eq39(planPhases.itemId, itemId2), eq39(planPhases.idx, phaseIdx))
|
|
18710
18792
|
);
|
|
18711
18793
|
}
|
|
18712
18794
|
if (fields.manualCheck) {
|
|
18713
18795
|
await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
|
|
18714
|
-
|
|
18796
|
+
and15(eq39(planPhases.itemId, itemId2), eq39(planPhases.idx, phaseIdx))
|
|
18715
18797
|
);
|
|
18716
18798
|
}
|
|
18717
18799
|
if (fields.task) {
|
|
18718
18800
|
await tx.delete(planTasks).where(
|
|
18719
|
-
|
|
18801
|
+
and15(eq39(planTasks.itemId, itemId2), eq39(planTasks.phaseIdx, phaseIdx))
|
|
18720
18802
|
);
|
|
18721
18803
|
if (fields.task.length) {
|
|
18722
18804
|
await tx.insert(planTasks).values(
|
|
@@ -18734,14 +18816,14 @@ async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
|
18734
18816
|
|
|
18735
18817
|
// src/commands/backlog/findPhase.ts
|
|
18736
18818
|
import chalk96 from "chalk";
|
|
18737
|
-
import { and as
|
|
18819
|
+
import { and as and16, count as count7, eq as eq40 } from "drizzle-orm";
|
|
18738
18820
|
async function findPhase(id, phase) {
|
|
18739
18821
|
const found = await findOneItem(id);
|
|
18740
18822
|
if (!found) return void 0;
|
|
18741
18823
|
const { orm, item } = found;
|
|
18742
18824
|
const itemId2 = item.id;
|
|
18743
18825
|
const phaseIdx = Number.parseInt(phase, 10) - 1;
|
|
18744
|
-
const [row3] = await orm.select({ cnt: count7() }).from(planPhases).where(
|
|
18826
|
+
const [row3] = await orm.select({ cnt: count7() }).from(planPhases).where(and16(eq40(planPhases.itemId, itemId2), eq40(planPhases.idx, phaseIdx)));
|
|
18745
18827
|
if (!row3 || row3.cnt === 0) {
|
|
18746
18828
|
console.log(
|
|
18747
18829
|
chalk96.red(
|
|
@@ -18914,16 +18996,16 @@ function registerUpdatePhaseCommand(cmd) {
|
|
|
18914
18996
|
|
|
18915
18997
|
// src/commands/backlog/removePhase.ts
|
|
18916
18998
|
import chalk98 from "chalk";
|
|
18917
|
-
import { and as
|
|
18999
|
+
import { and as and17, eq as eq41 } from "drizzle-orm";
|
|
18918
19000
|
async function removePhase(id, phase) {
|
|
18919
19001
|
const found = await findPhase(id, phase);
|
|
18920
19002
|
if (!found) return;
|
|
18921
19003
|
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
18922
19004
|
await orm.transaction(async (tx) => {
|
|
18923
19005
|
await tx.delete(planTasks).where(
|
|
18924
|
-
|
|
19006
|
+
and17(eq41(planTasks.itemId, itemId2), eq41(planTasks.phaseIdx, phaseIdx))
|
|
18925
19007
|
);
|
|
18926
|
-
await tx.delete(planPhases).where(
|
|
19008
|
+
await tx.delete(planPhases).where(and17(eq41(planPhases.itemId, itemId2), eq41(planPhases.idx, phaseIdx)));
|
|
18927
19009
|
await reindexPhases(tx, itemId2);
|
|
18928
19010
|
await adjustCurrentPhase(tx, item, phaseIdx);
|
|
18929
19011
|
});
|
|
@@ -18936,7 +19018,7 @@ async function removePhase(id, phase) {
|
|
|
18936
19018
|
|
|
18937
19019
|
// src/commands/backlog/update/update.ts
|
|
18938
19020
|
import chalk102 from "chalk";
|
|
18939
|
-
import { eq as
|
|
19021
|
+
import { eq as eq42 } from "drizzle-orm";
|
|
18940
19022
|
|
|
18941
19023
|
// src/commands/backlog/update/buildUpdateValues.ts
|
|
18942
19024
|
import chalk99 from "chalk";
|
|
@@ -19047,7 +19129,7 @@ async function update(id, options2) {
|
|
|
19047
19129
|
if (!built) return;
|
|
19048
19130
|
const { orm } = found;
|
|
19049
19131
|
const itemId2 = found.item.id;
|
|
19050
|
-
await orm.update(items).set(built.set).where(
|
|
19132
|
+
await orm.update(items).set(built.set).where(eq42(items.id, itemId2));
|
|
19051
19133
|
console.log(
|
|
19052
19134
|
chalk102.green(`Updated ${built.fields} on item ${formatItemId(itemId2)}.`)
|
|
19053
19135
|
);
|
|
@@ -19093,7 +19175,7 @@ function readPlanUpdate(source) {
|
|
|
19093
19175
|
}
|
|
19094
19176
|
|
|
19095
19177
|
// src/commands/backlog/updatePlan/replacePlan.ts
|
|
19096
|
-
import { eq as
|
|
19178
|
+
import { eq as eq43 } from "drizzle-orm";
|
|
19097
19179
|
|
|
19098
19180
|
// src/commands/backlog/updatePlan/clampCurrentPhase.ts
|
|
19099
19181
|
function clampCurrentPhase(currentPhase, phaseCount) {
|
|
@@ -19104,8 +19186,8 @@ function clampCurrentPhase(currentPhase, phaseCount) {
|
|
|
19104
19186
|
// src/commands/backlog/updatePlan/replacePlan.ts
|
|
19105
19187
|
async function replacePlan(orm, itemId2, phases, currentPhase) {
|
|
19106
19188
|
await orm.transaction(async (tx) => {
|
|
19107
|
-
await tx.delete(planTasks).where(
|
|
19108
|
-
await tx.delete(planPhases).where(
|
|
19189
|
+
await tx.delete(planTasks).where(eq43(planTasks.itemId, itemId2));
|
|
19190
|
+
await tx.delete(planPhases).where(eq43(planPhases.itemId, itemId2));
|
|
19109
19191
|
if (phases.length > 0) {
|
|
19110
19192
|
await tx.insert(planPhases).values(
|
|
19111
19193
|
phases.map((phase, idx) => ({
|
|
@@ -19123,7 +19205,7 @@ async function replacePlan(orm, itemId2, phases, currentPhase) {
|
|
|
19123
19205
|
if (currentPhase === void 0) return;
|
|
19124
19206
|
const clamped = clampCurrentPhase(currentPhase, phases.length);
|
|
19125
19207
|
if (clamped === currentPhase) return;
|
|
19126
|
-
await tx.update(items).set({ currentPhase: clamped }).where(
|
|
19208
|
+
await tx.update(items).set({ currentPhase: clamped }).where(eq43(items.id, itemId2));
|
|
19127
19209
|
});
|
|
19128
19210
|
}
|
|
19129
19211
|
|
|
@@ -27567,18 +27649,6 @@ function registerGithub(program) {
|
|
|
27567
27649
|
|
|
27568
27650
|
// src/commands/inbox/list.ts
|
|
27569
27651
|
import chalk171 from "chalk";
|
|
27570
|
-
|
|
27571
|
-
// src/shared/db/listInboxMessages.ts
|
|
27572
|
-
import { desc as desc6, isNull, sql as sql22 } from "drizzle-orm";
|
|
27573
|
-
function listInboxMessages(orm) {
|
|
27574
|
-
return orm.select().from(inboxMessages).where(isNull(inboxMessages.dismissedAt)).orderBy(
|
|
27575
|
-
desc6(
|
|
27576
|
-
sql22`coalesce(${inboxMessages.receivedAt}, ${inboxMessages.flaggedAt})`
|
|
27577
|
-
)
|
|
27578
|
-
);
|
|
27579
|
-
}
|
|
27580
|
-
|
|
27581
|
-
// src/commands/inbox/list.ts
|
|
27582
27652
|
async function list3() {
|
|
27583
27653
|
const messages = await listInboxMessages(await getDb());
|
|
27584
27654
|
if (messages.length === 0) {
|
|
@@ -27599,10 +27669,10 @@ async function list3() {
|
|
|
27599
27669
|
import chalk172 from "chalk";
|
|
27600
27670
|
|
|
27601
27671
|
// src/shared/db/readInboxLastScan.ts
|
|
27602
|
-
import { eq as
|
|
27672
|
+
import { eq as eq44 } from "drizzle-orm";
|
|
27603
27673
|
var INBOX_LAST_SCAN_KEY = "inbox.lastScanAt";
|
|
27604
27674
|
async function readInboxLastScan(orm) {
|
|
27605
|
-
const [row3] = await orm.select().from(metadata).where(
|
|
27675
|
+
const [row3] = await orm.select().from(metadata).where(eq44(metadata.key, INBOX_LAST_SCAN_KEY));
|
|
27606
27676
|
return row3 ? new Date(row3.value) : void 0;
|
|
27607
27677
|
}
|
|
27608
27678
|
|
|
@@ -30337,9 +30407,9 @@ async function list4() {
|
|
|
30337
30407
|
import chalk188 from "chalk";
|
|
30338
30408
|
|
|
30339
30409
|
// src/commands/backlog/removeFeed.ts
|
|
30340
|
-
import { eq as
|
|
30410
|
+
import { eq as eq45 } from "drizzle-orm";
|
|
30341
30411
|
async function removeFeed(db, url) {
|
|
30342
|
-
const deleted = await db.delete(feeds).where(
|
|
30412
|
+
const deleted = await db.delete(feeds).where(eq45(feeds.url, url)).returning({ id: feeds.id });
|
|
30343
30413
|
return deleted.length > 0;
|
|
30344
30414
|
}
|
|
30345
30415
|
|
|
@@ -38454,7 +38524,10 @@ function selectInDiffFindings(lineBound, prDiff) {
|
|
|
38454
38524
|
return {
|
|
38455
38525
|
inDiff,
|
|
38456
38526
|
unanchored: outOfDiff.map(
|
|
38457
|
-
(finding) => ({
|
|
38527
|
+
(finding) => ({
|
|
38528
|
+
...finding,
|
|
38529
|
+
reason: "out-of-diff"
|
|
38530
|
+
})
|
|
38458
38531
|
)
|
|
38459
38532
|
};
|
|
38460
38533
|
}
|
|
@@ -38491,7 +38564,10 @@ function selectPostableFindings(markdown, prInfo) {
|
|
|
38491
38564
|
);
|
|
38492
38565
|
}
|
|
38493
38566
|
const carriedUnlocated = unlocated.map(
|
|
38494
|
-
(finding) => ({
|
|
38567
|
+
(finding) => ({
|
|
38568
|
+
...finding,
|
|
38569
|
+
reason: "unlocated"
|
|
38570
|
+
})
|
|
38495
38571
|
);
|
|
38496
38572
|
if (lineBound.length === 0) {
|
|
38497
38573
|
console.log("No line-bound findings to comment on.");
|
|
@@ -44675,12 +44751,12 @@ function parseEnvPairs(pairs) {
|
|
|
44675
44751
|
if (pairs.length === 0) return void 0;
|
|
44676
44752
|
const env = {};
|
|
44677
44753
|
for (const pair of pairs) {
|
|
44678
|
-
const
|
|
44679
|
-
if (
|
|
44754
|
+
const eq48 = pair.indexOf("=");
|
|
44755
|
+
if (eq48 <= 0) {
|
|
44680
44756
|
console.error(`--env must be KEY=VALUE, got "${pair}"`);
|
|
44681
44757
|
process.exit(1);
|
|
44682
44758
|
}
|
|
44683
|
-
env[pair.slice(0,
|
|
44759
|
+
env[pair.slice(0, eq48)] = pair.slice(eq48 + 1);
|
|
44684
44760
|
}
|
|
44685
44761
|
return env;
|
|
44686
44762
|
}
|
|
@@ -44693,11 +44769,11 @@ function fail9(spec) {
|
|
|
44693
44769
|
process.exit(1);
|
|
44694
44770
|
}
|
|
44695
44771
|
function parseParamSpec(spec) {
|
|
44696
|
-
const
|
|
44697
|
-
const name =
|
|
44698
|
-
if (
|
|
44699
|
-
if (
|
|
44700
|
-
return { name, default: spec.slice(
|
|
44772
|
+
const eq48 = spec.indexOf("=");
|
|
44773
|
+
const name = eq48 === -1 ? spec : spec.slice(0, eq48);
|
|
44774
|
+
if (eq48 !== -1) {
|
|
44775
|
+
if (eq48 === 0 || name.endsWith("!")) fail9(spec);
|
|
44776
|
+
return { name, default: spec.slice(eq48 + 1) };
|
|
44701
44777
|
}
|
|
44702
44778
|
if (name.endsWith("!")) {
|
|
44703
44779
|
if (name.length === 1) fail9(spec);
|
|
@@ -47961,10 +48037,10 @@ function applyReviewPause(session, activity2) {
|
|
|
47961
48037
|
}
|
|
47962
48038
|
|
|
47963
48039
|
// src/shared/db/getPhaseActiveMs.ts
|
|
47964
|
-
import { and as
|
|
48040
|
+
import { and as and18, eq as eq46 } from "drizzle-orm";
|
|
47965
48041
|
async function getPhaseActiveMs(db, itemId2, phaseIdx) {
|
|
47966
48042
|
const [row3] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
47967
|
-
|
|
48043
|
+
and18(eq46(phaseUsage.itemId, itemId2), eq46(phaseUsage.phaseIdx, phaseIdx))
|
|
47968
48044
|
);
|
|
47969
48045
|
return row3?.activeMs ?? 0;
|
|
47970
48046
|
}
|
|
@@ -48806,17 +48882,17 @@ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
|
|
|
48806
48882
|
}
|
|
48807
48883
|
|
|
48808
48884
|
// src/shared/db/recordWindowTokens.ts
|
|
48809
|
-
import { and as
|
|
48885
|
+
import { and as and19, eq as eq47, sql as sql28 } from "drizzle-orm";
|
|
48810
48886
|
async function recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown) {
|
|
48811
48887
|
if (tokensUp <= 0 && tokensDown <= 0) return;
|
|
48812
48888
|
await db.update(usagePeaks).set({
|
|
48813
48889
|
tokensUp: sql28`${usagePeaks.tokensUp} + ${tokensUp}`,
|
|
48814
48890
|
tokensDown: sql28`${usagePeaks.tokensDown} + ${tokensDown}`
|
|
48815
48891
|
}).where(
|
|
48816
|
-
|
|
48817
|
-
|
|
48818
|
-
|
|
48819
|
-
|
|
48892
|
+
and19(
|
|
48893
|
+
eq47(usagePeaks.window, window),
|
|
48894
|
+
eq47(usagePeaks.resetsAt, resetsAt),
|
|
48895
|
+
eq47(usagePeaks.segment, 0)
|
|
48820
48896
|
)
|
|
48821
48897
|
);
|
|
48822
48898
|
}
|