@streichsbaer/pi-mesh 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/cli.js +144 -14
- package/dist/mesh.js +0 -2
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +6 -0
- package/dist/types.d.ts +0 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
npm install -g @streichsbaer/pi-mesh
|
|
15
|
+
npm install -g @streichsbaer/pi-mesh --ignore-scripts
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The package installs the `pi-mesh` binary.
|
|
18
|
+
The package installs the `pi-mesh` binary and does not require npm install scripts.
|
|
19
19
|
|
|
20
20
|
## Goals
|
|
21
21
|
|
|
@@ -33,6 +33,7 @@ pi-mesh sessions list --include-pi # include recent unmanaged Pi sessions
|
|
|
33
33
|
pi-mesh sessions find auth
|
|
34
34
|
pi-mesh sessions list --folder ./api
|
|
35
35
|
pi-mesh sessions list --label pi-mesh-development
|
|
36
|
+
pi-mesh sessions delete worker-api
|
|
36
37
|
pi-mesh models list sonnet --folder ./api --scoped
|
|
37
38
|
pi-mesh transcript <session> --last 3
|
|
38
39
|
pi-mesh state <session>
|
|
@@ -76,7 +77,6 @@ State is stored outside the repo:
|
|
|
76
77
|
```text
|
|
77
78
|
~/.pi/agent/pi-mesh/
|
|
78
79
|
registry.jsonl
|
|
79
|
-
inbox/
|
|
80
80
|
locks/
|
|
81
81
|
socket-dir
|
|
82
82
|
```
|
|
@@ -87,6 +87,8 @@ Live control sockets use short hashed paths under a private randomized runtime d
|
|
|
87
87
|
|
|
88
88
|
Already-running normal Pi sessions can be discovered and read from their JSONL files. `pi-mesh sessions list` shows managed sessions by default; pass `--include-pi` or `--all` to include recent unmanaged Pi sessions. To message one, close the original process first and attach/resume it through `pi-mesh attach` so pi-mesh can own the live control socket.
|
|
89
89
|
|
|
90
|
+
Use `pi-mesh sessions delete <session>` to remove a stopped managed session from the local registry. Active sessions, including records with a live process or socket, must be stopped first. The Pi JSONL session file is kept unless `--delete-file` is passed and confirmed; pass `--force` to skip that file-delete confirmation.
|
|
91
|
+
|
|
90
92
|
## Install for development
|
|
91
93
|
|
|
92
94
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
4
5
|
import { exists } from "./utils.js";
|
|
5
6
|
import { formatTimestamp, truncate, compactWhitespace } from "./utils.js";
|
|
6
7
|
import { loadSessionData, loadSessionDataForSession, renderToolLine, resolveSessionSpec, searchSessions, tail } from "./pi-session-parser.js";
|
|
7
|
-
import { createMeshId, filterManagedSessions, findManagedSessions, listManagedSessions, lockPathFor, normalizeLabels, socketPathFor, upsertManagedSession } from "./registry.js";
|
|
8
|
+
import { createMeshId, deleteManagedSession, filterManagedSessions, findManagedSessionById, findManagedSessions, listManagedSessions, lockPathFor, normalizeLabels, socketPathFor, upsertManagedSession } from "./registry.js";
|
|
8
9
|
import { resolveMesh } from "./mesh.js";
|
|
9
10
|
import { withDirectoryLock } from "./lock.js";
|
|
10
11
|
import { mergeModelSelection } from "./model-selection.js";
|
|
@@ -137,6 +138,15 @@ async function getSessionSelector(args, spec) {
|
|
|
137
138
|
function isLiveStatus(status) {
|
|
138
139
|
return status === "running" || status === "idle" || status === "busy" || status === "starting";
|
|
139
140
|
}
|
|
141
|
+
async function activeSessionReason(record) {
|
|
142
|
+
if (isLiveStatus(record.status))
|
|
143
|
+
return `status=${record.status}`;
|
|
144
|
+
if (record.socketPath && await exists(record.socketPath))
|
|
145
|
+
return `socket=${record.socketPath}`;
|
|
146
|
+
if (record.status === "error" && isProcessAlive(record.pid))
|
|
147
|
+
return `pid=${record.pid}`;
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
140
150
|
function formatMatches(records) {
|
|
141
151
|
return records.map((record) => `- ${record.meshId}${record.name ? ` name=${JSON.stringify(record.name)}` : ""} folder=${record.folder} status=${record.status}`).join("\n");
|
|
142
152
|
}
|
|
@@ -147,14 +157,44 @@ function isProcessAlive(pid) {
|
|
|
147
157
|
process.kill(pid, 0);
|
|
148
158
|
return true;
|
|
149
159
|
}
|
|
150
|
-
catch {
|
|
151
|
-
return
|
|
160
|
+
catch (error) {
|
|
161
|
+
return error.code === "EPERM";
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
function isStaleSocketError(error) {
|
|
155
165
|
const code = error?.code;
|
|
156
166
|
return code === "ECONNREFUSED" || code === "ENOENT" || code === "EPIPE" || code === "ECONNRESET";
|
|
157
167
|
}
|
|
168
|
+
async function confirmDeleteSessionFile(sessionFile) {
|
|
169
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
170
|
+
throw new Error("--delete-file requires an interactive terminal. Pass --force to delete the session file without confirmation.");
|
|
171
|
+
}
|
|
172
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
173
|
+
try {
|
|
174
|
+
for (;;) {
|
|
175
|
+
const answer = (await rl.question(`Delete session file ${sessionFile}? [Y/n] `)).trim().toLowerCase();
|
|
176
|
+
if (answer === "" || answer === "y" || answer === "yes")
|
|
177
|
+
return true;
|
|
178
|
+
if (answer === "n" || answer === "no")
|
|
179
|
+
return false;
|
|
180
|
+
console.error("Please answer y or n.");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
rl.close();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function removeExistingFile(filePath) {
|
|
188
|
+
try {
|
|
189
|
+
await fs.rm(filePath);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
if (error.code === "ENOENT")
|
|
194
|
+
return false;
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
158
198
|
async function refreshStaleManagedSessions(mesh, records) {
|
|
159
199
|
const refreshed = [];
|
|
160
200
|
for (const record of records) {
|
|
@@ -178,6 +218,7 @@ function printHelp() {
|
|
|
178
218
|
Usage:
|
|
179
219
|
pi-mesh sessions list [--folder <dir>] [--name <name>] [--label <label>] [--limit 25] [--json] [--include-pi|--all]
|
|
180
220
|
pi-mesh sessions find <query> [--folder <dir>] [--name <name>] [--label <label>] [--limit 25] [--json] [--include-pi|--all]
|
|
221
|
+
pi-mesh sessions delete <session> [--folder <dir>] [--name <name>] [--label <label>] [--delete-file] [--force] [--json]
|
|
181
222
|
pi-mesh transcript <session> [--folder <dir>] [--name <name>] [--label <label>] [--last 3] [--json] [--show-tools]
|
|
182
223
|
pi-mesh state <session> [--folder <dir>] [--name <name>] [--label <label>] [--json]
|
|
183
224
|
pi-mesh models list [search] [--folder <dir>] [--json] [--all] [--scoped]
|
|
@@ -256,6 +297,51 @@ async function cmdSessions(parsed) {
|
|
|
256
297
|
}
|
|
257
298
|
return;
|
|
258
299
|
}
|
|
300
|
+
if (sub === "delete" || sub === "remove" || sub === "forget") {
|
|
301
|
+
const spec = parsed.positionals[2];
|
|
302
|
+
if (!spec)
|
|
303
|
+
throw new Error("Usage: pi-mesh sessions delete <session>");
|
|
304
|
+
const selector = await getSessionSelector(parsed, spec);
|
|
305
|
+
const matches = await findManagedSessions(mesh, selector);
|
|
306
|
+
if (matches.length === 0)
|
|
307
|
+
throw new Error(`No managed session matches ${JSON.stringify(spec)}.`);
|
|
308
|
+
if (matches.length > 1)
|
|
309
|
+
throw new Error(`Multiple managed sessions match ${JSON.stringify(spec)}; refine with --folder, --name, or --label:\n${formatMatches(matches)}`);
|
|
310
|
+
const deleteFileRequested = getBool(parsed, "delete-file");
|
|
311
|
+
const force = getBool(parsed, "force");
|
|
312
|
+
const confirmedSessionFile = matches[0].sessionFile;
|
|
313
|
+
await assertNotAlreadyActive(matches[0], "deleting it");
|
|
314
|
+
const deleteFileApproved = deleteFileRequested && (force || await confirmDeleteSessionFile(confirmedSessionFile));
|
|
315
|
+
const result = await withDirectoryLock(lockPathFor(mesh, matches[0].meshId), async () => {
|
|
316
|
+
const record = await findManagedSessionById(mesh, matches[0].meshId);
|
|
317
|
+
if (!record)
|
|
318
|
+
throw new Error(`Session ${matches[0].meshId} was deleted concurrently.`);
|
|
319
|
+
await assertNotAlreadyActive(record, "deleting it");
|
|
320
|
+
let deletedFile = false;
|
|
321
|
+
if (deleteFileApproved) {
|
|
322
|
+
if (!force && record.sessionFile !== confirmedSessionFile) {
|
|
323
|
+
throw new Error(`Session file changed from ${confirmedSessionFile} to ${record.sessionFile}; rerun delete to confirm the new file.`);
|
|
324
|
+
}
|
|
325
|
+
deletedFile = await removeExistingFile(record.sessionFile);
|
|
326
|
+
}
|
|
327
|
+
if (record.socketPath)
|
|
328
|
+
await fs.rm(record.socketPath, { force: true }).catch(() => undefined);
|
|
329
|
+
await deleteManagedSession(mesh, record.meshId);
|
|
330
|
+
return { record, deletedFile };
|
|
331
|
+
});
|
|
332
|
+
if (asJson) {
|
|
333
|
+
console.log(JSON.stringify({ ok: true, deleted: result.record, deletedFile: result.deletedFile }, null, 2));
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
console.log(`Deleted managed session ${result.record.meshId} from the pi-mesh registry.`);
|
|
337
|
+
if (result.deletedFile)
|
|
338
|
+
console.log(`Deleted session file ${result.record.sessionFile}.`);
|
|
339
|
+
else if (deleteFileRequested)
|
|
340
|
+
console.log(`Kept session file ${result.record.sessionFile}.`);
|
|
341
|
+
else
|
|
342
|
+
console.log(`Kept session file ${result.record.sessionFile}. Pass --delete-file to remove it too.`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
259
345
|
if (sub === "find") {
|
|
260
346
|
const query = parsed.positionals.slice(2).join(" ").trim();
|
|
261
347
|
if (!query)
|
|
@@ -443,8 +529,8 @@ async function registerSession(mesh, input) {
|
|
|
443
529
|
folder: input.folder,
|
|
444
530
|
sessionFile: input.sessionFile,
|
|
445
531
|
rawSessionId: input.rawSessionId,
|
|
446
|
-
pid: process.pid,
|
|
447
|
-
socketPath: input.socketPath,
|
|
532
|
+
pid: input.status === "offline" ? undefined : process.pid,
|
|
533
|
+
socketPath: input.status === "offline" ? undefined : input.socketPath,
|
|
448
534
|
createdAt: now,
|
|
449
535
|
updatedAt: now,
|
|
450
536
|
lastError: input.lastError,
|
|
@@ -453,12 +539,29 @@ async function registerSession(mesh, input) {
|
|
|
453
539
|
}
|
|
454
540
|
async function upsertManagedStatus(mesh, record, patch) {
|
|
455
541
|
const pendingModelSelection = record.pendingModelSelection && (await exists(record.sessionFile)) ? undefined : record.pendingModelSelection;
|
|
456
|
-
|
|
542
|
+
const next = { ...record, pendingModelSelection, ...patch };
|
|
543
|
+
if (next.status === "offline") {
|
|
544
|
+
next.pid = undefined;
|
|
545
|
+
next.socketPath = undefined;
|
|
546
|
+
}
|
|
547
|
+
return upsertManagedSession(mesh, next);
|
|
457
548
|
}
|
|
458
|
-
function
|
|
459
|
-
if (!record
|
|
549
|
+
async function assertNotAlreadyActive(record, action) {
|
|
550
|
+
if (!record)
|
|
551
|
+
return;
|
|
552
|
+
const activeReason = await activeSessionReason(record);
|
|
553
|
+
if (!activeReason)
|
|
460
554
|
return;
|
|
461
|
-
throw new Error(`Session ${record.meshId} is already
|
|
555
|
+
throw new Error(`Session ${record.meshId} is already active (${activeReason}); use pi-mesh send or stop that TUI before ${action}.`);
|
|
556
|
+
}
|
|
557
|
+
async function claimManagedSessionForActivation(mesh, record, patch, action) {
|
|
558
|
+
return withDirectoryLock(lockPathFor(mesh, record.meshId), async () => {
|
|
559
|
+
const latest = await findManagedSessionById(mesh, record.meshId);
|
|
560
|
+
if (!latest)
|
|
561
|
+
throw new Error(`Session ${record.meshId} was deleted concurrently.`);
|
|
562
|
+
await assertNotAlreadyActive(latest, action);
|
|
563
|
+
return upsertManagedSession(mesh, { ...latest, ...patch });
|
|
564
|
+
});
|
|
462
565
|
}
|
|
463
566
|
async function cmdSpawn(parsed) {
|
|
464
567
|
const folder = await resolveFolder(getString(parsed, "folder") || process.cwd());
|
|
@@ -524,6 +627,8 @@ async function cmdSpawn(parsed) {
|
|
|
524
627
|
sessionFile: result.sessionFile || record.sessionFile,
|
|
525
628
|
rawSessionId: result.rawSessionId,
|
|
526
629
|
status: "offline",
|
|
630
|
+
pid: undefined,
|
|
631
|
+
socketPath: undefined,
|
|
527
632
|
pendingModelSelection: undefined,
|
|
528
633
|
});
|
|
529
634
|
}
|
|
@@ -549,15 +654,26 @@ async function cmdRun(parsed) {
|
|
|
549
654
|
throw new Error(`Multiple sessions named ${JSON.stringify(name)} match this folder/label selection; use a session id or --new:\n${formatMatches(matches)}`);
|
|
550
655
|
existing = matches[0];
|
|
551
656
|
}
|
|
552
|
-
|
|
553
|
-
const sessionFile = existing?.sessionFile;
|
|
657
|
+
await assertNotAlreadyActive(existing, "opening another live TUI");
|
|
554
658
|
const pendingModelSelection = existing && !(await exists(existing.sessionFile)) ? existing.pendingModelSelection : undefined;
|
|
555
659
|
const seedModelSelection = mergeModelSelection(pendingModelSelection, rawModelSelection);
|
|
556
660
|
const runFolder = existing?.folder || folder;
|
|
557
661
|
const modelSelection = await validateCliModelSelection(runFolder, seedModelSelection);
|
|
558
|
-
const meshId = existing?.meshId || createMeshId({ folder: runFolder, sessionFile });
|
|
662
|
+
const meshId = existing?.meshId || createMeshId({ folder: runFolder, sessionFile: existing?.sessionFile });
|
|
559
663
|
const socketPath = await socketPathFor(mesh, meshId);
|
|
560
664
|
let record = existing;
|
|
665
|
+
if (record) {
|
|
666
|
+
record = await claimManagedSessionForActivation(mesh, record, {
|
|
667
|
+
name,
|
|
668
|
+
labels: labels.length ? labels : record.labels,
|
|
669
|
+
kind: "interactive",
|
|
670
|
+
status: "starting",
|
|
671
|
+
pid: process.pid,
|
|
672
|
+
socketPath,
|
|
673
|
+
pendingModelSelection: modelSelection && !(await exists(record.sessionFile)) ? modelSelection : undefined,
|
|
674
|
+
}, "opening another live TUI");
|
|
675
|
+
}
|
|
676
|
+
const sessionFile = record?.sessionFile;
|
|
561
677
|
const { runInteractive } = await import("./pi-runner.js");
|
|
562
678
|
await runInteractive({
|
|
563
679
|
cwd: runFolder,
|
|
@@ -602,7 +718,7 @@ async function cmdAttach(parsed) {
|
|
|
602
718
|
const mesh = await getMesh();
|
|
603
719
|
await refreshStaleManagedSessions(mesh, await listManagedSessions(mesh));
|
|
604
720
|
const resolved = await resolveSessionFile(mesh, spec);
|
|
605
|
-
|
|
721
|
+
await assertNotAlreadyActive(resolved.managed, "attaching it again");
|
|
606
722
|
const folder = getString(parsed, "folder") ? await resolveFolder(getString(parsed, "folder")) : resolved.folder;
|
|
607
723
|
const pendingModelSelection = resolved.managed && !(await exists(resolved.managed.sessionFile)) ? resolved.managed.pendingModelSelection : undefined;
|
|
608
724
|
const seedModelSelection = mergeModelSelection(pendingModelSelection, rawModelSelection);
|
|
@@ -612,13 +728,25 @@ async function cmdAttach(parsed) {
|
|
|
612
728
|
const meshId = resolved.managed?.meshId || createMeshId({ folder, sessionFile: resolved.sessionFile });
|
|
613
729
|
const socketPath = await socketPathFor(mesh, meshId);
|
|
614
730
|
let record = resolved.managed;
|
|
731
|
+
if (record) {
|
|
732
|
+
record = await claimManagedSessionForActivation(mesh, record, {
|
|
733
|
+
name,
|
|
734
|
+
labels: labels.length ? labels : record.labels,
|
|
735
|
+
folder,
|
|
736
|
+
kind: "attached",
|
|
737
|
+
status: "starting",
|
|
738
|
+
pid: process.pid,
|
|
739
|
+
socketPath,
|
|
740
|
+
pendingModelSelection: modelSelection && !(await exists(record.sessionFile)) ? modelSelection : undefined,
|
|
741
|
+
}, "attaching it again");
|
|
742
|
+
}
|
|
615
743
|
if (!resolved.managed) {
|
|
616
744
|
console.error("Note: attaching an unmanaged Pi session. Close any other Pi process using the same JSONL file first.");
|
|
617
745
|
}
|
|
618
746
|
const { runInteractive } = await import("./pi-runner.js");
|
|
619
747
|
await runInteractive({
|
|
620
748
|
cwd: folder,
|
|
621
|
-
sessionFile: resolved.sessionFile,
|
|
749
|
+
sessionFile: record?.sessionFile || resolved.sessionFile,
|
|
622
750
|
name,
|
|
623
751
|
socketPath,
|
|
624
752
|
modelSelection,
|
|
@@ -696,6 +824,8 @@ async function deliverToManagedSession(mesh, managed, message, delivery, rawMode
|
|
|
696
824
|
sessionFile: result.sessionFile || managed.sessionFile,
|
|
697
825
|
rawSessionId: result.rawSessionId,
|
|
698
826
|
status: "offline",
|
|
827
|
+
pid: undefined,
|
|
828
|
+
socketPath: undefined,
|
|
699
829
|
lastError: undefined,
|
|
700
830
|
pendingModelSelection: undefined,
|
|
701
831
|
});
|
package/dist/mesh.js
CHANGED
|
@@ -6,7 +6,6 @@ export async function resolveMesh() {
|
|
|
6
6
|
id: "local",
|
|
7
7
|
baseDir,
|
|
8
8
|
registryFile: path.join(baseDir, "registry.jsonl"),
|
|
9
|
-
inboxDir: path.join(baseDir, "inbox"),
|
|
10
9
|
locksDir: path.join(baseDir, "locks"),
|
|
11
10
|
socketDirFile: path.join(baseDir, "socket-dir"),
|
|
12
11
|
};
|
|
@@ -16,7 +15,6 @@ export async function resolveMesh() {
|
|
|
16
15
|
export async function ensureMesh(mesh) {
|
|
17
16
|
await Promise.all([
|
|
18
17
|
ensureDir(mesh.baseDir),
|
|
19
|
-
ensureDir(mesh.inboxDir),
|
|
20
18
|
ensureDir(mesh.locksDir),
|
|
21
19
|
]);
|
|
22
20
|
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -17,8 +17,10 @@ export declare function listManagedSessions(mesh: MeshPaths): Promise<ManagedSes
|
|
|
17
17
|
export declare function sessionMatchesSelector(record: ManagedSessionRecord, selector: SessionSelector): boolean;
|
|
18
18
|
export declare function filterManagedSessions(records: ManagedSessionRecord[], selector: SessionSelector): ManagedSessionRecord[];
|
|
19
19
|
export declare function findManagedSessions(mesh: MeshPaths, selector: SessionSelector): Promise<ManagedSessionRecord[]>;
|
|
20
|
+
export declare function findManagedSessionById(mesh: MeshPaths, meshId: string): Promise<ManagedSessionRecord | undefined>;
|
|
20
21
|
export declare function findManagedSession(mesh: MeshPaths, spec: string): Promise<ManagedSessionRecord | undefined>;
|
|
21
22
|
export declare function upsertManagedSession(mesh: MeshPaths, record: ManagedSessionRecord): Promise<ManagedSessionRecord>;
|
|
23
|
+
export declare function deleteManagedSession(mesh: MeshPaths, meshId: string): Promise<void>;
|
|
22
24
|
export declare function markManagedSession(mesh: MeshPaths, meshId: string, patch: Partial<ManagedSessionRecord>): Promise<ManagedSessionRecord | undefined>;
|
|
23
25
|
export declare function socketPathFor(mesh: MeshPaths, meshId: string): Promise<string>;
|
|
24
26
|
export declare function lockPathFor(mesh: MeshPaths, meshId: string): string;
|
package/dist/registry.js
CHANGED
|
@@ -77,6 +77,9 @@ export function filterManagedSessions(records, selector) {
|
|
|
77
77
|
export async function findManagedSessions(mesh, selector) {
|
|
78
78
|
return filterManagedSessions(await listManagedSessions(mesh), selector);
|
|
79
79
|
}
|
|
80
|
+
export async function findManagedSessionById(mesh, meshId) {
|
|
81
|
+
return (await listManagedSessions(mesh)).find((record) => record.meshId === meshId);
|
|
82
|
+
}
|
|
80
83
|
export async function findManagedSession(mesh, spec) {
|
|
81
84
|
return (await findManagedSessions(mesh, { spec }))[0];
|
|
82
85
|
}
|
|
@@ -97,6 +100,9 @@ export async function upsertManagedSession(mesh, record) {
|
|
|
97
100
|
await appendRegistryEvent(mesh, { type: "upsert", record: next, timestamp: now });
|
|
98
101
|
return next;
|
|
99
102
|
}
|
|
103
|
+
export async function deleteManagedSession(mesh, meshId) {
|
|
104
|
+
await appendRegistryEvent(mesh, { type: "delete", meshId, timestamp: new Date().toISOString() });
|
|
105
|
+
}
|
|
100
106
|
export async function markManagedSession(mesh, meshId, patch) {
|
|
101
107
|
const current = await findManagedSession(mesh, meshId);
|
|
102
108
|
if (!current)
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streichsbaer/pi-mesh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "CLI and skill for coordinating Pi sessions through a local file/socket mesh",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "npm@11.17.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@earendil-works/pi-coding-agent": "^0.80.2"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^22.19.0",
|
|
39
39
|
"node-pty": "^1.1.0",
|
|
40
40
|
"shx": "^0.4.0",
|
|
41
41
|
"tsx": "^4.20.6",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"access": "public"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">=22.19.0"
|
|
67
67
|
},
|
|
68
68
|
"volta": {
|
|
69
|
-
"node": "
|
|
69
|
+
"node": "22.19.0",
|
|
70
70
|
"npm": "11.17.0"
|
|
71
71
|
}
|
|
72
72
|
}
|