@h-rig/runtime 0.0.6-alpha.21 → 0.0.6-alpha.23
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/dist/bin/rig-agent-dispatch.js +588 -28
- package/dist/src/control-plane/agent-wrapper.js +592 -28
- package/dist/src/control-plane/harness-main.js +142 -17
- package/dist/src/control-plane/hooks/completion-verification.js +142 -17
- package/dist/src/control-plane/native/harness-cli.js +142 -17
- package/dist/src/control-plane/native/pr-automation.js +142 -17
- package/dist/src/control-plane/native/pr-review-gate.js +142 -17
- package/dist/src/control-plane/native/run-ops.js +1 -1
- package/dist/src/control-plane/native/task-ops.js +142 -17
- package/dist/src/control-plane/native/verifier.js +142 -17
- package/dist/src/control-plane/pi-sessiond/bin.js +793 -0
- package/dist/src/control-plane/pi-sessiond/client.js +41 -0
- package/dist/src/control-plane/pi-sessiond/event-hub.js +59 -0
- package/dist/src/control-plane/pi-sessiond/extension-ui-context.js +198 -0
- package/dist/src/control-plane/pi-sessiond/launcher.js +163 -0
- package/dist/src/control-plane/pi-sessiond/server.js +802 -0
- package/dist/src/control-plane/pi-sessiond/session-service.js +540 -0
- package/dist/src/control-plane/pi-sessiond/types.js +1 -0
- package/dist/src/control-plane/runtime/index.js +17 -0
- package/dist/src/control-plane/runtime/isolation/home.js +17 -0
- package/dist/src/control-plane/runtime/isolation/index.js +17 -0
- package/dist/src/control-plane/runtime/isolation/runner.js +17 -0
- package/dist/src/control-plane/runtime/isolation.js +17 -0
- package/dist/src/control-plane/runtime/queue.js +17 -0
- package/package.json +7 -6
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/runtime/src/control-plane/pi-sessiond/session-service.ts
|
|
3
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4
|
+
import { mkdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import {
|
|
7
|
+
AuthStorage,
|
|
8
|
+
createAgentSessionFromServices,
|
|
9
|
+
createAgentSessionRuntime,
|
|
10
|
+
createAgentSessionServices,
|
|
11
|
+
ModelRegistry,
|
|
12
|
+
SessionManager
|
|
13
|
+
} from "@earendil-works/pi-coding-agent";
|
|
14
|
+
|
|
15
|
+
// packages/runtime/src/control-plane/pi-sessiond/extension-ui-context.ts
|
|
16
|
+
import { randomUUID } from "crypto";
|
|
17
|
+
import {
|
|
18
|
+
initTheme
|
|
19
|
+
} from "@earendil-works/pi-coding-agent";
|
|
20
|
+
var headlessTheme = {
|
|
21
|
+
fg: (_color, text) => text,
|
|
22
|
+
bg: (_color, text) => text,
|
|
23
|
+
dim: (text) => text,
|
|
24
|
+
bold: (text) => text,
|
|
25
|
+
italic: (text) => text,
|
|
26
|
+
underline: (text) => text,
|
|
27
|
+
strikethrough: (text) => text,
|
|
28
|
+
reset: (text) => text,
|
|
29
|
+
getFgAnsi: () => "",
|
|
30
|
+
getBgAnsi: () => "",
|
|
31
|
+
name: "rig-headless"
|
|
32
|
+
};
|
|
33
|
+
function ensureThemeInitialized() {
|
|
34
|
+
try {
|
|
35
|
+
initTheme(undefined, false);
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class RigPiExtensionUiBridge {
|
|
40
|
+
sessionId;
|
|
41
|
+
runId;
|
|
42
|
+
publish;
|
|
43
|
+
pending = new Map;
|
|
44
|
+
constructor(sessionId, runId, publish) {
|
|
45
|
+
this.sessionId = sessionId;
|
|
46
|
+
this.runId = runId;
|
|
47
|
+
this.publish = publish;
|
|
48
|
+
ensureThemeInitialized();
|
|
49
|
+
}
|
|
50
|
+
createContext() {
|
|
51
|
+
const dialog = (opts, defaultValue, request, parse) => {
|
|
52
|
+
if (opts?.signal?.aborted)
|
|
53
|
+
return Promise.resolve(defaultValue);
|
|
54
|
+
const id = randomUUID();
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
let timeout;
|
|
57
|
+
const cleanup = () => {
|
|
58
|
+
if (timeout)
|
|
59
|
+
clearTimeout(timeout);
|
|
60
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
61
|
+
this.pending.delete(id);
|
|
62
|
+
};
|
|
63
|
+
const finish = (value) => {
|
|
64
|
+
cleanup();
|
|
65
|
+
resolve(value);
|
|
66
|
+
};
|
|
67
|
+
const onAbort = () => finish(defaultValue);
|
|
68
|
+
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
69
|
+
if (opts?.timeout)
|
|
70
|
+
timeout = setTimeout(() => finish(defaultValue), opts.timeout);
|
|
71
|
+
this.pending.set(id, {
|
|
72
|
+
timeout,
|
|
73
|
+
abort: onAbort,
|
|
74
|
+
resolve: (raw) => finish(parse(normalizeResponse(raw)))
|
|
75
|
+
});
|
|
76
|
+
this.publish({
|
|
77
|
+
type: "extension_ui_request",
|
|
78
|
+
sessionId: this.sessionId,
|
|
79
|
+
runId: this.runId,
|
|
80
|
+
request: { id, timeout: opts?.timeout, ...request }
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
select: (title, options, opts) => dialog(opts, undefined, { method: "select", title, options }, (response) => {
|
|
86
|
+
if (response.cancelled)
|
|
87
|
+
return;
|
|
88
|
+
return typeof response.value === "string" ? response.value : undefined;
|
|
89
|
+
}),
|
|
90
|
+
confirm: (title, message, opts) => dialog(opts, false, { method: "confirm", title, message }, (response) => {
|
|
91
|
+
if (response.cancelled)
|
|
92
|
+
return false;
|
|
93
|
+
return response.confirmed === true || response.value === true;
|
|
94
|
+
}),
|
|
95
|
+
input: (title, placeholder, opts) => dialog(opts, undefined, { method: "input", title, placeholder }, (response) => {
|
|
96
|
+
if (response.cancelled)
|
|
97
|
+
return;
|
|
98
|
+
return typeof response.value === "string" ? response.value : undefined;
|
|
99
|
+
}),
|
|
100
|
+
notify: (message, type) => {
|
|
101
|
+
this.fire({ method: "notify", message, notifyType: type });
|
|
102
|
+
},
|
|
103
|
+
onTerminalInput: () => () => {},
|
|
104
|
+
setStatus: (key, text) => {
|
|
105
|
+
this.fire({ method: "setStatus", statusKey: key, statusText: text });
|
|
106
|
+
},
|
|
107
|
+
setWorkingMessage: (message) => {
|
|
108
|
+
this.fire({ method: "setWorkingMessage", message });
|
|
109
|
+
},
|
|
110
|
+
setWorkingVisible: (visible) => {
|
|
111
|
+
this.fire({ method: "setWorkingVisible", visible });
|
|
112
|
+
},
|
|
113
|
+
setWorkingIndicator: (options) => {
|
|
114
|
+
this.fire({ method: "setWorkingIndicator", options });
|
|
115
|
+
},
|
|
116
|
+
setHiddenThinkingLabel: (label) => {
|
|
117
|
+
this.fire({ method: "setHiddenThinkingLabel", label });
|
|
118
|
+
},
|
|
119
|
+
setWidget: (key, content, options) => {
|
|
120
|
+
if (content === undefined || Array.isArray(content)) {
|
|
121
|
+
this.fire({ method: "setWidget", widgetKey: key, widgetLines: content, widgetPlacement: options?.placement });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.fire({ method: "notify", message: `Extension widget '${key}' uses a component factory that Rig daemon mode cannot render.`, notifyType: "warning" });
|
|
125
|
+
},
|
|
126
|
+
setFooter: (factory) => {
|
|
127
|
+
if (factory !== undefined)
|
|
128
|
+
this.fire({ method: "notify", message: "Custom extension footers are not rendered in Rig daemon mode.", notifyType: "warning" });
|
|
129
|
+
},
|
|
130
|
+
setHeader: (factory) => {
|
|
131
|
+
if (factory !== undefined)
|
|
132
|
+
this.fire({ method: "notify", message: "Custom extension headers are not rendered in Rig daemon mode.", notifyType: "warning" });
|
|
133
|
+
},
|
|
134
|
+
setTitle: (title) => {
|
|
135
|
+
this.fire({ method: "setTitle", title });
|
|
136
|
+
},
|
|
137
|
+
custom: async () => {
|
|
138
|
+
this.fire({ method: "notify", message: "Custom extension UI components are not supported in Rig daemon mode.", notifyType: "warning" });
|
|
139
|
+
return;
|
|
140
|
+
},
|
|
141
|
+
pasteToEditor: (text) => {
|
|
142
|
+
this.fire({ method: "set_editor_text", text });
|
|
143
|
+
},
|
|
144
|
+
setEditorText: (text) => {
|
|
145
|
+
this.fire({ method: "set_editor_text", text });
|
|
146
|
+
},
|
|
147
|
+
getEditorText: () => "",
|
|
148
|
+
editor: (title, prefill) => dialog(undefined, undefined, { method: "editor", title, prefill }, (response) => {
|
|
149
|
+
if (response.cancelled)
|
|
150
|
+
return;
|
|
151
|
+
return typeof response.value === "string" ? response.value : undefined;
|
|
152
|
+
}),
|
|
153
|
+
addAutocompleteProvider: () => {},
|
|
154
|
+
setEditorComponent: (factory) => {
|
|
155
|
+
if (factory !== undefined)
|
|
156
|
+
this.fire({ method: "notify", message: "Custom editor components are not supported in Rig daemon mode.", notifyType: "warning" });
|
|
157
|
+
},
|
|
158
|
+
getEditorComponent: () => {
|
|
159
|
+
return;
|
|
160
|
+
},
|
|
161
|
+
get theme() {
|
|
162
|
+
return headlessTheme;
|
|
163
|
+
},
|
|
164
|
+
getAllThemes: () => [],
|
|
165
|
+
getTheme: () => {
|
|
166
|
+
return;
|
|
167
|
+
},
|
|
168
|
+
setTheme: () => ({ success: false, error: "Theme switching is not supported in Rig daemon mode" }),
|
|
169
|
+
getToolsExpanded: () => false,
|
|
170
|
+
setToolsExpanded: () => {}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
respond(input) {
|
|
174
|
+
const pending = this.pending.get(input.requestId);
|
|
175
|
+
if (!pending)
|
|
176
|
+
return false;
|
|
177
|
+
pending.resolve(input);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
cancelAll() {
|
|
181
|
+
for (const [id, pending] of this.pending.entries()) {
|
|
182
|
+
if (pending.timeout)
|
|
183
|
+
clearTimeout(pending.timeout);
|
|
184
|
+
pending.abort?.();
|
|
185
|
+
this.pending.delete(id);
|
|
186
|
+
pending.resolve({ requestId: id, cancelled: true });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
fire(request) {
|
|
190
|
+
this.publish({
|
|
191
|
+
type: "extension_ui_request",
|
|
192
|
+
sessionId: this.sessionId,
|
|
193
|
+
runId: this.runId,
|
|
194
|
+
request: { id: randomUUID(), ...request }
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function normalizeResponse(value) {
|
|
199
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
200
|
+
return { requestId: "", cancelled: true };
|
|
201
|
+
const record = value;
|
|
202
|
+
return {
|
|
203
|
+
requestId: typeof record.requestId === "string" ? record.requestId : "",
|
|
204
|
+
value: record.value,
|
|
205
|
+
confirmed: typeof record.confirmed === "boolean" ? record.confirmed : undefined,
|
|
206
|
+
cancelled: record.cancelled === true
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// packages/runtime/src/control-plane/pi-sessiond/session-service.ts
|
|
211
|
+
var BUILTIN_COMMANDS = [
|
|
212
|
+
{ name: "session", description: "Show session info and stats", source: "builtin" },
|
|
213
|
+
{ name: "name", description: "Set session display name", source: "builtin" },
|
|
214
|
+
{ name: "compact", description: "Manually compact session context", source: "builtin" },
|
|
215
|
+
{ name: "reload", description: "Reload keybindings, extensions, skills, prompts, and themes", source: "builtin" },
|
|
216
|
+
{ name: "quit", description: "Detach from the current local Pi frontend", source: "builtin" }
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
class RigPiSessionService {
|
|
220
|
+
hub;
|
|
221
|
+
config;
|
|
222
|
+
activeBySession = new Map;
|
|
223
|
+
sessionIdByRun = new Map;
|
|
224
|
+
heartbeat;
|
|
225
|
+
constructor(hub, config) {
|
|
226
|
+
this.hub = hub;
|
|
227
|
+
this.config = config;
|
|
228
|
+
this.heartbeat = setInterval(() => this.publishHeartbeats(), 2000);
|
|
229
|
+
}
|
|
230
|
+
activeCount() {
|
|
231
|
+
return this.activeBySession.size;
|
|
232
|
+
}
|
|
233
|
+
async dispose() {
|
|
234
|
+
clearInterval(this.heartbeat);
|
|
235
|
+
const sessions = [...this.activeBySession.values()];
|
|
236
|
+
this.activeBySession.clear();
|
|
237
|
+
this.sessionIdByRun.clear();
|
|
238
|
+
await Promise.all(sessions.map(async (active) => {
|
|
239
|
+
active.unsubscribe();
|
|
240
|
+
active.ui.cancelAll();
|
|
241
|
+
try {
|
|
242
|
+
await active.runtime.session.abort();
|
|
243
|
+
} catch {}
|
|
244
|
+
await active.runtime.dispose();
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
async startSession(input) {
|
|
248
|
+
const existing = this.getByRun(input.runId);
|
|
249
|
+
if (existing)
|
|
250
|
+
return existing.metadata;
|
|
251
|
+
mkdirSync(input.agentDir, { recursive: true });
|
|
252
|
+
mkdirSync(input.sessionDir, { recursive: true });
|
|
253
|
+
const authStorage = AuthStorage.create(join(input.agentDir, "auth.json"));
|
|
254
|
+
const modelRegistry = ModelRegistry.create(authStorage, join(input.agentDir, "models.json"));
|
|
255
|
+
const createRuntime = async ({ cwd, agentDir, sessionManager: sessionManager2, sessionStartEvent }) => {
|
|
256
|
+
const services = await createAgentSessionServices({ cwd, agentDir, authStorage, modelRegistry });
|
|
257
|
+
const result = await createAgentSessionFromServices({ services, sessionManager: sessionManager2, sessionStartEvent });
|
|
258
|
+
return { ...result, services, diagnostics: services.diagnostics };
|
|
259
|
+
};
|
|
260
|
+
const sessionManager = SessionManager.create(input.cwd, input.sessionDir);
|
|
261
|
+
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
262
|
+
cwd: input.cwd,
|
|
263
|
+
agentDir: input.agentDir,
|
|
264
|
+
sessionManager,
|
|
265
|
+
sessionStartEvent: { type: "session_start", reason: "new" }
|
|
266
|
+
});
|
|
267
|
+
const now = new Date().toISOString();
|
|
268
|
+
const metadata = {
|
|
269
|
+
runId: input.runId,
|
|
270
|
+
sessionId: runtime.session.sessionId,
|
|
271
|
+
daemonId: this.config.daemonId,
|
|
272
|
+
cwd: input.cwd,
|
|
273
|
+
agentDir: input.agentDir,
|
|
274
|
+
sessionDir: input.sessionDir,
|
|
275
|
+
transport: "http-control-ws-events",
|
|
276
|
+
serverBasePath: `/api/runs/${encodeURIComponent(input.runId)}/pi`,
|
|
277
|
+
eventsPath: `/api/runs/${encodeURIComponent(input.runId)}/pi/events`,
|
|
278
|
+
createdAt: now,
|
|
279
|
+
updatedAt: now,
|
|
280
|
+
backend: {
|
|
281
|
+
kind: "worker-pi-sessiond",
|
|
282
|
+
version: this.config.version,
|
|
283
|
+
commit: this.config.commit,
|
|
284
|
+
pid: process.pid
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const active = {
|
|
288
|
+
runId: input.runId,
|
|
289
|
+
runtime,
|
|
290
|
+
metadata,
|
|
291
|
+
unsubscribe: () => {},
|
|
292
|
+
ui: new RigPiExtensionUiBridge(runtime.session.sessionId, input.runId, (event) => this.publish(runtime.session.sessionId, input.runId, event))
|
|
293
|
+
};
|
|
294
|
+
await this.bindRuntime(active);
|
|
295
|
+
if (input.sessionName?.trim())
|
|
296
|
+
runtime.session.setSessionName(input.sessionName.trim());
|
|
297
|
+
this.activeBySession.set(runtime.session.sessionId, active);
|
|
298
|
+
this.sessionIdByRun.set(input.runId, runtime.session.sessionId);
|
|
299
|
+
this.publish(runtime.session.sessionId, input.runId, { type: "ready", metadata });
|
|
300
|
+
this.publishStatus(active);
|
|
301
|
+
this.publishActivity(active, "ready", "idle");
|
|
302
|
+
return metadata;
|
|
303
|
+
}
|
|
304
|
+
getByRun(runId) {
|
|
305
|
+
const sessionId = this.sessionIdByRun.get(runId);
|
|
306
|
+
return sessionId ? this.activeBySession.get(sessionId) : undefined;
|
|
307
|
+
}
|
|
308
|
+
getBySession(sessionId) {
|
|
309
|
+
return this.activeBySession.get(sessionId);
|
|
310
|
+
}
|
|
311
|
+
messages(sessionId) {
|
|
312
|
+
const active = this.requireActive(sessionId);
|
|
313
|
+
return [...active.runtime.session.messages];
|
|
314
|
+
}
|
|
315
|
+
status(sessionId) {
|
|
316
|
+
return this.statusFromActive(this.requireActive(sessionId));
|
|
317
|
+
}
|
|
318
|
+
commands(sessionId) {
|
|
319
|
+
const session = this.requireActive(sessionId).runtime.session;
|
|
320
|
+
const commands = [...BUILTIN_COMMANDS];
|
|
321
|
+
for (const command of session.extensionRunner.getRegisteredCommands()) {
|
|
322
|
+
commands.push({ name: command.invocationName, description: command.description, source: "extension" });
|
|
323
|
+
}
|
|
324
|
+
for (const template of session.promptTemplates) {
|
|
325
|
+
commands.push({ name: template.name, description: template.description, source: "prompt" });
|
|
326
|
+
}
|
|
327
|
+
for (const skill of session.resourceLoader.getSkills().skills) {
|
|
328
|
+
commands.push({ name: `skill:${skill.name}`, description: skill.description, source: "skill" });
|
|
329
|
+
}
|
|
330
|
+
return commands.sort((a, b) => a.name.localeCompare(b.name));
|
|
331
|
+
}
|
|
332
|
+
async prompt(sessionId, text, streamingBehavior) {
|
|
333
|
+
const active = this.requireActive(sessionId);
|
|
334
|
+
const session = active.runtime.session;
|
|
335
|
+
const behavior = session.isStreaming || session.isCompacting ? streamingBehavior ?? "steer" : undefined;
|
|
336
|
+
this.publishActivity(active, behavior === "followUp" ? "follow-up queued" : behavior === "steer" ? "steering queued" : "prompt accepted", "active");
|
|
337
|
+
session.prompt(text, behavior ? { streamingBehavior: behavior, source: "rpc" } : { source: "rpc" }).catch((error) => {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
339
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message });
|
|
340
|
+
this.publishActivity(active, "prompt failed", "error", message);
|
|
341
|
+
});
|
|
342
|
+
this.publishStatus(active);
|
|
343
|
+
return { accepted: true };
|
|
344
|
+
}
|
|
345
|
+
async shell(sessionId, text) {
|
|
346
|
+
const active = this.requireActive(sessionId);
|
|
347
|
+
const session = active.runtime.session;
|
|
348
|
+
const excluded = text.startsWith("!!");
|
|
349
|
+
const command = (excluded ? text.slice(2) : text.startsWith("!") ? text.slice(1) : text).trim();
|
|
350
|
+
if (!command)
|
|
351
|
+
throw new Error("Usage: !<shell command>");
|
|
352
|
+
if (session.isBashRunning)
|
|
353
|
+
throw new Error("A bash command is already running");
|
|
354
|
+
this.publishActivity(active, "running bash", "active", command);
|
|
355
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.start", command, excludeFromContext: excluded } });
|
|
356
|
+
session.executeBash(command, (chunk) => {
|
|
357
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.chunk", chunk } });
|
|
358
|
+
this.publishActivity(active, "running bash", "active", command);
|
|
359
|
+
this.publishStatus(active);
|
|
360
|
+
}, { excludeFromContext: excluded }).then((result) => {
|
|
361
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.end", ...result } });
|
|
362
|
+
this.publishActivity(active, "bash complete", result.exitCode === 0 ? "idle" : "error", command);
|
|
363
|
+
this.publishStatus(active);
|
|
364
|
+
}).catch((error) => {
|
|
365
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
366
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "pi.ui_event", sessionId, runId: active.runId, event: { type: "shell.end", output: message, isError: true } });
|
|
367
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message });
|
|
368
|
+
this.publishActivity(active, "bash failed", "error", message);
|
|
369
|
+
this.publishStatus(active);
|
|
370
|
+
});
|
|
371
|
+
return { accepted: true };
|
|
372
|
+
}
|
|
373
|
+
async runCommand(sessionId, text) {
|
|
374
|
+
const active = this.requireActive(sessionId);
|
|
375
|
+
const trimmed = text.trim();
|
|
376
|
+
const [rawName = "", ...args] = trimmed.replace(/^\//, "").split(/\s+/);
|
|
377
|
+
const rest = args.join(" ").trim();
|
|
378
|
+
if (rawName === "session")
|
|
379
|
+
return { type: "done", message: formatSessionStats(active.runtime.session) };
|
|
380
|
+
if (rawName === "name") {
|
|
381
|
+
if (!rest)
|
|
382
|
+
return { type: "unsupported", message: "Usage: /name <session name>" };
|
|
383
|
+
active.runtime.session.setSessionName(rest);
|
|
384
|
+
this.publishStatus(active);
|
|
385
|
+
return { type: "done", message: `Session named: ${rest}` };
|
|
386
|
+
}
|
|
387
|
+
if (rawName === "compact") {
|
|
388
|
+
active.runtime.session.compact(rest || undefined).catch((error) => {
|
|
389
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "error", message: error instanceof Error ? error.message : String(error) });
|
|
390
|
+
});
|
|
391
|
+
return { type: "done", message: "Compaction started\u2026" };
|
|
392
|
+
}
|
|
393
|
+
if (rawName === "reload") {
|
|
394
|
+
await active.runtime.session.reload();
|
|
395
|
+
await this.bindRuntime(active);
|
|
396
|
+
return { type: "done", message: "Pi resources reloaded" };
|
|
397
|
+
}
|
|
398
|
+
if (rawName === "quit")
|
|
399
|
+
return { type: "done", message: "Detach locally with /detach or /quit." };
|
|
400
|
+
await this.prompt(sessionId, trimmed, active.runtime.session.isStreaming ? "steer" : undefined);
|
|
401
|
+
return { type: "done", message: `Accepted ${trimmed}` };
|
|
402
|
+
}
|
|
403
|
+
respondToCommand(_sessionId, _requestId, _value) {
|
|
404
|
+
return { type: "unsupported", message: "No pending command selection is active." };
|
|
405
|
+
}
|
|
406
|
+
respondToExtensionUi(sessionId, input) {
|
|
407
|
+
const active = this.requireActive(sessionId);
|
|
408
|
+
return { accepted: active.ui.respond(input) };
|
|
409
|
+
}
|
|
410
|
+
async abort(sessionId) {
|
|
411
|
+
const active = this.requireActive(sessionId);
|
|
412
|
+
active.runtime.session.clearQueue();
|
|
413
|
+
await active.runtime.session.abort();
|
|
414
|
+
this.publishActivity(active, "stopped", "idle");
|
|
415
|
+
this.publishStatus(active);
|
|
416
|
+
return { aborted: true };
|
|
417
|
+
}
|
|
418
|
+
async bindRuntime(active) {
|
|
419
|
+
active.unsubscribe();
|
|
420
|
+
const session = active.runtime.session;
|
|
421
|
+
active.ui.cancelAll();
|
|
422
|
+
active.ui = new RigPiExtensionUiBridge(session.sessionId, active.runId, (event) => this.publish(session.sessionId, active.runId, event));
|
|
423
|
+
active.metadata = { ...active.metadata, sessionId: session.sessionId, updatedAt: new Date().toISOString() };
|
|
424
|
+
this.sessionIdByRun.set(active.runId, session.sessionId);
|
|
425
|
+
this.activeBySession.set(session.sessionId, active);
|
|
426
|
+
active.runtime.setRebindSession(async () => this.bindRuntime(active));
|
|
427
|
+
await session.bindExtensions({
|
|
428
|
+
uiContext: active.ui.createContext(),
|
|
429
|
+
commandContextActions: {
|
|
430
|
+
waitForIdle: () => session.agent.waitForIdle(),
|
|
431
|
+
newSession: async (options) => active.runtime.newSession(options),
|
|
432
|
+
fork: async (entryId, options) => {
|
|
433
|
+
const result = await active.runtime.fork(entryId, options);
|
|
434
|
+
return { cancelled: result.cancelled };
|
|
435
|
+
},
|
|
436
|
+
navigateTree: async (targetId, options) => session.navigateTree(targetId, options),
|
|
437
|
+
switchSession: async (sessionPath, options) => active.runtime.switchSession(sessionPath, options),
|
|
438
|
+
reload: async () => {
|
|
439
|
+
await session.reload();
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
shutdownHandler: () => {
|
|
443
|
+
this.abort(session.sessionId);
|
|
444
|
+
},
|
|
445
|
+
onError: (error) => {
|
|
446
|
+
this.publish(session.sessionId, active.runId, { type: "error", message: error.error, detail: error });
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
active.unsubscribe = session.subscribe((event) => {
|
|
450
|
+
this.publish(session.sessionId, active.runId, { type: "pi.event", sessionId: session.sessionId, runId: active.runId, event });
|
|
451
|
+
this.publishActivityForEvent(active, event);
|
|
452
|
+
this.publishStatus(active);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
publish(sessionId, runId, event) {
|
|
456
|
+
this.hub.publish(sessionId, runId, event);
|
|
457
|
+
}
|
|
458
|
+
publishStatus(active) {
|
|
459
|
+
const status = this.statusFromActive(active);
|
|
460
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "status.update", sessionId: active.runtime.session.sessionId, runId: active.runId, status });
|
|
461
|
+
}
|
|
462
|
+
publishActivity(active, label, phase, detail) {
|
|
463
|
+
const activity = {
|
|
464
|
+
sessionId: active.runtime.session.sessionId,
|
|
465
|
+
runId: active.runId,
|
|
466
|
+
phase,
|
|
467
|
+
label,
|
|
468
|
+
detail,
|
|
469
|
+
at: new Date().toISOString()
|
|
470
|
+
};
|
|
471
|
+
active.activity = activity;
|
|
472
|
+
this.publish(active.runtime.session.sessionId, active.runId, { type: "activity.update", sessionId: active.runtime.session.sessionId, runId: active.runId, activity });
|
|
473
|
+
}
|
|
474
|
+
publishActivityForEvent(active, event) {
|
|
475
|
+
const type = event && typeof event === "object" && !Array.isArray(event) ? event.type : undefined;
|
|
476
|
+
if (type === "agent_start")
|
|
477
|
+
this.publishActivity(active, "agent running", "active");
|
|
478
|
+
else if (type === "agent_end")
|
|
479
|
+
this.publishActivity(active, "idle", "idle");
|
|
480
|
+
else if (type === "tool_execution_start")
|
|
481
|
+
this.publishActivity(active, `tool: ${String(event.toolName ?? "tool")}`, "active");
|
|
482
|
+
else if (type === "compaction_start")
|
|
483
|
+
this.publishActivity(active, "compacting", "active");
|
|
484
|
+
else if (type === "compaction_end")
|
|
485
|
+
this.publishActivity(active, "compaction complete", "idle");
|
|
486
|
+
}
|
|
487
|
+
publishHeartbeats() {
|
|
488
|
+
for (const active of this.activeBySession.values()) {
|
|
489
|
+
if (active.runtime.session.isStreaming || active.runtime.session.isCompacting || active.runtime.session.isBashRunning || active.runtime.session.pendingMessageCount > 0) {
|
|
490
|
+
this.publishStatus(active);
|
|
491
|
+
this.publishActivity(active, active.activity?.label ?? "active", "active", active.activity?.detail);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
statusFromActive(active) {
|
|
496
|
+
const session = active.runtime.session;
|
|
497
|
+
return {
|
|
498
|
+
sessionId: session.sessionId,
|
|
499
|
+
runId: active.runId,
|
|
500
|
+
cwd: active.runtime.cwd,
|
|
501
|
+
sessionFile: session.sessionFile,
|
|
502
|
+
sessionName: session.sessionName,
|
|
503
|
+
model: session.model,
|
|
504
|
+
thinkingLevel: session.thinkingLevel,
|
|
505
|
+
isStreaming: session.isStreaming,
|
|
506
|
+
isCompacting: session.isCompacting,
|
|
507
|
+
isBashRunning: session.isBashRunning,
|
|
508
|
+
pendingMessageCount: session.pendingMessageCount,
|
|
509
|
+
messageCount: session.messages.length,
|
|
510
|
+
steeringMessages: session.getSteeringMessages(),
|
|
511
|
+
followUpMessages: session.getFollowUpMessages(),
|
|
512
|
+
contextUsage: session.getContextUsage(),
|
|
513
|
+
stats: session.getSessionStats()
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
requireActive(sessionId) {
|
|
517
|
+
const active = this.activeBySession.get(sessionId);
|
|
518
|
+
if (!active)
|
|
519
|
+
throw new Error("Pi session not found");
|
|
520
|
+
return active;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function formatSessionStats(session) {
|
|
524
|
+
const stats = session.getSessionStats();
|
|
525
|
+
return [
|
|
526
|
+
`Session: ${stats.sessionId}`,
|
|
527
|
+
`Messages: ${stats.totalMessages} (${stats.userMessages} user, ${stats.assistantMessages} assistant)`,
|
|
528
|
+
`Tool calls: ${stats.toolCalls}`,
|
|
529
|
+
`Tokens: \u2191${stats.tokens.input} \u2193${stats.tokens.output} total ${stats.tokens.total}`,
|
|
530
|
+
`Cost: $${stats.cost.toFixed(4)}`
|
|
531
|
+
].join(`
|
|
532
|
+
`);
|
|
533
|
+
}
|
|
534
|
+
function createDaemonId() {
|
|
535
|
+
return `rig-pi-sessiond-${randomUUID2()}`;
|
|
536
|
+
}
|
|
537
|
+
export {
|
|
538
|
+
createDaemonId,
|
|
539
|
+
RigPiSessionService
|
|
540
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -6218,6 +6218,21 @@ var GITHUB_KNOWN_HOSTS = [
|
|
|
6218
6218
|
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
6219
6219
|
].join(`
|
|
6220
6220
|
`);
|
|
6221
|
+
function resolveControlPlaneSourceRoot(projectRoot) {
|
|
6222
|
+
const candidates = [
|
|
6223
|
+
process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
|
|
6224
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
6225
|
+
resolve27(import.meta.dir, "../../../../.."),
|
|
6226
|
+
projectRoot
|
|
6227
|
+
].filter((value) => Boolean(value));
|
|
6228
|
+
for (const candidate of candidates) {
|
|
6229
|
+
const root = resolve27(candidate);
|
|
6230
|
+
if (existsSync25(resolve27(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
|
|
6231
|
+
return root;
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
return "";
|
|
6235
|
+
}
|
|
6221
6236
|
async function runtimeEnv(projectRoot, runtime) {
|
|
6222
6237
|
const bunBinaryPath = resolveBunBinaryPath();
|
|
6223
6238
|
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
@@ -6263,9 +6278,11 @@ async function runtimeEnv(projectRoot, runtime) {
|
|
|
6263
6278
|
const runtimeRigGit = resolve27(runtime.binDir, runtimeRigGitFileName());
|
|
6264
6279
|
const preferredShell = existsSync25(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
6265
6280
|
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
6281
|
+
const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
|
|
6266
6282
|
const env = {
|
|
6267
6283
|
PROJECT_RIG_ROOT: projectRoot,
|
|
6268
6284
|
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
6285
|
+
...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
|
|
6269
6286
|
HOME: runtime.homeDir,
|
|
6270
6287
|
TMPDIR: runtime.tmpDir,
|
|
6271
6288
|
XDG_CACHE_HOME: runtime.cacheDir,
|
|
@@ -750,6 +750,21 @@ var GITHUB_KNOWN_HOSTS = [
|
|
|
750
750
|
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
751
751
|
].join(`
|
|
752
752
|
`);
|
|
753
|
+
function resolveControlPlaneSourceRoot(projectRoot) {
|
|
754
|
+
const candidates = [
|
|
755
|
+
process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
|
|
756
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
757
|
+
resolve7(import.meta.dir, "../../../../.."),
|
|
758
|
+
projectRoot
|
|
759
|
+
].filter((value) => Boolean(value));
|
|
760
|
+
for (const candidate of candidates) {
|
|
761
|
+
const root = resolve7(candidate);
|
|
762
|
+
if (existsSync6(resolve7(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
|
|
763
|
+
return root;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return "";
|
|
767
|
+
}
|
|
753
768
|
async function runtimeEnv(projectRoot, runtime) {
|
|
754
769
|
const bunBinaryPath = resolveBunBinaryPath();
|
|
755
770
|
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
@@ -795,9 +810,11 @@ async function runtimeEnv(projectRoot, runtime) {
|
|
|
795
810
|
const runtimeRigGit = resolve7(runtime.binDir, runtimeRigGitFileName());
|
|
796
811
|
const preferredShell = existsSync6(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
797
812
|
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
813
|
+
const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
|
|
798
814
|
const env = {
|
|
799
815
|
PROJECT_RIG_ROOT: projectRoot,
|
|
800
816
|
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
817
|
+
...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
|
|
801
818
|
HOME: runtime.homeDir,
|
|
802
819
|
TMPDIR: runtime.tmpDir,
|
|
803
820
|
XDG_CACHE_HOME: runtime.cacheDir,
|
|
@@ -5518,6 +5518,21 @@ var GITHUB_KNOWN_HOSTS = [
|
|
|
5518
5518
|
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
5519
5519
|
].join(`
|
|
5520
5520
|
`);
|
|
5521
|
+
function resolveControlPlaneSourceRoot(projectRoot) {
|
|
5522
|
+
const candidates = [
|
|
5523
|
+
process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
|
|
5524
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
5525
|
+
resolve25(import.meta.dir, "../../../../.."),
|
|
5526
|
+
projectRoot
|
|
5527
|
+
].filter((value) => Boolean(value));
|
|
5528
|
+
for (const candidate of candidates) {
|
|
5529
|
+
const root = resolve25(candidate);
|
|
5530
|
+
if (existsSync24(resolve25(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
|
|
5531
|
+
return root;
|
|
5532
|
+
}
|
|
5533
|
+
}
|
|
5534
|
+
return "";
|
|
5535
|
+
}
|
|
5521
5536
|
async function runtimeEnv(projectRoot, runtime) {
|
|
5522
5537
|
const bunBinaryPath = resolveBunBinaryPath();
|
|
5523
5538
|
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
@@ -5563,9 +5578,11 @@ async function runtimeEnv(projectRoot, runtime) {
|
|
|
5563
5578
|
const runtimeRigGit = resolve25(runtime.binDir, runtimeRigGitFileName());
|
|
5564
5579
|
const preferredShell = existsSync24(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
5565
5580
|
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
5581
|
+
const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
|
|
5566
5582
|
const env = {
|
|
5567
5583
|
PROJECT_RIG_ROOT: projectRoot,
|
|
5568
5584
|
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
5585
|
+
...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
|
|
5569
5586
|
HOME: runtime.homeDir,
|
|
5570
5587
|
TMPDIR: runtime.tmpDir,
|
|
5571
5588
|
XDG_CACHE_HOME: runtime.cacheDir,
|
|
@@ -2148,6 +2148,21 @@ var GITHUB_KNOWN_HOSTS = [
|
|
|
2148
2148
|
"github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
2149
2149
|
].join(`
|
|
2150
2150
|
`);
|
|
2151
|
+
function resolveControlPlaneSourceRoot(projectRoot) {
|
|
2152
|
+
const candidates = [
|
|
2153
|
+
process.env.RIG_CONTROL_PLANE_SOURCE_ROOT?.trim(),
|
|
2154
|
+
process.env.RIG_HOST_PROJECT_ROOT?.trim(),
|
|
2155
|
+
resolve11(import.meta.dir, "../../../../.."),
|
|
2156
|
+
projectRoot
|
|
2157
|
+
].filter((value) => Boolean(value));
|
|
2158
|
+
for (const candidate of candidates) {
|
|
2159
|
+
const root = resolve11(candidate);
|
|
2160
|
+
if (existsSync9(resolve11(root, "packages/runtime/src/control-plane/pi-sessiond/bin.ts"))) {
|
|
2161
|
+
return root;
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
return "";
|
|
2165
|
+
}
|
|
2151
2166
|
async function runtimeEnv(projectRoot, runtime) {
|
|
2152
2167
|
const bunBinaryPath = resolveBunBinaryPath();
|
|
2153
2168
|
const bunDir = resolveBunInstallDir(bunBinaryPath);
|
|
@@ -2193,9 +2208,11 @@ async function runtimeEnv(projectRoot, runtime) {
|
|
|
2193
2208
|
const runtimeRigGit = resolve11(runtime.binDir, runtimeRigGitFileName());
|
|
2194
2209
|
const preferredShell = existsSync9(runtimeBash) ? runtimeBash : "/bin/bash";
|
|
2195
2210
|
const nativeRuntimeLibraryPath = await materializeNativeRuntimeLibrary(runtime.binDir);
|
|
2211
|
+
const controlPlaneSourceRoot = resolveControlPlaneSourceRoot(projectRoot);
|
|
2196
2212
|
const env = {
|
|
2197
2213
|
PROJECT_RIG_ROOT: projectRoot,
|
|
2198
2214
|
RIG_HOST_PROJECT_ROOT: projectRoot,
|
|
2215
|
+
...controlPlaneSourceRoot ? { RIG_CONTROL_PLANE_SOURCE_ROOT: controlPlaneSourceRoot } : {},
|
|
2199
2216
|
HOME: runtime.homeDir,
|
|
2200
2217
|
TMPDIR: runtime.tmpDir,
|
|
2201
2218
|
XDG_CACHE_HOME: runtime.cacheDir,
|