@h-rig/server 0.0.6-alpha.3 → 0.0.6-alpha.31

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.
@@ -3973,21 +3973,10 @@ async function runInspectorLocalReview(projectRoot, input) {
3973
3973
  details: null
3974
3974
  };
3975
3975
  }
3976
- const [{ RuntimeEventBus }, { PluginManager }, { verifyTask }] = await Promise.all([
3977
- import("@rig/runtime/control-plane/runtime/events"),
3978
- import("@rig/runtime/control-plane/runtime/plugins"),
3979
- import("@rig/runtime/control-plane/native/verifier")
3980
- ]);
3981
- const eventBus = new RuntimeEventBus({ projectRoot, runId: `inspector-review:${taskId}` });
3982
- const plugins = await PluginManager.load({
3983
- projectRoot,
3984
- runId: `inspector-review:${taskId}`,
3985
- eventBus
3986
- });
3976
+ const { verifyTask } = await import("@rig/runtime/control-plane/native/verifier");
3987
3977
  const outcome = await verifyTask({
3988
3978
  projectRoot,
3989
3979
  taskId,
3990
- plugins,
3991
3980
  skipAiReview: true
3992
3981
  });
3993
3982
  return {
@@ -1,6 +1,5 @@
1
1
  // @bun
2
2
  // packages/server/src/server-helpers/issue-analysis.ts
3
- import { execFile } from "child_process";
4
3
  import { createHash } from "crypto";
5
4
  function stableIssueHash(issue) {
6
5
  const labels = Array.isArray(issue.labels) ? [...issue.labels].map(String).sort() : [];
@@ -134,16 +133,33 @@ function parseIssueAnalysisResult(raw) {
134
133
  return result;
135
134
  }
136
135
  function createDefaultPiIssueAnalysisCommandRunner() {
137
- return (command, args, options) => new Promise((resolve) => {
138
- execFile(command, [...args], {
139
- timeout: options.timeoutMs,
140
- maxBuffer: 10 * 1024 * 1024,
141
- env: options.env ? { ...process.env, ...options.env } : process.env
142
- }, (error, stdout, stderr) => {
143
- const exitCode = typeof error?.code === "number" ? error.code : error ? 1 : 0;
144
- resolve({ exitCode, stdout: String(stdout ?? ""), stderr: String(stderr ?? "") });
136
+ return async (command, args, options) => {
137
+ const env = options.env ? { ...process.env, ...options.env } : process.env;
138
+ const proc = Bun.spawn([command, ...args], {
139
+ stdout: "pipe",
140
+ stderr: "pipe",
141
+ env
145
142
  });
146
- });
143
+ let timedOut = false;
144
+ const timer = setTimeout(() => {
145
+ timedOut = true;
146
+ proc.kill();
147
+ }, options.timeoutMs);
148
+ try {
149
+ const [stdout, stderr, exitCode] = await Promise.all([
150
+ new Response(proc.stdout).text(),
151
+ new Response(proc.stderr).text(),
152
+ proc.exited
153
+ ]);
154
+ return {
155
+ exitCode: timedOut && exitCode === 0 ? 1 : exitCode,
156
+ stdout,
157
+ stderr: timedOut && stderr.trim().length === 0 ? `Pi issue analysis timed out after ${options.timeoutMs}ms` : stderr
158
+ };
159
+ } finally {
160
+ clearTimeout(timer);
161
+ }
162
+ };
147
163
  }
148
164
  function createPiIssueAnalyzer(input = {}) {
149
165
  const piBinary = input.piBinary ?? process.env.RIG_ISSUE_ANALYSIS_PI_BINARY ?? "pi";
@@ -0,0 +1,84 @@
1
+ // @bun
2
+ // packages/server/src/server-helpers/pi-session-proxy.ts
3
+ import { readAuthorityRun as readAuthorityRun2 } from "@rig/runtime/control-plane/authority-files";
4
+
5
+ // packages/server/src/server-helpers/run-io.ts
6
+ import {
7
+ listAuthorityRuns,
8
+ readAuthorityRun,
9
+ readJsonlFile,
10
+ resolveAuthorityRunDir
11
+ } from "@rig/runtime/control-plane/authority-files";
12
+ function readRunPiSessionMetadata(projectRoot, runId) {
13
+ const run = readAuthorityRun(projectRoot, runId);
14
+ const metadata = run?.piSessionPrivate;
15
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
16
+ return null;
17
+ const record = metadata;
18
+ const publicMetadata = record.public;
19
+ const daemonConnection = record.daemonConnection;
20
+ if (!publicMetadata || typeof publicMetadata !== "object" || Array.isArray(publicMetadata))
21
+ return null;
22
+ if (!daemonConnection || typeof daemonConnection !== "object" || Array.isArray(daemonConnection))
23
+ return null;
24
+ return metadata;
25
+ }
26
+ var INITIAL_RUN_LOG_TAIL_MAX_BYTES = 8 * 1024 * 1024;
27
+
28
+ // packages/server/src/server-helpers/pi-session-proxy.ts
29
+ function resolveRunPiSessionProxy(projectRoot, runId) {
30
+ const run = readAuthorityRun2(projectRoot, runId);
31
+ if (!run)
32
+ return null;
33
+ const privateMetadata = readRunPiSessionMetadata(projectRoot, runId);
34
+ if (!privateMetadata)
35
+ return { pending: true, runId, status: typeof run.status === "string" ? run.status : undefined };
36
+ const connection = privateMetadata.daemonConnection;
37
+ if (connection.mode !== "http")
38
+ throw new Error("Only loopback HTTP Rig Pi session daemon connections are supported");
39
+ const token = tokenFromRef(connection.tokenRef);
40
+ if (!token)
41
+ throw new Error("Rig Pi session daemon token is unavailable");
42
+ return {
43
+ runId,
44
+ sessionId: privateMetadata.public.sessionId,
45
+ metadata: privateMetadata.public,
46
+ privateMetadata,
47
+ baseUrl: connection.baseUrl.replace(/\/+$/, ""),
48
+ token
49
+ };
50
+ }
51
+ async function proxyRunPiHttp(projectRoot, runId, input) {
52
+ const resolved = resolveRunPiSessionProxy(projectRoot, runId);
53
+ if (resolved === null)
54
+ return { status: 404, payload: { ok: false, error: "Run not found" } };
55
+ if ("pending" in resolved)
56
+ return { status: 409, payload: { ready: false, runId, status: resolved.status, retryAfterMs: 500 } };
57
+ const response = await fetch(`${resolved.baseUrl}${input.daemonPath}`, {
58
+ method: input.method,
59
+ headers: {
60
+ authorization: `Bearer ${resolved.token}`,
61
+ ...input.body === undefined ? {} : { "content-type": "application/json" }
62
+ },
63
+ body: input.body === undefined ? undefined : JSON.stringify(input.body)
64
+ });
65
+ const text = await response.text();
66
+ const payload = text.trim() ? JSON.parse(text) : null;
67
+ return { status: response.status, payload };
68
+ }
69
+ function buildRunPiDaemonWebSocketUrl(resolved) {
70
+ const url = new URL(`${resolved.baseUrl}/sessions/${encodeURIComponent(resolved.sessionId)}/events`);
71
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
72
+ url.searchParams.set("token", resolved.token);
73
+ return url.toString();
74
+ }
75
+ function tokenFromRef(ref) {
76
+ if (ref.startsWith("inline:"))
77
+ return ref.slice("inline:".length) || null;
78
+ return null;
79
+ }
80
+ export {
81
+ resolveRunPiSessionProxy,
82
+ proxyRunPiHttp,
83
+ buildRunPiDaemonWebSocketUrl
84
+ };
@@ -115,8 +115,13 @@ function upsertProjectRecord(projectRoot, input) {
115
115
  function linkProjectCheckout(projectRoot, repoSlug, checkout) {
116
116
  return upsertProjectRecord(projectRoot, { repoSlug, checkout });
117
117
  }
118
+ function projectRegistryContainsCheckout(projectRoot, checkoutPath) {
119
+ const target = resolve(checkoutPath);
120
+ return Object.values(readRegistry(projectRoot)).some((project) => project.checkouts.some((checkout) => checkout.path ? resolve(checkout.path) === target : false));
121
+ }
118
122
  export {
119
123
  upsertProjectRecord,
124
+ projectRegistryContainsCheckout,
120
125
  normalizeRepoSlug,
121
126
  linkProjectCheckout,
122
127
  getProjectRecord,
@@ -114,14 +114,29 @@ function summarizeUsefulRunError(projectRoot, runId, fallback) {
114
114
  const nonGeneric = errorLines.at(-1);
115
115
  return nonGeneric ?? (typeof fallback === "string" ? fallback : null);
116
116
  }
117
+ function readRunPiSessionMetadata(projectRoot, runId) {
118
+ const run = readAuthorityRun(projectRoot, runId);
119
+ const metadata = run?.piSessionPrivate;
120
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
121
+ return null;
122
+ const record = metadata;
123
+ const publicMetadata = record.public;
124
+ const daemonConnection = record.daemonConnection;
125
+ if (!publicMetadata || typeof publicMetadata !== "object" || Array.isArray(publicMetadata))
126
+ return null;
127
+ if (!daemonConnection || typeof daemonConnection !== "object" || Array.isArray(daemonConnection))
128
+ return null;
129
+ return metadata;
130
+ }
117
131
  function readRunDetails(projectRoot, runId) {
118
132
  const run = readAuthorityRun(projectRoot, runId);
119
133
  if (!run) {
120
134
  return null;
121
135
  }
122
136
  const usefulErrorText = isGenericRunFailure(run.errorText) ? summarizeUsefulRunError(projectRoot, runId, run.errorText) : null;
137
+ const { piSessionPrivate: _piSessionPrivate, ...publicRun } = run;
123
138
  return {
124
- run: usefulErrorText ? { ...run, errorText: usefulErrorText } : run,
139
+ run: usefulErrorText ? { ...publicRun, errorText: usefulErrorText } : publicRun,
125
140
  timeline: readJsonlFile(resolve(resolveAuthorityRunDir(projectRoot, runId), "timeline.jsonl")),
126
141
  approvals: readApprovals(projectRoot, { runId }),
127
142
  userInputs: readUserInputs(projectRoot, { runId })
@@ -167,6 +182,18 @@ function readJsonlFileTail(path, options) {
167
182
  function readRawRunLogs(projectRoot, runId) {
168
183
  return readJsonlFile(runLogsPath(projectRoot, runId)).filter((entry) => Boolean(entry && typeof entry === "object"));
169
184
  }
185
+ async function readRunTimelinePage(projectRoot, runId, options = {}) {
186
+ const limit = Math.max(1, Math.min(Math.trunc(options.limit ?? 200), 500));
187
+ const cursor = options.cursor == null ? 0 : Number.parseInt(options.cursor, 10);
188
+ const entries = readJsonlFile(runTimelinePath(projectRoot, runId)).filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
189
+ const startInclusive = Number.isFinite(cursor) ? Math.max(0, Math.min(cursor, entries.length)) : 0;
190
+ const endExclusive = Math.min(entries.length, startInclusive + limit);
191
+ return {
192
+ entries: entries.slice(startInclusive, endExclusive).map((entry, offset) => ({ ...entry, cursor: startInclusive + offset + 1 })),
193
+ nextCursor: String(endExclusive),
194
+ hasMore: endExclusive < entries.length
195
+ };
196
+ }
170
197
  var INITIAL_RUN_LOG_TAIL_MAX_BYTES = 8 * 1024 * 1024;
171
198
  async function readRunLogsPage(projectRoot, runId, options = {}) {
172
199
  const limit = Math.max(1, Math.min(Math.trunc(options.limit ?? 200), 500));
@@ -252,6 +279,8 @@ export {
252
279
  remoteArtifactsRoot,
253
280
  readUserInputsForRuns,
254
281
  readUserInputs,
282
+ readRunTimelinePage,
283
+ readRunPiSessionMetadata,
255
284
  readRunLogsPage,
256
285
  readRunDetails,
257
286
  readRawRunLogs,