@ashlr/hub 3.4.0 → 3.5.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/CHANGELOG.md +56 -1
- package/README.md +3 -0
- package/dist/build-identity.json +2 -2
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/local-runtime.js +40 -9
- package/dist/cli/local-runtime.js.map +1 -1
- package/dist/core/local-eval/classify.d.ts +57 -0
- package/dist/core/local-eval/classify.js +121 -0
- package/dist/core/local-eval/classify.js.map +1 -0
- package/dist/core/local-eval/configuration.d.ts +37 -0
- package/dist/core/local-eval/configuration.js +171 -0
- package/dist/core/local-eval/configuration.js.map +1 -0
- package/dist/core/local-eval/index.d.ts +15 -0
- package/dist/core/local-eval/index.js +16 -0
- package/dist/core/local-eval/index.js.map +1 -0
- package/dist/core/local-eval/main.d.ts +31 -0
- package/dist/core/local-eval/main.js +174 -0
- package/dist/core/local-eval/main.js.map +1 -0
- package/dist/core/local-eval/report.d.ts +25 -0
- package/dist/core/local-eval/report.js +157 -0
- package/dist/core/local-eval/report.js.map +1 -0
- package/dist/core/local-eval/runner.d.ts +55 -0
- package/dist/core/local-eval/runner.js +290 -0
- package/dist/core/local-eval/runner.js.map +1 -0
- package/dist/core/local-eval/tasks.d.ts +22 -0
- package/dist/core/local-eval/tasks.js +303 -0
- package/dist/core/local-eval/tasks.js.map +1 -0
- package/dist/core/local-eval/trace.d.ts +67 -0
- package/dist/core/local-eval/trace.js +309 -0
- package/dist/core/local-eval/trace.js.map +1 -0
- package/dist/core/local-eval/types.d.ts +262 -0
- package/dist/core/local-eval/types.js +23 -0
- package/dist/core/local-eval/types.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.d.ts +68 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.js +120 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-host.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.d.ts +15 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.js +21 -0
- package/dist/core/local-runtime/llama/anthropic-proxy-process.js.map +1 -0
- package/dist/core/local-runtime/llama/anthropic-proxy.d.ts +2 -1
- package/dist/core/local-runtime/llama/anthropic-proxy.js +14 -6
- package/dist/core/local-runtime/llama/anthropic-proxy.js.map +1 -1
- package/dist/core/local-runtime/llama/anthropic-shim.d.ts +33 -0
- package/dist/core/local-runtime/llama/anthropic-shim.js +59 -0
- package/dist/core/local-runtime/llama/anthropic-shim.js.map +1 -1
- package/dist/core/local-runtime/llama/config.d.ts +101 -6
- package/dist/core/local-runtime/llama/config.js +174 -6
- package/dist/core/local-runtime/llama/config.js.map +1 -1
- package/dist/core/local-runtime/llama/index.d.ts +15 -0
- package/dist/core/local-runtime/llama/index.js +13 -0
- package/dist/core/local-runtime/llama/index.js.map +1 -1
- package/dist/core/local-runtime/llama/paths.d.ts +17 -0
- package/dist/core/local-runtime/llama/paths.js +25 -0
- package/dist/core/local-runtime/llama/paths.js.map +1 -1
- package/dist/core/local-runtime/llama/proxy-invocation.d.ts +101 -0
- package/dist/core/local-runtime/llama/proxy-invocation.js +134 -0
- package/dist/core/local-runtime/llama/proxy-invocation.js.map +1 -0
- package/dist/core/local-runtime/llama/proxy-launchd.d.ts +83 -0
- package/dist/core/local-runtime/llama/proxy-launchd.js +216 -0
- package/dist/core/local-runtime/llama/proxy-launchd.js.map +1 -0
- package/dist/core/local-runtime/llama/shapes.d.ts +2 -1
- package/dist/core/local-runtime/llama/shapes.js +2 -1
- package/dist/core/local-runtime/llama/shapes.js.map +1 -1
- package/dist/core/verse/adapters/claude.js +16 -0
- package/dist/core/verse/adapters/claude.js.map +1 -1
- package/dist/core/verse/adapters/codex.js +60 -2
- package/dist/core/verse/adapters/codex.js.map +1 -1
- package/dist/core/verse/adapters/grok.js +13 -0
- package/dist/core/verse/adapters/grok.js.map +1 -1
- package/dist/core/verse/control-api.d.ts +8 -9
- package/dist/core/verse/control-api.js +14 -114
- package/dist/core/verse/control-api.js.map +1 -1
- package/dist/core/verse/github-api.d.ts +50 -0
- package/dist/core/verse/github-api.js +211 -0
- package/dist/core/verse/github-api.js.map +1 -0
- package/dist/core/verse/github-proposal.d.ts +64 -0
- package/dist/core/verse/github-proposal.js +186 -0
- package/dist/core/verse/github-proposal.js.map +1 -0
- package/dist/core/verse/github-repo.d.ts +104 -0
- package/dist/core/verse/github-repo.js +476 -0
- package/dist/core/verse/github-repo.js.map +1 -0
- package/dist/core/verse/github-types.d.ts +174 -0
- package/dist/core/verse/github-types.js +31 -0
- package/dist/core/verse/github-types.js.map +1 -0
- package/dist/core/verse/mcp-cli-health.d.ts +149 -0
- package/dist/core/verse/mcp-cli-health.js +227 -0
- package/dist/core/verse/mcp-cli-health.js.map +1 -0
- package/dist/core/verse/mcp-control-api.d.ts +68 -0
- package/dist/core/verse/mcp-control-api.js +322 -0
- package/dist/core/verse/mcp-control-api.js.map +1 -0
- package/dist/core/verse/mcp-mutation.d.ts +187 -0
- package/dist/core/verse/mcp-mutation.js +471 -0
- package/dist/core/verse/mcp-mutation.js.map +1 -0
- package/dist/core/verse/mcp-scope.d.ts +114 -0
- package/dist/core/verse/mcp-scope.js +149 -0
- package/dist/core/verse/mcp-scope.js.map +1 -0
- package/dist/core/verse/mcp-seat-view.d.ts +197 -0
- package/dist/core/verse/mcp-seat-view.js +322 -0
- package/dist/core/verse/mcp-seat-view.js.map +1 -0
- package/dist/core/verse/path-guard.d.ts +115 -0
- package/dist/core/verse/path-guard.js +171 -0
- package/dist/core/verse/path-guard.js.map +1 -0
- package/dist/core/verse/seats.js +30 -2
- package/dist/core/verse/seats.js.map +1 -1
- package/dist/core/verse/session-engine.js +60 -1
- package/dist/core/verse/session-engine.js.map +1 -1
- package/dist/core/verse/session-store.js +20 -0
- package/dist/core/verse/session-store.js.map +1 -1
- package/dist/core/verse/types.d.ts +225 -1
- package/dist/core/verse/types.js +26 -0
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/verse/verse-api.d.ts +16 -1
- package/dist/core/verse/verse-api.js +338 -16
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/verse/workspaces.d.ts +130 -0
- package/dist/core/verse/workspaces.js +501 -0
- package/dist/core/verse/workspaces.js.map +1 -0
- package/dist/core/web/api.js +23 -0
- package/dist/core/web/api.js.map +1 -1
- package/dist/core/web/public/next/assets/{App-LuILXjWG.js → App-CsbPYQqy.js} +2 -2
- package/dist/core/web/public/next/assets/{App-LuILXjWG.js.map → App-CsbPYQqy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js → ApprovalsSection-CBZquyLK.js} +2 -2
- package/dist/core/web/public/next/assets/{ApprovalsSection-B_Xor8JH.js.map → ApprovalsSection-CBZquyLK.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-CQVpEdCE.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-BEwITtPJ.js.map → AutonomySection-CQVpEdCE.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-DTN6m6QE.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DiRvNX3Q.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js +77 -0
- package/dist/core/web/public/next/assets/ChatSection-DmE9K0Tg.js.map +1 -0
- package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js → ConfirmDialog-deO0C0iY.js} +2 -2
- package/dist/core/web/public/next/assets/{ConfirmDialog-BqkZdPs_.js.map → ConfirmDialog-deO0C0iY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js → Dialog-CNd7q89m.js} +2 -2
- package/dist/core/web/public/next/assets/{Dialog-CPmedg4S.js.map → Dialog-CNd7q89m.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js → DiffViewer-DTO0KRuG.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-DmPJbeM_.js.map → DiffViewer-DTO0KRuG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js → Epistemic-53PikaUI.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-M3SxLHjq.js.map → Epistemic-53PikaUI.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js → Meter-DRWZQgr1.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-D_X7c9U3.js.map → Meter-DRWZQgr1.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js → MutationTokenDialog-DYF8n_RD.js} +2 -2
- package/dist/core/web/public/next/assets/{MutationTokenDialog-Ba5ZHu-8.js.map → MutationTokenDialog-DYF8n_RD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js → RefreshIndicator-DXJQXKyA.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-vHGpmyzb.js.map → RefreshIndicator-DXJQXKyA.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js → ResourcePoolConsoleApp-hDSEFdLU.js} +2 -2
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-B5g6JEVk.js.map → ResourcePoolConsoleApp-hDSEFdLU.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js → RouteErrorBoundary-CZd7ln8v.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-CroxuKoi.js.map → RouteErrorBoundary-CZd7ln8v.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js → SettingsSection-C9Qs9JWW.js} +2 -2
- package/dist/core/web/public/next/assets/{SettingsSection-p-7FCGfn.js.map → SettingsSection-C9Qs9JWW.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js → Skeleton-YkcOpgFb.js} +2 -2
- package/dist/core/web/public/next/assets/{Skeleton-4qq4HuJO.js.map → Skeleton-YkcOpgFb.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js → StatusBadge-DJw0LSPg.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-BbEdqEGn.js.map → StatusBadge-DJw0LSPg.js.map} +1 -1
- package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js → TableView-Bj-w1O6o.js} +2 -2
- package/dist/core/web/public/next/assets/{TableView-nyYs7Sv1.js.map → TableView-Bj-w1O6o.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js → UniverseConsoleApp-Bs2fxaen.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-DjTpQUrA.js.map → UniverseConsoleApp-Bs2fxaen.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js → UniverseView-B6b2hw-8.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseView-BtApwayT.js.map → UniverseView-B6b2hw-8.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js → UsageSection-C__ziNkV.js} +2 -2
- package/dist/core/web/public/next/assets/{UsageSection-Db99N6ao.js.map → UsageSection-C__ziNkV.js.map} +1 -1
- package/dist/core/web/public/next/assets/{VerseConsoleApp-COa3J2dO.js → VerseConsoleApp-Cu5aG6J6.js} +3 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-Cu5aG6J6.js.map +1 -0
- package/dist/core/web/public/next/assets/{global-CNVapoV1.js → global-BHjd6fVZ.js} +2 -2
- package/dist/core/web/public/next/assets/{global-CNVapoV1.js.map → global-BHjd6fVZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/index-CJ-e8YA9.js +2 -0
- package/dist/core/web/public/next/assets/index-CJ-e8YA9.js.map +1 -0
- package/dist/core/web/public/next/assets/{index-BkUlX2Se.js → index-CpgkRaTi.js} +3 -3
- package/dist/core/web/public/next/assets/{index-BkUlX2Se.js.map → index-CpgkRaTi.js.map} +1 -1
- package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js → queries-856qUN3Y.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-C9XS4SYq.js.map → queries-856qUN3Y.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js → use-guarded-action-DwaESKVy.js} +2 -2
- package/dist/core/web/public/next/assets/{use-guarded-action-Bql1nA-z.js.map → use-guarded-action-DwaESKVy.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-model-Dllo8rRt.js +2 -0
- package/dist/core/web/public/next/assets/{verse-model-IrNQKtER.js.map → verse-model-Dllo8rRt.js.map} +1 -1
- package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js +2 -0
- package/dist/core/web/public/next/assets/workspace-model-JgmWDKt4.js.map +1 -0
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/README.md +4 -0
- package/package.json +1 -1
- package/dist/cli/activation.d.ts +0 -41
- package/dist/cli/activation.js +0 -485
- package/dist/cli/activation.js.map +0 -1
- package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
- package/dist/core/fleet/generated-repair-resolution.js +0 -245
- package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
- package/dist/core/local-runtime/llama/port.d.ts +0 -17
- package/dist/core/local-runtime/llama/port.js +0 -47
- package/dist/core/local-runtime/llama/port.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
- package/dist/core/local-runtime/llama/proxy-host.js +0 -159
- package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
- package/dist/core/local-runtime/llama/proxy-process.js +0 -250
- package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
- package/dist/core/local-runtime/llama/proxy-record.js +0 -133
- package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
- package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
- package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-BB8RD-tv.css +0 -1
- package/dist/core/web/public/next/assets/AutonomySection-BEwITtPJ.js +0 -2
- package/dist/core/web/public/next/assets/ChatSection-DrxqhBPK.css +0 -1
- package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js +0 -77
- package/dist/core/web/public/next/assets/ChatSection-bRAzlMth.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-COa3J2dO.js.map +0 -1
- package/dist/core/web/public/next/assets/index-YNTgDG2b.js +0 -2
- package/dist/core/web/public/next/assets/index-YNTgDG2b.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-model-IrNQKtER.js +0 -2
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/verse/github-api.ts — `GET /api/verse/github{,/pr-plan}`
|
|
3
|
+
* (docs/VERSE-WORKSPACES.md §2). The routes that make github.ts reachable
|
|
4
|
+
* from Ashlr Verse.
|
|
5
|
+
*
|
|
6
|
+
* Mounted from src/core/web/api.ts's handleApi() BEFORE the V1 verse handler,
|
|
7
|
+
* for the same reason the V2 control plane is: `isVerseApiPath()` matches every
|
|
8
|
+
* `/api/verse/*` path and 404s what it does not recognise, so a route mounted
|
|
9
|
+
* after it is never reached.
|
|
10
|
+
*
|
|
11
|
+
* SECURITY POSTURE — identical to control-api.ts, minus the POST half:
|
|
12
|
+
* - Both routes are GET-only and sit behind the read-session boundary in
|
|
13
|
+
* server.ts. There is NO mutation here. Nothing in this module opens,
|
|
14
|
+
* closes, merges, comments on, or pushes anything; `createPr` is not
|
|
15
|
+
* imported and `applyProposal` is not called.
|
|
16
|
+
* - Every response goes through sendJson() → sanitizePublicJson().
|
|
17
|
+
* - `?repo=` is refused unless the path is one the operator already brought
|
|
18
|
+
* into Verse — an enrolled repo, or the project path of an existing
|
|
19
|
+
* session (exactly `discoverProjects()`'s answer). The route is therefore
|
|
20
|
+
* not a filesystem probe.
|
|
21
|
+
* - No token, remote URL, launcher argv, or gh stderr can reach a response:
|
|
22
|
+
* github-repo.ts reads only `nameWithOwner`, and gh failures collapse to
|
|
23
|
+
* `prsAvailable: false` plus a fixed sentence.
|
|
24
|
+
*/
|
|
25
|
+
import { sendJson } from '../web/api.js';
|
|
26
|
+
import { loadProposal } from '../inbox/store.js';
|
|
27
|
+
import { discoverProjects } from './projects.js';
|
|
28
|
+
import { expandHomePrefix, peekVerseEngine } from './verse-api.js';
|
|
29
|
+
import { describeVersePrPlans } from './github-proposal.js';
|
|
30
|
+
import { readVerseGithubSnapshot } from './github-repo.js';
|
|
31
|
+
const VERSE_PREFIX = '/api/verse';
|
|
32
|
+
export const VERSE_GITHUB_ROUTE = `${VERSE_PREFIX}/github`;
|
|
33
|
+
export const VERSE_GITHUB_PR_PLAN_ROUTE = `${VERSE_PREFIX}/github/pr-plan`;
|
|
34
|
+
const GITHUB_ROUTES = new Set([VERSE_GITHUB_ROUTE, VERSE_GITHUB_PR_PLAN_ROUTE]);
|
|
35
|
+
/** Bound the work one request can ask for. */
|
|
36
|
+
const MAX_REPOS_PER_REQUEST = 12;
|
|
37
|
+
const MAX_PLAN_IDS = 50;
|
|
38
|
+
const MAX_ID_CHARS = 128;
|
|
39
|
+
const MAX_PATH_CHARS = 4_096;
|
|
40
|
+
/** Proposal ids are `prop-<ts>-<hex>`; accept that shape and nothing exotic. */
|
|
41
|
+
const PROPOSAL_ID_RE = /^[A-Za-z0-9._-]+$/;
|
|
42
|
+
/** True for exactly the two GitHub routes. */
|
|
43
|
+
export function isVerseGithubPath(path) {
|
|
44
|
+
return GITHUB_ROUTES.has(path);
|
|
45
|
+
}
|
|
46
|
+
const ERROR_STATUS = {
|
|
47
|
+
VERSE_INVALID: 400,
|
|
48
|
+
VERSE_REFUSED: 409,
|
|
49
|
+
VERSE_TOO_LARGE: 413,
|
|
50
|
+
VERSE_UNAVAILABLE: 503,
|
|
51
|
+
};
|
|
52
|
+
function sendError(res, code, error) {
|
|
53
|
+
sendJson(res, ERROR_STATUS[code], { code, error });
|
|
54
|
+
}
|
|
55
|
+
function queryParam(req, key) {
|
|
56
|
+
const raw = req.url ?? '';
|
|
57
|
+
const qIndex = raw.indexOf('?');
|
|
58
|
+
if (qIndex < 0)
|
|
59
|
+
return null;
|
|
60
|
+
const params = new URLSearchParams(raw.slice(qIndex + 1));
|
|
61
|
+
return params.get(key);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Every root the operator has already brought into Verse: enrolled repos
|
|
65
|
+
* first, then the project path of each live session. Identical to what
|
|
66
|
+
* `GET /api/verse/bootstrap` lists, so the GitHub panel can never show a root
|
|
67
|
+
* the picker does not.
|
|
68
|
+
*
|
|
69
|
+
* `peekVerseEngine()` is used rather than `getVerseEngine()` on purpose — a
|
|
70
|
+
* read must never be the thing that creates the session engine.
|
|
71
|
+
*/
|
|
72
|
+
function defaultKnownRoots() {
|
|
73
|
+
let sessions = [];
|
|
74
|
+
try {
|
|
75
|
+
sessions = peekVerseEngine()?.listSessions() ?? [];
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
sessions = [];
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return discoverProjects({ sessions }).map((p) => p.path);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Handle one `/api/verse/github*` request. Returns true when a response was
|
|
89
|
+
* written (including errors); false when `path` is not one of these routes.
|
|
90
|
+
*/
|
|
91
|
+
export async function handleVerseGithubApi(_ctx, req, res, path, method, deps = {}) {
|
|
92
|
+
if (!isVerseGithubPath(path))
|
|
93
|
+
return false;
|
|
94
|
+
try {
|
|
95
|
+
if (method !== 'GET') {
|
|
96
|
+
sendJson(res, 404, { error: `not found: ${method} ${path}` });
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (path === VERSE_GITHUB_ROUTE) {
|
|
100
|
+
handleRepos(req, res, deps);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
handlePrPlans(req, res, deps);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
if (!res.headersSent) {
|
|
108
|
+
sendJson(res, 500, { code: 'INTERNAL_ERROR', error: 'internal server error' });
|
|
109
|
+
}
|
|
110
|
+
else if (!res.writableEnded) {
|
|
111
|
+
try {
|
|
112
|
+
res.end();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
/* already gone */
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// GET /api/verse/github[?repo=<absolute path>]
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
function handleRepos(req, res, deps) {
|
|
125
|
+
const read = deps.read ?? readVerseGithubSnapshot;
|
|
126
|
+
const known = (deps.knownRoots ?? defaultKnownRoots)();
|
|
127
|
+
const requested = queryParam(req, 'repo');
|
|
128
|
+
let roots;
|
|
129
|
+
// Without `?repo=`, this is the cheap index: identity and default branch per
|
|
130
|
+
// root, from local `git` reads only. `gh` is an 8 s-ceilinged synchronous
|
|
131
|
+
// subprocess on a single-threaded server, so a dozen roots' worth of list
|
|
132
|
+
// calls in one request is not something a UI mount may trigger. The panel
|
|
133
|
+
// fills in one root at a time with `?repo=`.
|
|
134
|
+
let includeLists = false;
|
|
135
|
+
if (requested === null) {
|
|
136
|
+
roots = known.slice(0, MAX_REPOS_PER_REQUEST);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
includeLists = true;
|
|
140
|
+
if (requested.length === 0 || requested.length > MAX_PATH_CHARS) {
|
|
141
|
+
sendError(res, 'VERSE_INVALID', 'repo must be a non-empty path');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const resolved = expandHomePrefix(requested);
|
|
145
|
+
if (!known.includes(resolved)) {
|
|
146
|
+
// Not "no such directory" — deliberately the same answer whether the
|
|
147
|
+
// path exists or not, so this route cannot be used to probe the disk.
|
|
148
|
+
sendError(res, 'VERSE_REFUSED', 'repo is not an enrolled project or an open session path');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
roots = [resolved];
|
|
152
|
+
}
|
|
153
|
+
const snapshot = read(roots, {
|
|
154
|
+
includeLists,
|
|
155
|
+
...(deps.readOptions ?? {}),
|
|
156
|
+
});
|
|
157
|
+
sendJson(res, 200, snapshot);
|
|
158
|
+
}
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// GET /api/verse/github/pr-plan?id=<proposalId>[,<proposalId>…]
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
function handlePrPlans(req, res, deps) {
|
|
163
|
+
const describe = deps.describePlans ?? describeVersePrPlans;
|
|
164
|
+
const load = deps.loadProposal ?? ((id) => loadProposal(id));
|
|
165
|
+
const raw = queryParam(req, 'id');
|
|
166
|
+
if (raw === null || raw.length === 0) {
|
|
167
|
+
sendError(res, 'VERSE_INVALID', 'id is required');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const ids = [];
|
|
171
|
+
for (const part of raw.split(',')) {
|
|
172
|
+
const id = part.trim();
|
|
173
|
+
if (id.length === 0)
|
|
174
|
+
continue;
|
|
175
|
+
if (id.length > MAX_ID_CHARS || !PROPOSAL_ID_RE.test(id)) {
|
|
176
|
+
sendError(res, 'VERSE_INVALID', 'id contains an unsupported character');
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!ids.includes(id))
|
|
180
|
+
ids.push(id);
|
|
181
|
+
if (ids.length > MAX_PLAN_IDS) {
|
|
182
|
+
sendError(res, 'VERSE_INVALID', `at most ${MAX_PLAN_IDS} ids per request`);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (ids.length === 0) {
|
|
187
|
+
sendError(res, 'VERSE_INVALID', 'id is required');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const proposals = [];
|
|
191
|
+
for (const id of ids) {
|
|
192
|
+
let proposal = null;
|
|
193
|
+
try {
|
|
194
|
+
proposal = load(id);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
proposal = null;
|
|
198
|
+
}
|
|
199
|
+
// An unknown id is silently absent rather than a 404: a client asking for
|
|
200
|
+
// several plans should not lose all of them because one was just applied
|
|
201
|
+
// and rotated out from under it.
|
|
202
|
+
if (proposal)
|
|
203
|
+
proposals.push(proposal);
|
|
204
|
+
}
|
|
205
|
+
const body = {
|
|
206
|
+
plans: describe(proposals),
|
|
207
|
+
observedAt: new Date().toISOString(),
|
|
208
|
+
};
|
|
209
|
+
sendJson(res, 200, body);
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=github-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-api.js","sourceRoot":"","sources":["../../../src/core/verse/github-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/verse/github-proposal.ts — the pre-click disclosure for an approved
|
|
3
|
+
* proposal of kind `pr` (docs/VERSE-WORKSPACES.md §2).
|
|
4
|
+
*
|
|
5
|
+
* THE POINT OF THIS FILE. Approving a `pr` proposal is the one action in the
|
|
6
|
+
* hub that reaches outside the machine: `core/inbox/apply.ts` builds a branch
|
|
7
|
+
* in a throwaway worktree and calls `createPr()`, which publishes that branch
|
|
8
|
+
* and opens a real pull request. The Approvals view names the repo and the
|
|
9
|
+
* kind. It does not name the branch it is about to create or the pull request
|
|
10
|
+
* it is about to open, so the operator learns both AFTER the click, from
|
|
11
|
+
* `proposal.result` — a sentence, produced by the thing they already approved.
|
|
12
|
+
* This module computes them BEFORE.
|
|
13
|
+
*
|
|
14
|
+
* ── WHAT APPLY ACTUALLY DOES (verified against src/core/inbox/apply.ts) ────
|
|
15
|
+
* • branch = `ashlr/proposal/${proposal.id}` (apply.ts:52, :125, :254)
|
|
16
|
+
* • createPr(repo, { title: proposal.title,
|
|
17
|
+
* body: proposal.summary,
|
|
18
|
+
* head: branch }) (apply.ts:257-261)
|
|
19
|
+
* • NO `base` is passed, so GitHub resolves the base to the repository's
|
|
20
|
+
* default branch. `predictPrPlan` reproduces that locally from
|
|
21
|
+
* `git symbolic-ref origin/HEAD` and labels it `baseSource: 'predicted'`,
|
|
22
|
+
* because it is our reading of gh's choice, not an instruction to gh.
|
|
23
|
+
* • NO `git push` runs. `gh pr create --head <branch>` publishes the branch
|
|
24
|
+
* as a side effect, which is why `publishesBranch` is true and stated.
|
|
25
|
+
*
|
|
26
|
+
* ── DRIFT ──────────────────────────────────────────────────────────────────
|
|
27
|
+
* `PROPOSAL_BRANCH_PREFIX` is module-private in apply.ts, so the literal is
|
|
28
|
+
* repeated here. test/verse-github-proposal.test.ts reads apply.ts and fails
|
|
29
|
+
* if the two ever diverge — a disclosure that predicts the wrong branch name
|
|
30
|
+
* is worse than none.
|
|
31
|
+
*
|
|
32
|
+
* READ-ONLY. NEVER THROWS. Opens nothing, pushes nothing, spawns only the
|
|
33
|
+
* `git` reads core/git.ts already performs.
|
|
34
|
+
*/
|
|
35
|
+
import type { Proposal } from '../types.js';
|
|
36
|
+
import type { VerseGithubPrPlan } from './github-types.js';
|
|
37
|
+
/**
|
|
38
|
+
* Mirrors `PROPOSAL_BRANCH_PREFIX` in src/core/inbox/apply.ts:52, which is not
|
|
39
|
+
* exported. Guarded against drift by test/verse-github-proposal.test.ts.
|
|
40
|
+
*/
|
|
41
|
+
export declare const VERSE_PROPOSAL_BRANCH_PREFIX = "ashlr/proposal/";
|
|
42
|
+
/** The subset of `Proposal` a plan needs. Structural, so fixtures stay small. */
|
|
43
|
+
export type VersePrPlanProposal = Pick<Proposal, 'id' | 'kind' | 'repo' | 'title' | 'summary' | 'status'> & Partial<Pick<Proposal, 'result' | 'remoteHandoff' | 'localMergeIntent'>>;
|
|
44
|
+
/** Git facts the plan needs. Defaults to core/git.ts; injected in tests. */
|
|
45
|
+
export interface VersePrPlanGitProbe {
|
|
46
|
+
defaultBranch(path: string): string;
|
|
47
|
+
nameWithOwner(path: string): string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface VersePrPlanOptions {
|
|
50
|
+
git?: VersePrPlanGitProbe;
|
|
51
|
+
}
|
|
52
|
+
/** The branch `applyPr` will create for this proposal id. */
|
|
53
|
+
export declare function verseProposalBranch(proposalId: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Describe what approving this proposal will do — or, once it has been
|
|
56
|
+
* applied, what it did.
|
|
57
|
+
*
|
|
58
|
+
* Returns `state: 'unavailable'` (and no branch/base) for anything that is not
|
|
59
|
+
* an outward-facing `pr` proposal, so a caller can render the disclosure
|
|
60
|
+
* unconditionally and let the shape decide whether there is anything to show.
|
|
61
|
+
*/
|
|
62
|
+
export declare function describeVersePrPlan(proposal: VersePrPlanProposal, opts?: VersePrPlanOptions): VerseGithubPrPlan;
|
|
63
|
+
/** Plans for several proposals, in the order given. Never throws. */
|
|
64
|
+
export declare function describeVersePrPlans(proposals: readonly VersePrPlanProposal[], opts?: VersePrPlanOptions): VerseGithubPrPlan[];
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/verse/github-proposal.ts — the pre-click disclosure for an approved
|
|
3
|
+
* proposal of kind `pr` (docs/VERSE-WORKSPACES.md §2).
|
|
4
|
+
*
|
|
5
|
+
* THE POINT OF THIS FILE. Approving a `pr` proposal is the one action in the
|
|
6
|
+
* hub that reaches outside the machine: `core/inbox/apply.ts` builds a branch
|
|
7
|
+
* in a throwaway worktree and calls `createPr()`, which publishes that branch
|
|
8
|
+
* and opens a real pull request. The Approvals view names the repo and the
|
|
9
|
+
* kind. It does not name the branch it is about to create or the pull request
|
|
10
|
+
* it is about to open, so the operator learns both AFTER the click, from
|
|
11
|
+
* `proposal.result` — a sentence, produced by the thing they already approved.
|
|
12
|
+
* This module computes them BEFORE.
|
|
13
|
+
*
|
|
14
|
+
* ── WHAT APPLY ACTUALLY DOES (verified against src/core/inbox/apply.ts) ────
|
|
15
|
+
* • branch = `ashlr/proposal/${proposal.id}` (apply.ts:52, :125, :254)
|
|
16
|
+
* • createPr(repo, { title: proposal.title,
|
|
17
|
+
* body: proposal.summary,
|
|
18
|
+
* head: branch }) (apply.ts:257-261)
|
|
19
|
+
* • NO `base` is passed, so GitHub resolves the base to the repository's
|
|
20
|
+
* default branch. `predictPrPlan` reproduces that locally from
|
|
21
|
+
* `git symbolic-ref origin/HEAD` and labels it `baseSource: 'predicted'`,
|
|
22
|
+
* because it is our reading of gh's choice, not an instruction to gh.
|
|
23
|
+
* • NO `git push` runs. `gh pr create --head <branch>` publishes the branch
|
|
24
|
+
* as a side effect, which is why `publishesBranch` is true and stated.
|
|
25
|
+
*
|
|
26
|
+
* ── DRIFT ──────────────────────────────────────────────────────────────────
|
|
27
|
+
* `PROPOSAL_BRANCH_PREFIX` is module-private in apply.ts, so the literal is
|
|
28
|
+
* repeated here. test/verse-github-proposal.test.ts reads apply.ts and fails
|
|
29
|
+
* if the two ever diverge — a disclosure that predicts the wrong branch name
|
|
30
|
+
* is worse than none.
|
|
31
|
+
*
|
|
32
|
+
* READ-ONLY. NEVER THROWS. Opens nothing, pushes nothing, spawns only the
|
|
33
|
+
* `git` reads core/git.ts already performs.
|
|
34
|
+
*/
|
|
35
|
+
import { defaultBranch, resolveGitHubOriginAuthority } from '../git.js';
|
|
36
|
+
/**
|
|
37
|
+
* Mirrors `PROPOSAL_BRANCH_PREFIX` in src/core/inbox/apply.ts:52, which is not
|
|
38
|
+
* exported. Guarded against drift by test/verse-github-proposal.test.ts.
|
|
39
|
+
*/
|
|
40
|
+
export const VERSE_PROPOSAL_BRANCH_PREFIX = 'ashlr/proposal/';
|
|
41
|
+
const MAX_BODY_PREVIEW_CHARS = 400;
|
|
42
|
+
const MAX_REF_CHARS = 256;
|
|
43
|
+
const MAX_TITLE_CHARS = 512;
|
|
44
|
+
/**
|
|
45
|
+
* Only a real GitHub pull-request URL is ever surfaced as `prUrl`.
|
|
46
|
+
* `proposal.result` also carries gh's failure text on the unhappy path, and
|
|
47
|
+
* that text is not something to hand a UI as a link.
|
|
48
|
+
*/
|
|
49
|
+
const PR_URL_RE = /https:\/\/github\.com\/[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\/[A-Za-z0-9_.-]+\/pull\/\d+/;
|
|
50
|
+
const realGit = {
|
|
51
|
+
defaultBranch: (path) => defaultBranch(path),
|
|
52
|
+
nameWithOwner: (path) => resolveGitHubOriginAuthority(path),
|
|
53
|
+
};
|
|
54
|
+
/** The branch `applyPr` will create for this proposal id. */
|
|
55
|
+
export function verseProposalBranch(proposalId) {
|
|
56
|
+
return `${VERSE_PROPOSAL_BRANCH_PREFIX}${proposalId}`;
|
|
57
|
+
}
|
|
58
|
+
function boundedString(value, max) {
|
|
59
|
+
if (typeof value !== 'string')
|
|
60
|
+
return null;
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
if (trimmed.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
return trimmed.length > max ? trimmed.slice(0, max) : trimmed;
|
|
65
|
+
}
|
|
66
|
+
function extractPrUrl(...candidates) {
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (typeof candidate !== 'string')
|
|
69
|
+
continue;
|
|
70
|
+
const match = PR_URL_RE.exec(candidate);
|
|
71
|
+
if (match)
|
|
72
|
+
return match[0];
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function unavailable(proposal, detail) {
|
|
77
|
+
return {
|
|
78
|
+
proposalId: proposal.id,
|
|
79
|
+
state: 'unavailable',
|
|
80
|
+
kind: proposal.kind,
|
|
81
|
+
repoPath: proposal.repo ?? null,
|
|
82
|
+
nameWithOwner: null,
|
|
83
|
+
branch: null,
|
|
84
|
+
branchSource: 'unknown',
|
|
85
|
+
base: null,
|
|
86
|
+
baseSource: 'unknown',
|
|
87
|
+
title: null,
|
|
88
|
+
bodyPreview: null,
|
|
89
|
+
bodyTruncated: false,
|
|
90
|
+
prUrl: null,
|
|
91
|
+
publishesBranch: false,
|
|
92
|
+
detail,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Describe what approving this proposal will do — or, once it has been
|
|
97
|
+
* applied, what it did.
|
|
98
|
+
*
|
|
99
|
+
* Returns `state: 'unavailable'` (and no branch/base) for anything that is not
|
|
100
|
+
* an outward-facing `pr` proposal, so a caller can render the disclosure
|
|
101
|
+
* unconditionally and let the shape decide whether there is anything to show.
|
|
102
|
+
*/
|
|
103
|
+
export function describeVersePrPlan(proposal, opts = {}) {
|
|
104
|
+
const git = opts.git ?? realGit;
|
|
105
|
+
if (proposal.kind !== 'pr') {
|
|
106
|
+
return unavailable(proposal, `kind ${proposal.kind} opens no pull request`);
|
|
107
|
+
}
|
|
108
|
+
const repoPath = boundedString(proposal.repo, 4_096);
|
|
109
|
+
if (!repoPath) {
|
|
110
|
+
return unavailable(proposal, 'proposal carries no repository, so no pull request can be opened');
|
|
111
|
+
}
|
|
112
|
+
const handoff = proposal.remoteHandoff;
|
|
113
|
+
const intent = proposal.localMergeIntent;
|
|
114
|
+
// Recorded values win over predicted ones: once apply has run, the plan
|
|
115
|
+
// stops being a forecast and becomes a record of what happened.
|
|
116
|
+
const recordedBranch = boundedString(handoff?.branch, MAX_REF_CHARS)
|
|
117
|
+
?? boundedString(intent?.branch, MAX_REF_CHARS);
|
|
118
|
+
const branch = recordedBranch ?? verseProposalBranch(proposal.id);
|
|
119
|
+
const branchSource = recordedBranch ? 'recorded' : 'predicted';
|
|
120
|
+
const recordedBase = boundedString(handoff?.base, MAX_REF_CHARS)
|
|
121
|
+
?? boundedString(intent?.base, MAX_REF_CHARS);
|
|
122
|
+
let base = recordedBase;
|
|
123
|
+
let baseSource = recordedBase ? 'recorded' : 'unknown';
|
|
124
|
+
if (!base) {
|
|
125
|
+
try {
|
|
126
|
+
base = boundedString(git.defaultBranch(repoPath), MAX_REF_CHARS);
|
|
127
|
+
baseSource = base ? 'predicted' : 'unknown';
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
base = null;
|
|
131
|
+
baseSource = 'unknown';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
let nameWithOwner = null;
|
|
135
|
+
try {
|
|
136
|
+
nameWithOwner = boundedString(git.nameWithOwner(repoPath), 512);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
nameWithOwner = null;
|
|
140
|
+
}
|
|
141
|
+
const prUrl = extractPrUrl(handoff?.prUrl, proposal.result);
|
|
142
|
+
const title = boundedString(proposal.title, MAX_TITLE_CHARS);
|
|
143
|
+
const summary = typeof proposal.summary === 'string' ? proposal.summary.trim() : '';
|
|
144
|
+
const bodyTruncated = summary.length > MAX_BODY_PREVIEW_CHARS;
|
|
145
|
+
const bodyPreview = summary.length === 0
|
|
146
|
+
? null
|
|
147
|
+
: summary.slice(0, MAX_BODY_PREVIEW_CHARS);
|
|
148
|
+
const state = prUrl ? 'opened' : 'ready';
|
|
149
|
+
const target = nameWithOwner ?? repoPath;
|
|
150
|
+
const detail = state === 'opened'
|
|
151
|
+
? `Opened ${prUrl} from ${branch} into ${base ?? 'the default branch'} on ${target}.`
|
|
152
|
+
: `Approving pushes ${branch} to ${target} and opens a pull request into ${base ?? 'its default branch'}.`;
|
|
153
|
+
return {
|
|
154
|
+
proposalId: proposal.id,
|
|
155
|
+
state,
|
|
156
|
+
kind: proposal.kind,
|
|
157
|
+
repoPath,
|
|
158
|
+
nameWithOwner,
|
|
159
|
+
branch,
|
|
160
|
+
branchSource,
|
|
161
|
+
base,
|
|
162
|
+
baseSource,
|
|
163
|
+
title,
|
|
164
|
+
bodyPreview,
|
|
165
|
+
bodyTruncated,
|
|
166
|
+
prUrl,
|
|
167
|
+
// True for a plan that has not run yet: `gh pr create --head <branch>`
|
|
168
|
+
// publishes the branch. Once the PR exists the push has already happened.
|
|
169
|
+
publishesBranch: state === 'ready',
|
|
170
|
+
detail,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/** Plans for several proposals, in the order given. Never throws. */
|
|
174
|
+
export function describeVersePrPlans(proposals, opts = {}) {
|
|
175
|
+
const out = [];
|
|
176
|
+
for (const proposal of proposals) {
|
|
177
|
+
try {
|
|
178
|
+
out.push(describeVersePrPlan(proposal, opts));
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
out.push(unavailable(proposal, 'could not describe this proposal'));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=github-proposal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-proposal.js","sourceRoot":"","sources":["../../../src/core/verse/github-proposal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,aAAa,EAAE,4BAA4B,EAAE,MAAM,WAAW,CAAC;AAQxE;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,iBAAiB,CAAC;AAE9D,MAAM,sBAAsB,GAAG,GAAG,CAAC;AACnC,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B;;;;GAIG;AACH,MAAM,SAAS,GAAG,6FAA6F,CAAC;AAmBhH,MAAM,OAAO,GAAwB;IACnC,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,4BAA4B,CAAC,IAAI,CAAC;CAC5D,CAAC;AAEF,6DAA6D;AAC7D,MAAM,UAAU,mBAAmB,CAAC,UAAkB;IACpD,OAAO,GAAG,4BAA4B,GAAG,UAAU,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,aAAa,CAAC,KAAc,EAAE,GAAW;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAChE,CAAC;AAED,SAAS,YAAY,CAAC,GAAG,UAA8B;IACrD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,OAAO,SAAS,KAAK,QAAQ;YAAE,SAAS;QAC5C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,WAAW,CAClB,QAA6B,EAC7B,MAAc;IAEd,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC/B,aAAa,EAAE,IAAI;QACnB,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,SAAS;QACvB,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,SAAS;QACrB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,IAAI;QACX,eAAe,EAAE,KAAK;QACtB,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA6B,EAC7B,OAA2B,EAAE;IAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC;IAEhC,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,WAAW,CAAC,QAAQ,EAAE,QAAQ,QAAQ,CAAC,IAAI,wBAAwB,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,WAAW,CAAC,QAAQ,EAAE,kEAAkE,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,gBAAgB,CAAC;IAEzC,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC;WAC/D,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,cAAc,IAAI,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAClE,MAAM,YAAY,GAA4B,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;IAExF,MAAM,YAAY,GAAG,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC;WAC3D,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;IAChD,IAAI,IAAI,GAAG,YAAY,CAAC;IACxB,IAAI,UAAU,GAA4B,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAChF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC;YACH,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC;YACjE,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,GAAG,IAAI,CAAC;YACZ,UAAU,GAAG,SAAS,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,CAAC;QACH,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE5D,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,sBAAsB,CAAC;IAC9D,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC;QACtC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAE7C,MAAM,KAAK,GAA2B,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IACjE,MAAM,MAAM,GAAG,aAAa,IAAI,QAAQ,CAAC;IAEzC,MAAM,MAAM,GAAG,KAAK,KAAK,QAAQ;QAC/B,CAAC,CAAC,UAAU,KAAK,SAAS,MAAM,SAAS,IAAI,IAAI,oBAAoB,OAAO,MAAM,GAAG;QACrF,CAAC,CAAC,oBAAoB,MAAM,OAAO,MAAM,kCAAkC,IAAI,IAAI,oBAAoB,GAAG,CAAC;IAE7G,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,KAAK;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ;QACR,aAAa;QACb,MAAM;QACN,YAAY;QACZ,IAAI;QACJ,UAAU;QACV,KAAK;QACL,WAAW;QACX,aAAa;QACb,KAAK;QACL,uEAAuE;QACvE,0EAA0E;QAC1E,eAAe,EAAE,KAAK,KAAK,OAAO;QAClC,MAAM;KACP,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,oBAAoB,CAClC,SAAyC,EACzC,OAA2B,EAAE;IAE7B,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,kCAAkC,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/verse/github-repo.ts — per-root GitHub surfacing for Ashlr Verse
|
|
3
|
+
* (docs/VERSE-WORKSPACES.md §2). READ-ONLY. NEVER THROWS.
|
|
4
|
+
*
|
|
5
|
+
* ── AUTH ───────────────────────────────────────────────────────────────────
|
|
6
|
+
* The `gh` CLI owns its own credentials, exactly as core/integrations/github.ts
|
|
7
|
+
* does. This module spawns the same binary with the same env hygiene and holds
|
|
8
|
+
* NO token of its own. There is no second credential path here, and no code
|
|
9
|
+
* path in this file reads, stores, or emits one.
|
|
10
|
+
*
|
|
11
|
+
* ── WHY IT SPAWNS `gh` AT ALL, GIVEN github.ts EXISTS ──────────────────────
|
|
12
|
+
* github.ts does the hard part and this module reuses its conventions
|
|
13
|
+
* verbatim (same binary, same 8 s timeout, same env, same never-throw
|
|
14
|
+
* contract). It does not reuse its two list functions, for two reasons that
|
|
15
|
+
* are contract-level rather than stylistic:
|
|
16
|
+
*
|
|
17
|
+
* 1. CI STATE. `listPrs()` requests `number,title,url,state,author` and
|
|
18
|
+
* `PrSummary` has no checks field; `githubStatus().ci` is repo-wide,
|
|
19
|
+
* derived from the last five *workflow runs*, and cannot say which PR is
|
|
20
|
+
* red. §2 asks for "open PRs and their CI state", which needs
|
|
21
|
+
* `statusCheckRollup` on the PR query. github.ts exports no runner to
|
|
22
|
+
* borrow (`runGh` is private), so the query is issued here.
|
|
23
|
+
* 2. FAILURE vs EMPTY. `listPrs()`/`listIssues()` return `[]` both when gh
|
|
24
|
+
* fails and when nothing is open. An operator staring at an empty panel
|
|
25
|
+
* must be able to tell "nothing to do" from "could not look", so this
|
|
26
|
+
* module keeps gh's null and projects it as `prsAvailable` /
|
|
27
|
+
* `issuesAvailable`.
|
|
28
|
+
*
|
|
29
|
+
* Two `gh` calls per root, both list reads. Remote identity and default branch
|
|
30
|
+
* come from core/git.ts and cost no network at all.
|
|
31
|
+
*
|
|
32
|
+
* ── NO URL LEAVES THIS MODULE ──────────────────────────────────────────────
|
|
33
|
+
* `resolveGitHubOriginAuthorityDetails()` returns fetch/push URLs, and a
|
|
34
|
+
* supported GitHub HTTPS transport may carry credentials in its userinfo. Only
|
|
35
|
+
* `nameWithOwner` is read, so no remote URL can reach a response, a log, or an
|
|
36
|
+
* SSE frame. `gh`'s own stderr is never surfaced either — a failure becomes a
|
|
37
|
+
* fixed sentence, not a passthrough of whatever the tool printed.
|
|
38
|
+
*/
|
|
39
|
+
import type { VerseGithubChecks, VerseGithubIssue, VerseGithubPr, VerseGithubRepoSnapshot, VerseGithubSnapshot } from './github-types.js';
|
|
40
|
+
/** Run `gh` in `cwd`; trimmed stdout, or null on ANY failure. Never throws. */
|
|
41
|
+
export type VerseGithubGhRunner = (cwd: string, args: readonly string[]) => string | null;
|
|
42
|
+
/** The git facts this module needs. Defaults to core/git.ts. */
|
|
43
|
+
export interface VerseGithubGitProbe {
|
|
44
|
+
isRepo(path: string): boolean;
|
|
45
|
+
defaultBranch(path: string): string;
|
|
46
|
+
/** Lowercase `owner/name`, or null when origin does not resolve to one GitHub repo. */
|
|
47
|
+
nameWithOwner(path: string): string | null;
|
|
48
|
+
}
|
|
49
|
+
export interface VerseGithubReadOptions {
|
|
50
|
+
gh?: VerseGithubGhRunner;
|
|
51
|
+
git?: VerseGithubGitProbe;
|
|
52
|
+
/** 1..100, default 20. */
|
|
53
|
+
prLimit?: number;
|
|
54
|
+
/** 1..100, default 20. */
|
|
55
|
+
issueLimit?: number;
|
|
56
|
+
/** Clock seam for tests. */
|
|
57
|
+
now?: () => Date;
|
|
58
|
+
/** Directory existence seam for tests. */
|
|
59
|
+
isDirectory?: (path: string) => boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Invoke `gh` for the PR and issue lists. Default true.
|
|
62
|
+
*
|
|
63
|
+
* `gh` is a synchronous subprocess with an 8 s ceiling, and the web server
|
|
64
|
+
* is single-threaded: reading N roots' lists in one request blocks it for up
|
|
65
|
+
* to N x 2 x 8 s. A multi-root listing therefore passes false and returns
|
|
66
|
+
* identity only (`listsRequested: false`), which costs nothing but two local
|
|
67
|
+
* `git` reads per root.
|
|
68
|
+
*/
|
|
69
|
+
includeLists?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Summarise one PR's `statusCheckRollup`.
|
|
73
|
+
*
|
|
74
|
+
* Handles both GraphQL variants gh emits: `CheckRun` (`status`/`conclusion`)
|
|
75
|
+
* and `StatusContext` (`state`). Entries of neither shape are counted as
|
|
76
|
+
* pending rather than silently dropped — an unrecognised check is not a
|
|
77
|
+
* passing one.
|
|
78
|
+
*
|
|
79
|
+
* FAILING WINS OVER PENDING, deliberately diverging from
|
|
80
|
+
* `resolveCiStatus()`'s first-match-wins scan in github.ts: that function
|
|
81
|
+
* summarises the last five workflow *runs* of a repo, where "something is
|
|
82
|
+
* running" is the useful headline. Here the operator is deciding whether to
|
|
83
|
+
* touch a specific PR, and a red check must not be hidden behind a queued one.
|
|
84
|
+
*/
|
|
85
|
+
export declare function summarizeCheckRollup(rollup: unknown): VerseGithubChecks;
|
|
86
|
+
/**
|
|
87
|
+
* Parse `gh pr list --json <PR_JSON_FIELDS>` output.
|
|
88
|
+
*
|
|
89
|
+
* Malformed entries are DROPPED, not defaulted — the same choice
|
|
90
|
+
* `listIssues()` makes and the opposite of `listPrs()`, which coerces a
|
|
91
|
+
* missing number to 0. A PR row that cannot name its own number or URL is not
|
|
92
|
+
* something an operator should be offered a link to.
|
|
93
|
+
*/
|
|
94
|
+
export declare function parsePrList(parsed: unknown): VerseGithubPr[];
|
|
95
|
+
/** Parse `gh issue list --json <ISSUE_JSON_FIELDS>`. Malformed entries dropped. */
|
|
96
|
+
export declare function parseIssueList(parsed: unknown): VerseGithubIssue[];
|
|
97
|
+
/**
|
|
98
|
+
* Read one workspace root. Never throws, never returns a remote URL, and never
|
|
99
|
+
* mutates anything — the only subprocesses are `git` reads via core/git.ts and
|
|
100
|
+
* two `gh` list reads.
|
|
101
|
+
*/
|
|
102
|
+
export declare function readVerseGithubRepo(path: string, opts?: VerseGithubReadOptions): VerseGithubRepoSnapshot;
|
|
103
|
+
/** Read several roots in order, de-duplicated by path. Never throws. */
|
|
104
|
+
export declare function readVerseGithubSnapshot(paths: readonly string[], opts?: VerseGithubReadOptions): VerseGithubSnapshot;
|