@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.
Files changed (108) hide show
  1. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +5 -2
  2. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +2 -1
  3. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +1 -1
  4. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  5. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +51 -47
  6. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +9 -3
  7. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +9 -6
  8. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare.js +0 -18
  9. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +6 -3
  10. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +6 -3
  11. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +1 -1
  12. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +15 -25
  13. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +30 -26
  14. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +189 -204
  15. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +24 -21
  16. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -5
  17. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +11 -2
  18. package/node_modules/@earendil-works/pi-ai/dist/auth/helpers.js +2 -2
  19. package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +19 -4
  20. package/node_modules/@earendil-works/pi-ai/dist/compat.js +81 -7
  21. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
  22. package/node_modules/@earendil-works/pi-ai/dist/images-models.js +7 -3
  23. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  24. package/node_modules/@earendil-works/pi-ai/dist/legacy-api-aliases.js +49 -0
  25. package/node_modules/@earendil-works/pi-ai/dist/models.js +7 -3
  26. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +25 -8
  27. package/node_modules/@earendil-works/pi-ai/dist/providers/ant-ling.models.js +3 -3
  28. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +0 -34
  29. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +2 -0
  30. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.js +2 -2
  31. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +21 -9
  32. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +85 -0
  33. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.js +2 -2
  34. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-workers-ai.models.js +13 -13
  35. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -2
  36. package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +1 -20
  37. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +19 -1
  38. package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js +72 -0
  39. package/node_modules/@earendil-works/pi-ai/dist/providers/minimax-cn.models.js +4 -4
  40. package/node_modules/@earendil-works/pi-ai/dist/providers/minimax.models.js +4 -4
  41. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +19 -0
  42. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +11 -11
  43. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +15 -15
  44. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +310 -39
  45. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +37 -18
  46. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +349 -77
  47. package/node_modules/@earendil-works/pi-ai/dist/providers/xai.models.js +7 -0
  48. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +6 -6
  49. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +6 -6
  50. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +95 -0
  51. package/node_modules/@earendil-works/pi-ai/dist/utils/headers.js +10 -0
  52. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +2 -2
  53. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +86 -0
  54. package/node_modules/@earendil-works/pi-ai/package.json +1 -33
  55. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +13 -25
  56. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +15 -30
  57. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +6 -3
  58. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +5 -1
  59. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-registry.js +2 -4
  60. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +1 -1
  61. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +2 -0
  62. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +9 -5
  63. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +11 -0
  64. package/node_modules/@earendil-works/pi-coding-agent/dist/core/timings.js +24 -14
  65. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +12 -25
  66. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +20 -4
  67. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +9 -9
  68. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +12 -3
  69. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/session-selector.js +14 -3
  70. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +1 -1
  71. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +37 -25
  72. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
  73. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
  74. package/node_modules/@earendil-works/pi-coding-agent/dist/rpc-entry.js +10 -0
  75. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-convert.js +21 -15
  76. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-process.js +83 -0
  77. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/mime.js +41 -0
  78. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  79. package/node_modules/@earendil-works/pi-coding-agent/package.json +8 -5
  80. package/node_modules/@earendil-works/pi-orchestrator/dist/cli.js +136 -0
  81. package/node_modules/@earendil-works/pi-orchestrator/dist/config.js +55 -0
  82. package/node_modules/@earendil-works/pi-orchestrator/dist/handler.js +105 -0
  83. package/node_modules/@earendil-works/pi-orchestrator/dist/index.js +11 -0
  84. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/client.js +56 -0
  85. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/protocol.js +12 -0
  86. package/node_modules/@earendil-works/pi-orchestrator/dist/ipc/server.js +145 -0
  87. package/node_modules/@earendil-works/pi-orchestrator/dist/radius.js +365 -0
  88. package/node_modules/@earendil-works/pi-orchestrator/dist/rpc-process.js +168 -0
  89. package/node_modules/@earendil-works/pi-orchestrator/dist/serve.js +70 -0
  90. package/node_modules/@earendil-works/pi-orchestrator/dist/storage.js +58 -0
  91. package/node_modules/@earendil-works/pi-orchestrator/dist/supervisor.js +290 -0
  92. package/node_modules/@earendil-works/pi-orchestrator/dist/types.js +2 -0
  93. package/node_modules/@earendil-works/pi-orchestrator/package.json +45 -0
  94. package/node_modules/@earendil-works/pi-tui/dist/components/markdown.js +3 -0
  95. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  96. package/node_modules/@types/node/README.md +1 -1
  97. package/node_modules/@types/node/diagnostics_channel.d.ts +17 -20
  98. package/node_modules/@types/node/package.json +2 -2
  99. package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
  100. package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
  101. package/node_modules/brace-expansion/dist/esm/index.js +24 -14
  102. package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
  103. package/node_modules/brace-expansion/package.json +2 -2
  104. package/node_modules/google-auth-library/build/src/auth/googleauth.js +1 -1
  105. package/node_modules/google-auth-library/package.json +4 -4
  106. package/package.json +7 -6
  107. package/slingshot/index.js +302 -304
  108. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.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) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-tui",
3
- "version": "0.79.10",
3
+ "version": "0.80.2",
4
4
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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: Fri, 19 Jun 2026 07:14:38 GMT
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
- function channel(name: string | symbol): Channel;
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
- StoreType = unknown,
101
- ContextType extends object = StoreType extends object ? StoreType : object,
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<StoreType = unknown, ContextType = StoreType> {
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<StoreType = unknown, ContextType = StoreType> {
308
- start: Channel<StoreType, ContextType>;
309
- end: Channel<StoreType, ContextType>;
310
- asyncStart: Channel<StoreType, ContextType>;
311
- asyncEnd: Channel<StoreType, ContextType>;
312
- error: Channel<StoreType, ContextType>;
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
- class TracingChannel<StoreType = unknown, ContextType extends object = {}> implements TracingChannelCollection {
324
- start: Channel<StoreType, ContextType>;
325
- end: Channel<StoreType, ContextType>;
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.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": "b97b21d94288bc153e105a65f4c8fe609dd471f179c5bbd931ba27f959bd0744",
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
- const m = (0, balanced_match_1.balanced)('{', '}', str);
99
- if (!m)
100
- return [str];
101
- // no need to expand pre, since it is guaranteed to be free of brace-sets
102
- const pre = m.pre;
103
- const post = m.post.length ? expand_(m.post, max, false) : [''];
104
- if (/\$$/.test(m.pre)) {
105
- for (let k = 0; k < post.length && k < max; k++) {
106
- const expansion = pre + '{' + m.body + '}' + post[k];
107
- expansions.push(expansion);
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
- return expand_(str, max, true);
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