@h-rig/rig-host 0.0.6-alpha.100
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 +1 -0
- package/dist/bin/rig-run.d.ts +2 -0
- package/dist/bin/rig-run.js +284 -0
- package/dist/src/cli-entry.d.ts +1 -0
- package/dist/src/cli-entry.js +476 -0
- package/dist/src/domain/approval.d.ts +1 -0
- package/dist/src/domain/approval.js +9 -0
- package/dist/src/domain/input.d.ts +1 -0
- package/dist/src/domain/input.js +9 -0
- package/dist/src/domain/run.d.ts +28 -0
- package/dist/src/domain/run.js +176 -0
- package/dist/src/domain/task.d.ts +4 -0
- package/dist/src/domain/task.js +94 -0
- package/dist/src/domain/workspace.d.ts +4 -0
- package/dist/src/domain/workspace.js +183 -0
- package/dist/src/host-session.d.ts +47 -0
- package/dist/src/host-session.js +355 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +685 -0
- package/dist/src/local-omp-runner.d.ts +52 -0
- package/dist/src/local-omp-runner.js +185 -0
- package/dist/src/operator-entry.d.ts +7 -0
- package/dist/src/operator-entry.js +79 -0
- package/dist/src/protocol.d.ts +123 -0
- package/dist/src/protocol.js +8 -0
- package/dist/src/pty-spawn.d.ts +25 -0
- package/dist/src/pty-spawn.js +34 -0
- package/dist/src/replication/broadcast.d.ts +13 -0
- package/dist/src/replication/broadcast.js +63 -0
- package/dist/src/replication/replay.d.ts +12 -0
- package/dist/src/replication/replay.js +25 -0
- package/dist/src/replication/welcome.d.ts +3 -0
- package/dist/src/replication/welcome.js +45 -0
- package/package.json +39 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/rig-host/src/host-session.ts
|
|
3
|
+
import { resolve as resolve3 } from "path";
|
|
4
|
+
|
|
5
|
+
// packages/rig-host/src/domain/run.ts
|
|
6
|
+
import { basename, resolve as resolve2 } from "path";
|
|
7
|
+
|
|
8
|
+
// packages/rig-host/src/domain/task.ts
|
|
9
|
+
import { resolve } from "path";
|
|
10
|
+
import { createPluginHost } from "@rig/core";
|
|
11
|
+
import { loadConfig } from "@rig/core/load-config";
|
|
12
|
+
function stringList(value) {
|
|
13
|
+
if (!Array.isArray(value))
|
|
14
|
+
return [];
|
|
15
|
+
return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
16
|
+
}
|
|
17
|
+
function stringOrNull(value) {
|
|
18
|
+
if (typeof value !== "string")
|
|
19
|
+
return null;
|
|
20
|
+
const trimmed = value.trim();
|
|
21
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
22
|
+
}
|
|
23
|
+
function titleFor(record) {
|
|
24
|
+
return stringOrNull(record.title) ?? stringOrNull(record.name) ?? record.id;
|
|
25
|
+
}
|
|
26
|
+
function descriptionFor(record) {
|
|
27
|
+
return stringOrNull(record.description) ?? stringOrNull(record.body) ?? "";
|
|
28
|
+
}
|
|
29
|
+
function scopeFor(record) {
|
|
30
|
+
const direct = stringList(record.scope);
|
|
31
|
+
if (direct.length > 0)
|
|
32
|
+
return direct;
|
|
33
|
+
return stringList(record.labels).filter((label) => label.startsWith("scope:")).map((label) => label.slice("scope:".length).trim()).filter((label) => label.length > 0);
|
|
34
|
+
}
|
|
35
|
+
function validationKeysFor(record) {
|
|
36
|
+
const direct = stringList(record.validationKeys);
|
|
37
|
+
if (direct.length > 0)
|
|
38
|
+
return direct;
|
|
39
|
+
const validation = stringList(record.validation);
|
|
40
|
+
if (validation.length > 0)
|
|
41
|
+
return validation;
|
|
42
|
+
return stringList(record.labels).filter((label) => label.startsWith("validator:")).map((label) => label.slice("validator:".length).trim()).filter((label) => label.length > 0);
|
|
43
|
+
}
|
|
44
|
+
function createdAtFor(record, fallback) {
|
|
45
|
+
return stringOrNull(record.createdAt) ?? fallback;
|
|
46
|
+
}
|
|
47
|
+
function updatedAtFor(record, fallback) {
|
|
48
|
+
return stringOrNull(record.updatedAt) ?? createdAtFor(record, fallback) ?? fallback;
|
|
49
|
+
}
|
|
50
|
+
function mapTaskRecordToSummary(record, workspaceId, fallbackTimestamp) {
|
|
51
|
+
const metadata = record;
|
|
52
|
+
const createdAt = createdAtFor(record, fallbackTimestamp);
|
|
53
|
+
const updatedAt = updatedAtFor(record, fallbackTimestamp);
|
|
54
|
+
return {
|
|
55
|
+
id: record.id,
|
|
56
|
+
workspaceId,
|
|
57
|
+
graphId: null,
|
|
58
|
+
externalId: stringOrNull(metadata.externalId) ?? stringOrNull(metadata.externalRef),
|
|
59
|
+
title: titleFor(record),
|
|
60
|
+
description: descriptionFor(record),
|
|
61
|
+
status: record.status,
|
|
62
|
+
priority: typeof metadata.priority === "number" && Number.isInteger(metadata.priority) && metadata.priority > 0 ? metadata.priority : null,
|
|
63
|
+
role: stringOrNull(metadata.role),
|
|
64
|
+
scope: scopeFor(record),
|
|
65
|
+
validationKeys: validationKeysFor(record),
|
|
66
|
+
sourceIssueId: stringOrNull(metadata.sourceIssueId),
|
|
67
|
+
dependencies: [...record.deps],
|
|
68
|
+
parentChildDeps: [],
|
|
69
|
+
metadata,
|
|
70
|
+
createdAt,
|
|
71
|
+
updatedAt
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function loadRigIdeaTasks(workspaceRoot) {
|
|
75
|
+
const normalizedRoot = resolve(workspaceRoot);
|
|
76
|
+
const config = await loadConfig(normalizedRoot);
|
|
77
|
+
const pluginHost = createPluginHost(config.plugins);
|
|
78
|
+
const taskSourceFactory = pluginHost.resolveTaskSourceFactoryByKind(config.taskSource.kind);
|
|
79
|
+
if (!taskSourceFactory) {
|
|
80
|
+
const kinds = pluginHost.listExecutableTaskSources().map((entry) => entry.kind).join(", ") || "none";
|
|
81
|
+
throw new Error(`No task source factory registered for kind "${config.taskSource.kind}". Registered kinds: ${kinds}.`);
|
|
82
|
+
}
|
|
83
|
+
const source = taskSourceFactory.factory(config.taskSource, { projectRoot: normalizedRoot });
|
|
84
|
+
const fallbackTimestamp = new Date().toISOString();
|
|
85
|
+
const workspaceId = normalizedRoot;
|
|
86
|
+
const tasks = await source.list();
|
|
87
|
+
return tasks.map((task) => mapTaskRecordToSummary(task, workspaceId, fallbackTimestamp));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// packages/rig-host/src/domain/run.ts
|
|
91
|
+
var LEGACY_AUTHORITY_RUN_CREATION_RETIRED_MESSAGE = "Legacy rig-host run-record creation is retired for the product path. Use the default Rig OMP extension/collab session flow instead.";
|
|
92
|
+
var LEGACY_AUTHORITY_RUN_CONTROL_RETIRED_MESSAGE = "Legacy rig-host run-record control, inspection, and active-run state are retired for the product path. Use the default Rig OMP extension/collab session flow instead.";
|
|
93
|
+
function legacyAuthorityRunUnsupportedMessage() {
|
|
94
|
+
return LEGACY_AUTHORITY_RUN_CREATION_RETIRED_MESSAGE;
|
|
95
|
+
}
|
|
96
|
+
function legacyAuthorityRunControlUnsupportedMessage() {
|
|
97
|
+
return LEGACY_AUTHORITY_RUN_CONTROL_RETIRED_MESSAGE;
|
|
98
|
+
}
|
|
99
|
+
function legacyAuthorityRunControlUnsupportedError() {
|
|
100
|
+
return new Error(legacyAuthorityRunControlUnsupportedMessage());
|
|
101
|
+
}
|
|
102
|
+
function projectRetiredLegacyRigIdeaWorkspace(workspaceRoot, tasks) {
|
|
103
|
+
const normalizedRoot = resolve2(workspaceRoot);
|
|
104
|
+
const updatedAt = new Date().toISOString();
|
|
105
|
+
const title = basename(normalizedRoot) || normalizedRoot;
|
|
106
|
+
return {
|
|
107
|
+
workspaceRoot: normalizedRoot,
|
|
108
|
+
activeRunId: null,
|
|
109
|
+
sequence: 0,
|
|
110
|
+
workspaces: [{
|
|
111
|
+
id: normalizedRoot,
|
|
112
|
+
title,
|
|
113
|
+
rootPath: normalizedRoot,
|
|
114
|
+
sourceKind: "native",
|
|
115
|
+
defaultModel: null,
|
|
116
|
+
createdAt: updatedAt,
|
|
117
|
+
updatedAt
|
|
118
|
+
}],
|
|
119
|
+
graphs: [],
|
|
120
|
+
tasks: [...tasks],
|
|
121
|
+
runs: [],
|
|
122
|
+
runtimes: [],
|
|
123
|
+
conversations: [],
|
|
124
|
+
messages: [],
|
|
125
|
+
actions: [],
|
|
126
|
+
logs: [],
|
|
127
|
+
approvals: [],
|
|
128
|
+
userInputs: [],
|
|
129
|
+
validations: [],
|
|
130
|
+
reviews: [],
|
|
131
|
+
artifacts: [],
|
|
132
|
+
policyDecisions: [],
|
|
133
|
+
queue: [],
|
|
134
|
+
worktrees: [],
|
|
135
|
+
remoteEndpoints: [],
|
|
136
|
+
remoteConnections: [],
|
|
137
|
+
remoteOrchestrations: [],
|
|
138
|
+
updatedAt
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function createRigIdeaHarness(workspaceRoot) {
|
|
142
|
+
const normalizedRoot = resolve2(workspaceRoot);
|
|
143
|
+
const tasks = await loadRigIdeaTasks(normalizedRoot).catch(() => []);
|
|
144
|
+
return {
|
|
145
|
+
snapshot() {
|
|
146
|
+
return projectRetiredLegacyRigIdeaWorkspace(normalizedRoot, tasks);
|
|
147
|
+
},
|
|
148
|
+
async stopRun(_runId) {
|
|
149
|
+
throw legacyAuthorityRunControlUnsupportedError();
|
|
150
|
+
},
|
|
151
|
+
async resolveApproval(_runId, _requestId, _decision) {
|
|
152
|
+
throw legacyAuthorityRunControlUnsupportedError();
|
|
153
|
+
},
|
|
154
|
+
async resolveUserInput(_runId, _requestId, _answers) {
|
|
155
|
+
throw legacyAuthorityRunControlUnsupportedError();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// packages/rig-host/src/replication/broadcast.ts
|
|
161
|
+
function createRigHostBroadcaster(listener, collab) {
|
|
162
|
+
const backlog = [];
|
|
163
|
+
const welcomedPeers = new Set;
|
|
164
|
+
const hasWelcomedRelayPeer = () => {
|
|
165
|
+
for (const peer of welcomedPeers) {
|
|
166
|
+
if (peer !== 0)
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
};
|
|
171
|
+
const emit = (frame, targetPeer = 0) => {
|
|
172
|
+
listener?.(frame);
|
|
173
|
+
collab?.send(frame, targetPeer);
|
|
174
|
+
};
|
|
175
|
+
const flush = (targetPeer) => {
|
|
176
|
+
let write = 0;
|
|
177
|
+
for (let read = 0;read < backlog.length; read += 1) {
|
|
178
|
+
const next = backlog[read];
|
|
179
|
+
if (next.targetPeer === targetPeer) {
|
|
180
|
+
emit(next.frame, next.targetPeer);
|
|
181
|
+
} else {
|
|
182
|
+
backlog[write] = next;
|
|
183
|
+
write += 1;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
backlog.length = write;
|
|
187
|
+
};
|
|
188
|
+
return {
|
|
189
|
+
publish(frame, targetPeer = 0) {
|
|
190
|
+
if (frame.t === "welcome") {
|
|
191
|
+
welcomedPeers.add(targetPeer);
|
|
192
|
+
emit(frame, targetPeer);
|
|
193
|
+
flush(targetPeer);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!welcomedPeers.has(targetPeer) && frame.t !== "bye" && frame.t !== "error") {
|
|
197
|
+
if (targetPeer === 0) {
|
|
198
|
+
if (hasWelcomedRelayPeer())
|
|
199
|
+
emit(frame, targetPeer);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
backlog.push({ frame, targetPeer });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
emit(frame, targetPeer);
|
|
206
|
+
},
|
|
207
|
+
publishWelcome(snapshot, readOnly, targetPeer) {
|
|
208
|
+
this.publish({ t: "welcome", snapshot, readOnly }, targetPeer);
|
|
209
|
+
},
|
|
210
|
+
rejectReadOnly(action, targetPeer) {
|
|
211
|
+
this.publish({
|
|
212
|
+
t: "error",
|
|
213
|
+
code: "READ_ONLY",
|
|
214
|
+
message: `${action} is disabled on a read-only link`
|
|
215
|
+
}, targetPeer);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// packages/rig-host/src/host-session.ts
|
|
221
|
+
var LOCAL_PEER_ID = 0;
|
|
222
|
+
var RIG_GENERIC_OMP_JOIN_LIMITATION = "Legacy rig-host remote join and attachment are retired for the product path. Use the default Rig OMP extension/collab session flow instead.";
|
|
223
|
+
var RIG_RUN_SCOPE_LIMITATION = "Legacy rig-host selected-run remote join is retired for the product path. Selected-run discovery now belongs to OMP collab; use the default Rig OMP extension/collab session flow instead.";
|
|
224
|
+
async function createRigOmpWritableJoinLink(_options) {
|
|
225
|
+
throw new Error(RIG_GENERIC_OMP_JOIN_LIMITATION);
|
|
226
|
+
}
|
|
227
|
+
async function createRigOmpJoinRoute(options) {
|
|
228
|
+
const runId = options.runId?.trim() ? options.runId : null;
|
|
229
|
+
throw new Error(runId ? RIG_RUN_SCOPE_LIMITATION : RIG_GENERIC_OMP_JOIN_LIMITATION);
|
|
230
|
+
}
|
|
231
|
+
async function joinRigOmpRelayLink(_options) {
|
|
232
|
+
throw new Error(RIG_GENERIC_OMP_JOIN_LIMITATION);
|
|
233
|
+
}
|
|
234
|
+
async function runRigJoinSelectedRun(_options) {
|
|
235
|
+
throw new Error(RIG_RUN_SCOPE_LIMITATION);
|
|
236
|
+
}
|
|
237
|
+
function rigGenericOmpJoinLimitation() {
|
|
238
|
+
return RIG_GENERIC_OMP_JOIN_LIMITATION;
|
|
239
|
+
}
|
|
240
|
+
async function startRigHostSession(options) {
|
|
241
|
+
if (options.relayUrl) {
|
|
242
|
+
throw new Error(RIG_GENERIC_OMP_JOIN_LIMITATION);
|
|
243
|
+
}
|
|
244
|
+
const workspaceRoot = resolve3(options.workspaceRoot);
|
|
245
|
+
let stopped = false;
|
|
246
|
+
let harness = await createRigIdeaHarness(workspaceRoot);
|
|
247
|
+
let snapshot = harness.snapshot();
|
|
248
|
+
let hostSequence = snapshot.sequence;
|
|
249
|
+
const guests = new Map;
|
|
250
|
+
const broadcast = createRigHostBroadcaster(options.onFrame);
|
|
251
|
+
function publishState(nextSnapshot) {
|
|
252
|
+
hostSequence = Math.max(hostSequence, nextSnapshot.sequence) + 1;
|
|
253
|
+
snapshot = { ...nextSnapshot, sequence: hostSequence };
|
|
254
|
+
broadcast.publish({ t: "state", snapshot });
|
|
255
|
+
return snapshot;
|
|
256
|
+
}
|
|
257
|
+
async function refreshWorkspaceState() {
|
|
258
|
+
harness = await createRigIdeaHarness(workspaceRoot);
|
|
259
|
+
publishState(harness.snapshot());
|
|
260
|
+
}
|
|
261
|
+
function publishLegacyRunError(message, fromPeer) {
|
|
262
|
+
broadcast.publish({
|
|
263
|
+
t: "error",
|
|
264
|
+
code: "LEGACY_AUTHORITY_RUN_UNSUPPORTED",
|
|
265
|
+
message
|
|
266
|
+
}, fromPeer);
|
|
267
|
+
}
|
|
268
|
+
async function handleCommand(frame, fromPeer) {
|
|
269
|
+
switch (frame.command.kind) {
|
|
270
|
+
case "workspace.refresh":
|
|
271
|
+
await refreshWorkspaceState();
|
|
272
|
+
return;
|
|
273
|
+
case "run.createAdhoc":
|
|
274
|
+
case "task.run":
|
|
275
|
+
publishLegacyRunError(legacyAuthorityRunUnsupportedMessage(), fromPeer);
|
|
276
|
+
return;
|
|
277
|
+
case "run.stop":
|
|
278
|
+
case "run.resume":
|
|
279
|
+
case "run.interrupt":
|
|
280
|
+
case "run.submitMessage":
|
|
281
|
+
case "approval.resolve":
|
|
282
|
+
case "input.resolve":
|
|
283
|
+
publishLegacyRunError(legacyAuthorityRunControlUnsupportedMessage(), fromPeer);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async function handleGuestFrame(frame, fromPeer = LOCAL_PEER_ID) {
|
|
288
|
+
if (stopped)
|
|
289
|
+
return;
|
|
290
|
+
switch (frame.t) {
|
|
291
|
+
case "hello": {
|
|
292
|
+
const guest = {
|
|
293
|
+
sessionName: frame.sessionName.trim().slice(0, 64) || `guest-${fromPeer}`,
|
|
294
|
+
readOnly: frame.readOnly === true
|
|
295
|
+
};
|
|
296
|
+
guests.set(fromPeer, guest);
|
|
297
|
+
broadcast.publishWelcome(snapshot, guest.readOnly, fromPeer);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
case "command": {
|
|
301
|
+
if (guests.get(fromPeer)?.readOnly !== false) {
|
|
302
|
+
broadcast.rejectReadOnly("command", fromPeer);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
await handleCommand(frame, fromPeer);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
broadcast.publish({
|
|
309
|
+
t: "error",
|
|
310
|
+
code: "COMMAND_FAILED",
|
|
311
|
+
message: error instanceof Error ? error.message : String(error)
|
|
312
|
+
}, fromPeer);
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
case "abort": {
|
|
317
|
+
if (guests.get(fromPeer)?.readOnly !== false) {
|
|
318
|
+
broadcast.rejectReadOnly("abort", fromPeer);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
publishLegacyRunError(legacyAuthorityRunControlUnsupportedMessage(), fromPeer);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
case "fetch-transcript": {
|
|
325
|
+
publishLegacyRunError(legacyAuthorityRunControlUnsupportedMessage(), fromPeer);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
snapshot() {
|
|
332
|
+
return snapshot;
|
|
333
|
+
},
|
|
334
|
+
async createShareLink(_readOnly = false) {
|
|
335
|
+
throw new Error(RIG_GENERIC_OMP_JOIN_LIMITATION);
|
|
336
|
+
},
|
|
337
|
+
handleGuestFrame(frame) {
|
|
338
|
+
return handleGuestFrame(frame);
|
|
339
|
+
},
|
|
340
|
+
async stop(reason) {
|
|
341
|
+
if (stopped)
|
|
342
|
+
return;
|
|
343
|
+
stopped = true;
|
|
344
|
+
broadcast.publish({ t: "bye", reason });
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
export {
|
|
349
|
+
startRigHostSession,
|
|
350
|
+
runRigJoinSelectedRun,
|
|
351
|
+
rigGenericOmpJoinLimitation,
|
|
352
|
+
joinRigOmpRelayLink,
|
|
353
|
+
createRigOmpWritableJoinLink,
|
|
354
|
+
createRigOmpJoinRoute
|
|
355
|
+
};
|