@psnext/slingcli 2.5.20260623-1 → 2.5.20260629-1
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/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +5 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +2 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +51 -47
- package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +9 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +9 -6
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare.js +0 -18
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +15 -25
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +30 -26
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +189 -204
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +24 -21
- package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -5
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +11 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +19 -4
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +81 -7
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
- package/node_modules/@earendil-works/pi-ai/dist/images-models.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/legacy-api-aliases.js +49 -0
- package/node_modules/@earendil-works/pi-ai/dist/models.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +25 -8
- package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +0 -34
- package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +21 -9
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +85 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js +13 -13
- package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +1 -20
- package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +19 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js +72 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +19 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +11 -11
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +15 -15
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +310 -39
- package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +37 -18
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +349 -77
- package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +7 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +6 -6
- package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +6 -6
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +95 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/headers.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +86 -0
- package/node_modules/@earendil-works/pi-ai/package.json +1 -33
- package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +13 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +15 -30
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +6 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +5 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +2 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +2 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +9 -5
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/timings.js +24 -14
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +12 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +20 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +9 -9
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +12 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/session-selector.js +14 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +37 -25
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/rpc-entry.js +10 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-convert.js +21 -15
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-process.js +83 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/utils/mime.js +41 -0
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
- package/node_modules/@earendil-works/pi-coding-agent/package.json +8 -5
- package/node_modules/@earendil-works/pi-orchestrator/dist/cli.js +136 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/config.js +55 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/handler.js +105 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/index.js +11 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/client.js +56 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/protocol.js +12 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/server.js +145 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/radius.js +365 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/rpc-process.js +168 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/serve.js +70 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/storage.js +58 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/supervisor.js +290 -0
- package/node_modules/@earendil-works/pi-orchestrator/dist/types.js +2 -0
- package/node_modules/@earendil-works/pi-orchestrator/package.json +45 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +3 -0
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/diagnostics_channel.d.ts +17 -20
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
- package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
- package/node_modules/brace-expansion/dist/esm/index.js +24 -14
- package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
- package/node_modules/brace-expansion/package.json +2 -2
- package/node_modules/google-auth-library/build/src/auth/googleauth.js +1 -1
- package/node_modules/google-auth-library/package.json +4 -4
- package/package.json +7 -6
- package/slingshot/index.js +302 -304
- package/node_modules/@earendil-works/pi-ai/dist/api-registry.js +0 -44
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { getSocketPath } from "./config.js";
|
|
4
|
+
import { handleIpcRequest, openRpcStream } from "./handler.js";
|
|
5
|
+
import { startIpcServer } from "./ipc/server.js";
|
|
6
|
+
import { getRadiusOrchestratorBaseUrl, isRadiusEnabled, radiusPresence } from "./radius.js";
|
|
7
|
+
import { supervisor } from "./supervisor.js";
|
|
8
|
+
export async function serve() {
|
|
9
|
+
const socketPath = getSocketPath();
|
|
10
|
+
mkdirSync(dirname(socketPath), { recursive: true });
|
|
11
|
+
const server = await startIpcServer(Object.assign(handleIpcRequest, {
|
|
12
|
+
openRpcStream,
|
|
13
|
+
}));
|
|
14
|
+
try {
|
|
15
|
+
await supervisor.recoverAfterRestart();
|
|
16
|
+
if (isRadiusEnabled()) {
|
|
17
|
+
const machine = await radiusPresence.start();
|
|
18
|
+
console.log(`radius integration enabled: ${socketPath} -> ${getRadiusOrchestratorBaseUrl()}`);
|
|
19
|
+
if (machine) {
|
|
20
|
+
console.log(`radius machine id: ${machine.id}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log("radius integration disabled: login radius in ~/.pi/agent/auth.json or set PI_RADIUS_API_KEY");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
server.close();
|
|
29
|
+
if (existsSync(socketPath)) {
|
|
30
|
+
unlinkSync(socketPath);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
console.log(`orchestrator listening on ${socketPath}`);
|
|
35
|
+
let shutdownPromise;
|
|
36
|
+
const shutdown = async (exitCode) => {
|
|
37
|
+
if (shutdownPromise) {
|
|
38
|
+
await shutdownPromise;
|
|
39
|
+
process.exit(exitCode);
|
|
40
|
+
}
|
|
41
|
+
shutdownPromise = (async () => {
|
|
42
|
+
server.close();
|
|
43
|
+
await supervisor.shutdown();
|
|
44
|
+
await radiusPresence.stop();
|
|
45
|
+
if (existsSync(socketPath)) {
|
|
46
|
+
unlinkSync(socketPath);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
await shutdownPromise;
|
|
50
|
+
process.exit(exitCode);
|
|
51
|
+
};
|
|
52
|
+
process.on("SIGINT", () => {
|
|
53
|
+
void shutdown(0);
|
|
54
|
+
});
|
|
55
|
+
process.on("SIGTERM", () => {
|
|
56
|
+
void shutdown(0);
|
|
57
|
+
});
|
|
58
|
+
process.on("uncaughtException", (error) => {
|
|
59
|
+
console.error(error);
|
|
60
|
+
void shutdown(1);
|
|
61
|
+
});
|
|
62
|
+
process.on("unhandledRejection", (reason) => {
|
|
63
|
+
console.error(reason);
|
|
64
|
+
void shutdown(1);
|
|
65
|
+
});
|
|
66
|
+
await new Promise(() => {
|
|
67
|
+
// Keep the process alive until a signal or fatal error triggers shutdown.
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { getInstancesPath, getMachinePath, getOrchestratorDir } from "./config.js";
|
|
3
|
+
function ensureOrchestratorDir() {
|
|
4
|
+
const orchestratorDir = getOrchestratorDir();
|
|
5
|
+
if (!existsSync(orchestratorDir)) {
|
|
6
|
+
mkdirSync(orchestratorDir, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function loadMachine() {
|
|
10
|
+
const machinePath = getMachinePath();
|
|
11
|
+
if (!existsSync(machinePath)) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const data = readFileSync(machinePath, "utf-8");
|
|
15
|
+
return JSON.parse(data);
|
|
16
|
+
}
|
|
17
|
+
export function saveMachine(machine) {
|
|
18
|
+
ensureOrchestratorDir();
|
|
19
|
+
writeFileSync(getMachinePath(), JSON.stringify(machine, null, 2));
|
|
20
|
+
}
|
|
21
|
+
export function deleteMachine() {
|
|
22
|
+
const machinePath = getMachinePath();
|
|
23
|
+
if (!existsSync(machinePath)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
rmSync(machinePath);
|
|
27
|
+
}
|
|
28
|
+
export function loadInstances() {
|
|
29
|
+
const instancesPath = getInstancesPath();
|
|
30
|
+
if (!existsSync(instancesPath)) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const data = readFileSync(instancesPath, "utf-8");
|
|
34
|
+
return JSON.parse(data);
|
|
35
|
+
}
|
|
36
|
+
export function saveInstances(instances) {
|
|
37
|
+
ensureOrchestratorDir();
|
|
38
|
+
writeFileSync(getInstancesPath(), JSON.stringify(instances, null, 2));
|
|
39
|
+
}
|
|
40
|
+
export function getInstance(instanceId) {
|
|
41
|
+
return loadInstances().find((instance) => instance.id === instanceId);
|
|
42
|
+
}
|
|
43
|
+
export function upsertInstance(instance) {
|
|
44
|
+
const instances = loadInstances();
|
|
45
|
+
const index = instances.findIndex((existing) => existing.id === instance.id);
|
|
46
|
+
if (index === -1) {
|
|
47
|
+
instances.push(instance);
|
|
48
|
+
saveInstances(instances);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
instances[index] = instance;
|
|
52
|
+
saveInstances(instances);
|
|
53
|
+
}
|
|
54
|
+
export function removeInstance(instanceId) {
|
|
55
|
+
const instances = loadInstances().filter((instance) => instance.id !== instanceId);
|
|
56
|
+
saveInstances(instances);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { radiusPresence } from "./radius.js";
|
|
3
|
+
import { createRpcProcessInstance } from "./rpc-process.js";
|
|
4
|
+
import { getInstance, loadInstances, removeInstance, saveInstances, upsertInstance } from "./storage.js";
|
|
5
|
+
function cloneInstance(record) {
|
|
6
|
+
return { ...record };
|
|
7
|
+
}
|
|
8
|
+
// Only refresh persisted session metadata after commands that can plausibly change
|
|
9
|
+
// the instance identity/details we store in instances.json. Most RPCs mutate transient
|
|
10
|
+
// runtime state only, so forcing a follow-up get_state after every command is wasted IO.
|
|
11
|
+
//
|
|
12
|
+
// - new_session / switch_session / fork / clone can change sessionId/sessionFile
|
|
13
|
+
// - set_session_name changes a persisted session detail we may want reflected externally
|
|
14
|
+
// - prompt can materialize or advance persisted session state after the child processes it
|
|
15
|
+
const SESSION_METADATA_COMMANDS = new Set([
|
|
16
|
+
"new_session",
|
|
17
|
+
"switch_session",
|
|
18
|
+
"fork",
|
|
19
|
+
"clone",
|
|
20
|
+
"set_session_name",
|
|
21
|
+
"prompt",
|
|
22
|
+
]);
|
|
23
|
+
function shouldRefreshSessionMetadata(command) {
|
|
24
|
+
return SESSION_METADATA_COMMANDS.has(command.type);
|
|
25
|
+
}
|
|
26
|
+
function isGetStateSuccess(response) {
|
|
27
|
+
return response.success === true && response.command === "get_state" && "data" in response;
|
|
28
|
+
}
|
|
29
|
+
export class OrchestratorSupervisor {
|
|
30
|
+
liveInstances = new Map();
|
|
31
|
+
setStatus(live, status) {
|
|
32
|
+
live.record = {
|
|
33
|
+
...live.record,
|
|
34
|
+
status,
|
|
35
|
+
lastSeenAt: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
upsertInstance(live.record);
|
|
38
|
+
}
|
|
39
|
+
updateRecord(live, updates) {
|
|
40
|
+
live.record = {
|
|
41
|
+
...live.record,
|
|
42
|
+
...updates,
|
|
43
|
+
lastSeenAt: new Date().toISOString(),
|
|
44
|
+
};
|
|
45
|
+
if (updates.radiusPiId !== undefined) {
|
|
46
|
+
live.resources.radiusPiId = updates.radiusPiId;
|
|
47
|
+
}
|
|
48
|
+
if (updates.sessionId !== undefined) {
|
|
49
|
+
live.resources.sessionId = updates.sessionId;
|
|
50
|
+
}
|
|
51
|
+
upsertInstance(live.record);
|
|
52
|
+
}
|
|
53
|
+
clearBindings(live) {
|
|
54
|
+
live.unsubscribeEvents?.();
|
|
55
|
+
live.unsubscribeExit?.();
|
|
56
|
+
live.unsubscribeEvents = undefined;
|
|
57
|
+
live.unsubscribeExit = undefined;
|
|
58
|
+
live.onUiRequest = undefined;
|
|
59
|
+
live.resources.rpcProcess?.setUiRequestHandler(undefined);
|
|
60
|
+
}
|
|
61
|
+
bindRpcProcess(live, rpcProcess) {
|
|
62
|
+
this.clearBindings(live);
|
|
63
|
+
live.resources.rpcProcess = rpcProcess;
|
|
64
|
+
live.unsubscribeEvents = rpcProcess.onEvent((event) => {
|
|
65
|
+
for (const subscriber of live.subscribers) {
|
|
66
|
+
subscriber(event);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
live.unsubscribeExit = rpcProcess.onExit((error) => {
|
|
70
|
+
void this.handleUnexpectedRpcExit(live, error);
|
|
71
|
+
});
|
|
72
|
+
rpcProcess.setUiRequestHandler((request) => {
|
|
73
|
+
live.onUiRequest?.(request);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async handleUnexpectedRpcExit(live, _error) {
|
|
77
|
+
if (this.liveInstances.get(live.record.id) !== live) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (live.record.status === "stopping" || live.record.status === "stopped") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.setStatus(live, "error");
|
|
84
|
+
this.clearBindings(live);
|
|
85
|
+
live.resources.rpcProcess = undefined;
|
|
86
|
+
if (live.resources.radiusPiId) {
|
|
87
|
+
try {
|
|
88
|
+
await radiusPresence.disconnectPi(live.record);
|
|
89
|
+
this.updateRecord(live, { radiusPiId: undefined });
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error(`Failed to disconnect Radius Pi ${live.record.id}: ${String(error)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.liveInstances.delete(live.record.id);
|
|
96
|
+
}
|
|
97
|
+
getRpcProcess(live) {
|
|
98
|
+
return live.resources.rpcProcess;
|
|
99
|
+
}
|
|
100
|
+
async syncInstanceRecord(live) {
|
|
101
|
+
const rpcProcess = this.getRpcProcess(live);
|
|
102
|
+
if (!rpcProcess) {
|
|
103
|
+
this.updateRecord(live, {});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const response = await rpcProcess.send({ type: "get_state" });
|
|
107
|
+
if (!isGetStateSuccess(response)) {
|
|
108
|
+
this.updateRecord(live, {});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
this.updateRecord(live, {
|
|
112
|
+
sessionId: response.data.sessionId,
|
|
113
|
+
sessionFile: response.data.sessionFile,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async cleanupAcquiredResources(live) {
|
|
117
|
+
const rpcProcess = live.resources.rpcProcess;
|
|
118
|
+
this.clearBindings(live);
|
|
119
|
+
if (live.resources.radiusPiId) {
|
|
120
|
+
await radiusPresence.disconnectPi(live.record);
|
|
121
|
+
live.resources.radiusPiId = undefined;
|
|
122
|
+
live.record = {
|
|
123
|
+
...live.record,
|
|
124
|
+
radiusPiId: undefined,
|
|
125
|
+
lastSeenAt: new Date().toISOString(),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
live.resources.sessionId = undefined;
|
|
129
|
+
if (rpcProcess) {
|
|
130
|
+
live.resources.rpcProcess = undefined;
|
|
131
|
+
await rpcProcess.dispose();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async failSpawn(live, error) {
|
|
135
|
+
this.setStatus(live, "error");
|
|
136
|
+
try {
|
|
137
|
+
await this.cleanupAcquiredResources(live);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
this.setStatus(live, "stopped");
|
|
141
|
+
this.liveInstances.delete(live.record.id);
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
updateInstance(instance) {
|
|
146
|
+
const live = this.liveInstances.get(instance.id);
|
|
147
|
+
if (live) {
|
|
148
|
+
live.record = instance;
|
|
149
|
+
live.resources.radiusPiId = instance.radiusPiId;
|
|
150
|
+
live.resources.sessionId = instance.sessionId;
|
|
151
|
+
}
|
|
152
|
+
upsertInstance(instance);
|
|
153
|
+
}
|
|
154
|
+
openRpcStream(instanceId, onEvent, onUiRequest) {
|
|
155
|
+
const live = this.liveInstances.get(instanceId);
|
|
156
|
+
const rpcProcess = live ? this.getRpcProcess(live) : undefined;
|
|
157
|
+
if (!live || !rpcProcess) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
live.subscribers.add(onEvent);
|
|
161
|
+
live.onUiRequest = onUiRequest;
|
|
162
|
+
return {
|
|
163
|
+
handleRpc: async (command) => {
|
|
164
|
+
const response = await rpcProcess.send(command);
|
|
165
|
+
if (shouldRefreshSessionMetadata(command)) {
|
|
166
|
+
await this.syncInstanceRecord(live);
|
|
167
|
+
}
|
|
168
|
+
return response;
|
|
169
|
+
},
|
|
170
|
+
handleUiResponse: (response) => {
|
|
171
|
+
rpcProcess.handleUiResponse(response);
|
|
172
|
+
},
|
|
173
|
+
close: () => {
|
|
174
|
+
if (live.onUiRequest === onUiRequest) {
|
|
175
|
+
live.onUiRequest = undefined;
|
|
176
|
+
}
|
|
177
|
+
live.subscribers.delete(onEvent);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
getLiveInstance(instanceId) {
|
|
182
|
+
const live = this.liveInstances.get(instanceId);
|
|
183
|
+
return live ? cloneInstance(live.record) : undefined;
|
|
184
|
+
}
|
|
185
|
+
listLiveInstances() {
|
|
186
|
+
return [...this.liveInstances.values()].map((live) => cloneInstance(live.record));
|
|
187
|
+
}
|
|
188
|
+
async recoverAfterRestart() {
|
|
189
|
+
const recoveredAt = new Date().toISOString();
|
|
190
|
+
const instances = loadInstances().map((instance) => ({
|
|
191
|
+
...instance,
|
|
192
|
+
status: instance.status === "online" || instance.status === "starting" ? "stopped" : instance.status,
|
|
193
|
+
lastSeenAt: recoveredAt,
|
|
194
|
+
}));
|
|
195
|
+
for (const instance of instances) {
|
|
196
|
+
await radiusPresence.disconnectPi(instance);
|
|
197
|
+
}
|
|
198
|
+
saveInstances(instances);
|
|
199
|
+
}
|
|
200
|
+
listInstances() {
|
|
201
|
+
return loadInstances().map(cloneInstance);
|
|
202
|
+
}
|
|
203
|
+
getInstance(instanceId) {
|
|
204
|
+
const live = this.liveInstances.get(instanceId);
|
|
205
|
+
if (live) {
|
|
206
|
+
return cloneInstance(live.record);
|
|
207
|
+
}
|
|
208
|
+
const stored = getInstance(instanceId);
|
|
209
|
+
return stored ? cloneInstance(stored) : undefined;
|
|
210
|
+
}
|
|
211
|
+
async spawnInstance(options) {
|
|
212
|
+
const now = new Date().toISOString();
|
|
213
|
+
const live = {
|
|
214
|
+
record: {
|
|
215
|
+
id: randomUUID(),
|
|
216
|
+
status: "starting",
|
|
217
|
+
cwd: options.cwd,
|
|
218
|
+
createdAt: now,
|
|
219
|
+
lastSeenAt: now,
|
|
220
|
+
label: options.label,
|
|
221
|
+
},
|
|
222
|
+
resources: {},
|
|
223
|
+
subscribers: new Set(),
|
|
224
|
+
};
|
|
225
|
+
this.liveInstances.set(live.record.id, live);
|
|
226
|
+
upsertInstance(live.record);
|
|
227
|
+
try {
|
|
228
|
+
const rpcProcess = createRpcProcessInstance({ cwd: options.cwd });
|
|
229
|
+
this.bindRpcProcess(live, rpcProcess);
|
|
230
|
+
await this.syncInstanceRecord(live);
|
|
231
|
+
const registeredRecord = await radiusPresence.registerPi(live.record);
|
|
232
|
+
this.updateRecord(live, { radiusPiId: registeredRecord.radiusPiId });
|
|
233
|
+
this.setStatus(live, "online");
|
|
234
|
+
return cloneInstance(live.record);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
return await this.failSpawn(live, error);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async stopInstance(instanceId) {
|
|
241
|
+
const live = this.liveInstances.get(instanceId);
|
|
242
|
+
if (!live) {
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
this.setStatus(live, "stopping");
|
|
246
|
+
try {
|
|
247
|
+
await this.cleanupAcquiredResources(live);
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
live.record = {
|
|
251
|
+
...live.record,
|
|
252
|
+
status: "stopped",
|
|
253
|
+
lastSeenAt: new Date().toISOString(),
|
|
254
|
+
};
|
|
255
|
+
this.liveInstances.delete(instanceId);
|
|
256
|
+
removeInstance(instanceId);
|
|
257
|
+
}
|
|
258
|
+
return cloneInstance(live.record);
|
|
259
|
+
}
|
|
260
|
+
async handleRpc(instanceId, command) {
|
|
261
|
+
const live = this.liveInstances.get(instanceId);
|
|
262
|
+
const rpcProcess = live ? this.getRpcProcess(live) : undefined;
|
|
263
|
+
if (!live || !rpcProcess) {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
const response = await rpcProcess.send(command);
|
|
267
|
+
if (shouldRefreshSessionMetadata(command)) {
|
|
268
|
+
await this.syncInstanceRecord(live);
|
|
269
|
+
}
|
|
270
|
+
return response;
|
|
271
|
+
}
|
|
272
|
+
async shutdown() {
|
|
273
|
+
for (const instanceId of [...this.liveInstances.keys()]) {
|
|
274
|
+
await this.stopInstance(instanceId);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
export const supervisor = new OrchestratorSupervisor();
|
|
279
|
+
radiusPresence.setCoordinator({
|
|
280
|
+
getLiveInstance(instanceId) {
|
|
281
|
+
return supervisor.getLiveInstance(instanceId);
|
|
282
|
+
},
|
|
283
|
+
listLiveInstances() {
|
|
284
|
+
return supervisor.listLiveInstances();
|
|
285
|
+
},
|
|
286
|
+
updateInstance(instance) {
|
|
287
|
+
supervisor.updateInstance(instance);
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
//# sourceMappingURL=supervisor.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@earendil-works/pi-orchestrator",
|
|
3
|
+
"version": "0.80.2",
|
|
4
|
+
"description": "experimental orchestrator package for pi",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "shx rm -rf dist",
|
|
21
|
+
"dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
22
|
+
"build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js",
|
|
23
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"pi",
|
|
27
|
+
"orchestrator"
|
|
28
|
+
],
|
|
29
|
+
"author": "Earendil Works",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/earendil-works/pi.git",
|
|
34
|
+
"directory": "packages/orchestrator"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22.19.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@earendil-works/pi-coding-agent": "0.80.2"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"shx": "0.4.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -371,6 +371,9 @@ export class Markdown {
|
|
|
371
371
|
};
|
|
372
372
|
for (const token of tokens) {
|
|
373
373
|
switch (token.type) {
|
|
374
|
+
case "escape":
|
|
375
|
+
result += applyTextWithNewlines(this.options.preserveBackslashEscapes ? token.raw : token.text);
|
|
376
|
+
break;
|
|
374
377
|
case "text":
|
|
375
378
|
// Text tokens in list items can have nested tokens for inline formatting
|
|
376
379
|
if (token.tokens && token.tokens.length > 0) {
|
|
@@ -8,7 +8,7 @@ This package contains type definitions for node (https://nodejs.org/).
|
|
|
8
8
|
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node.
|
|
9
9
|
|
|
10
10
|
### Additional Details
|
|
11
|
-
* Last updated:
|
|
11
|
+
* Last updated: Wed, 24 Jun 2026 20:32:59 GMT
|
|
12
12
|
* Dependencies: [undici-types](https://npmjs.com/package/undici-types)
|
|
13
13
|
|
|
14
14
|
# Credits
|
|
@@ -33,7 +33,10 @@ declare module "node:diagnostics_channel" {
|
|
|
33
33
|
* @param name The channel name
|
|
34
34
|
* @return The named channel object
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
// eslint-disable-next-line @definitelytyped/no-unnecessary-generics
|
|
37
|
+
function channel<ContextType = any, StoreType = ContextType>(
|
|
38
|
+
name: string | symbol,
|
|
39
|
+
): Channel<ContextType, StoreType>;
|
|
37
40
|
type ChannelListener = (message: unknown, name: string | symbol) => void;
|
|
38
41
|
/**
|
|
39
42
|
* Register a message handler to subscribe to this channel. This message handler
|
|
@@ -96,12 +99,9 @@ declare module "node:diagnostics_channel" {
|
|
|
96
99
|
* @param nameOrChannels Channel name or object containing all the `TracingChannel Channels`
|
|
97
100
|
* @return Collection of channels to trace with
|
|
98
101
|
*/
|
|
99
|
-
function tracingChannel<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
>(
|
|
103
|
-
nameOrChannels: string | TracingChannelCollection<StoreType, ContextType>,
|
|
104
|
-
): TracingChannel<StoreType, ContextType>;
|
|
102
|
+
function tracingChannel<ContextType extends object = object, StoreType = ContextType>(
|
|
103
|
+
nameOrChannels: string | TracingChannelCollection<ContextType, StoreType>,
|
|
104
|
+
): TracingChannel<ContextType, StoreType>;
|
|
105
105
|
/**
|
|
106
106
|
* The class `Channel` represents an individual named channel within the data
|
|
107
107
|
* pipeline. It is used to track subscribers and to publish messages when there
|
|
@@ -111,7 +111,7 @@ declare module "node:diagnostics_channel" {
|
|
|
111
111
|
* with `new Channel(name)` is not supported.
|
|
112
112
|
* @since v15.1.0, v14.17.0
|
|
113
113
|
*/
|
|
114
|
-
class Channel<
|
|
114
|
+
class Channel<ContextType = any, StoreType = ContextType> {
|
|
115
115
|
readonly name: string | symbol;
|
|
116
116
|
/**
|
|
117
117
|
* Check if there are active subscribers to this channel. This is helpful if
|
|
@@ -304,12 +304,12 @@ declare module "node:diagnostics_channel" {
|
|
|
304
304
|
},
|
|
305
305
|
) => void;
|
|
306
306
|
}
|
|
307
|
-
interface TracingChannelCollection<
|
|
308
|
-
start: Channel<
|
|
309
|
-
end: Channel<
|
|
310
|
-
asyncStart: Channel<
|
|
311
|
-
asyncEnd: Channel<
|
|
312
|
-
error: Channel<
|
|
307
|
+
interface TracingChannelCollection<ContextType extends object = object, StoreType = ContextType> {
|
|
308
|
+
start: Channel<ContextType, StoreType>;
|
|
309
|
+
end: Channel<ContextType, StoreType>;
|
|
310
|
+
asyncStart: Channel<ContextType, StoreType>;
|
|
311
|
+
asyncEnd: Channel<ContextType, StoreType>;
|
|
312
|
+
error: Channel<ContextType, StoreType>;
|
|
313
313
|
}
|
|
314
314
|
/**
|
|
315
315
|
* The class `TracingChannel` is a collection of `TracingChannel Channels` which
|
|
@@ -320,12 +320,9 @@ declare module "node:diagnostics_channel" {
|
|
|
320
320
|
* @since v19.9.0
|
|
321
321
|
* @experimental
|
|
322
322
|
*/
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
asyncStart: Channel<StoreType, ContextType>;
|
|
327
|
-
asyncEnd: Channel<StoreType, ContextType>;
|
|
328
|
-
error: Channel<StoreType, ContextType>;
|
|
323
|
+
interface TracingChannel<ContextType extends object = object, StoreType = ContextType>
|
|
324
|
+
extends TracingChannelCollection<ContextType, StoreType>
|
|
325
|
+
{
|
|
329
326
|
/**
|
|
330
327
|
* Helper to subscribe a collection of functions to the corresponding channels.
|
|
331
328
|
* This is the same as calling `channel.subscribe(onMessage)` on each channel
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@types/node",
|
|
3
|
-
"version": "26.0.
|
|
3
|
+
"version": "26.0.1",
|
|
4
4
|
"description": "TypeScript definitions for node",
|
|
5
5
|
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node",
|
|
6
6
|
"license": "MIT",
|
|
@@ -150,6 +150,6 @@
|
|
|
150
150
|
"undici-types": "~8.3.0"
|
|
151
151
|
},
|
|
152
152
|
"peerDependencies": {},
|
|
153
|
-
"typesPublisherContentHash": "
|
|
153
|
+
"typesPublisherContentHash": "a354aaa75dcea5eab474337cc3c7f8ab35ebed8c28d8cdb987dc31049b0fc0c6",
|
|
154
154
|
"typeScriptVersion": "5.6"
|
|
155
155
|
}
|
|
@@ -95,19 +95,23 @@ function gte(i, y) {
|
|
|
95
95
|
function expand_(str, max, isTop) {
|
|
96
96
|
/** @type {string[]} */
|
|
97
97
|
const expansions = [];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
// The `{a},b}` rewrite below restarts expansion on a rewritten string with
|
|
99
|
+
// the same `max` and `isTop = true`. Loop instead of recursing so a long run
|
|
100
|
+
// of non-expanding `{}` groups can't exhaust the call stack.
|
|
101
|
+
for (;;) {
|
|
102
|
+
const m = (0, balanced_match_1.balanced)('{', '}', str);
|
|
103
|
+
if (!m)
|
|
104
|
+
return [str];
|
|
105
|
+
// no need to expand pre, since it is guaranteed to be free of brace-sets
|
|
106
|
+
const pre = m.pre;
|
|
107
|
+
if (/\$$/.test(m.pre)) {
|
|
108
|
+
const post = m.post.length ? expand_(m.post, max, false) : [''];
|
|
109
|
+
for (let k = 0; k < post.length && k < max; k++) {
|
|
110
|
+
const expansion = pre + '{' + m.body + '}' + post[k];
|
|
111
|
+
expansions.push(expansion);
|
|
112
|
+
}
|
|
113
|
+
return expansions;
|
|
108
114
|
}
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
115
|
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
112
116
|
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
113
117
|
const isSequence = isNumericSequence || isAlphaSequence;
|
|
@@ -116,10 +120,16 @@ function expand_(str, max, isTop) {
|
|
|
116
120
|
// {a},b}
|
|
117
121
|
if (m.post.match(/,(?!,).*\}/)) {
|
|
118
122
|
str = m.pre + '{' + m.body + escClose + m.post;
|
|
119
|
-
|
|
123
|
+
isTop = true;
|
|
124
|
+
continue;
|
|
120
125
|
}
|
|
121
126
|
return [str];
|
|
122
127
|
}
|
|
128
|
+
// Only expand post once we know this brace set actually expands. Computing
|
|
129
|
+
// it before the early returns above expanded post a second time on every
|
|
130
|
+
// non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up
|
|
131
|
+
// exponentially.
|
|
132
|
+
const post = m.post.length ? expand_(m.post, max, false) : [''];
|
|
123
133
|
let n;
|
|
124
134
|
if (isSequence) {
|
|
125
135
|
n = m.body.split(/\.\./);
|
|
@@ -195,7 +205,7 @@ function expand_(str, max, isTop) {
|
|
|
195
205
|
}
|
|
196
206
|
}
|
|
197
207
|
}
|
|
208
|
+
return expansions;
|
|
198
209
|
}
|
|
199
|
-
return expansions;
|
|
200
210
|
}
|
|
201
211
|
//# sourceMappingURL=index.js.map
|