@averyyy/pi-client 0.80.6-piclient.4 → 0.80.6-piclient.5
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/CHANGELOG.md +1 -0
- package/README.md +4 -0
- package/bin/pi-client.js +63 -10
- package/bin/update.js +26 -46
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -15,3 +15,4 @@
|
|
|
15
15
|
### Changed
|
|
16
16
|
|
|
17
17
|
- Rebased the client on upstream Pi `0.80.6`, including GPT-5.6 model metadata and `max` thinking support.
|
|
18
|
+
- Changed `pi-client update` to update global packages without reinstalling the active checkout or stopping sessions; updated sessions require `/reload` to restart on the new runtime with their existing history.
|
package/README.md
CHANGED
|
@@ -41,6 +41,10 @@ If your server uses an auth token, set it on the client:
|
|
|
41
41
|
PI_SERVER_AUTH_TOKEN=your-token PI_SERVER_URL=http://127.0.0.1:4217 pi-client
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Update
|
|
45
|
+
|
|
46
|
+
`pi-client update` installs the latest client and server packages without stopping active client sessions. Existing sessions block new prompts until you run `/reload`; `/reload` restarts that session on the new runtime and resumes its persisted history.
|
|
47
|
+
|
|
44
48
|
## Related Package
|
|
45
49
|
|
|
46
50
|
Install the server separately:
|
package/bin/pi-client.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
3
5
|
import { dirname, join } from "node:path";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
|
|
@@ -21,15 +23,66 @@ Promise.resolve(modulePromise).catch((e) => {
|
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
function runPiClientCli(args) {
|
|
26
|
+
const reloadDir = mkdtempSync(join(tmpdir(), "pi-client-reload-"));
|
|
27
|
+
const reloadStatePath = join(reloadDir, "state.json");
|
|
28
|
+
let cliPath = getLocalCliPath();
|
|
29
|
+
let childArgs = args;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
for (;;) {
|
|
33
|
+
const result = spawnSync(process.execPath, [cliPath, ...childArgs], {
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
PI_CODING_AGENT: "true",
|
|
37
|
+
PI_SERVER_MODE: "true",
|
|
38
|
+
PI_CLIENT_RELOAD_STATE_PATH: reloadStatePath,
|
|
39
|
+
},
|
|
40
|
+
stdio: "inherit",
|
|
41
|
+
});
|
|
42
|
+
if (result.error) throw result.error;
|
|
43
|
+
if (result.status !== 75) {
|
|
44
|
+
process.exitCode = result.status ?? (result.signal === "SIGINT" ? 130 : 1);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const state = readReloadState(reloadStatePath);
|
|
49
|
+
if (!state) {
|
|
50
|
+
console.error("pi-client reload failed: missing session state from the previous runtime.");
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
rmSync(state.updateMarkerPath, { force: true });
|
|
55
|
+
childArgs = state.sessionDir ? ["--session-dir", state.sessionDir, "--session", state.sessionId] : ["--session", state.sessionId];
|
|
56
|
+
cliPath = getUpdatedGlobalCliPath() ?? getLocalCliPath();
|
|
57
|
+
}
|
|
58
|
+
} finally {
|
|
59
|
+
rmSync(reloadDir, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getLocalCliPath() {
|
|
24
64
|
const entry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (result.
|
|
34
|
-
|
|
65
|
+
return join(dirname(entry), "cli.js");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getUpdatedGlobalCliPath() {
|
|
69
|
+
const npmCli = join(dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js");
|
|
70
|
+
const result = existsSync(npmCli)
|
|
71
|
+
? spawnSync(process.execPath, [npmCli, "root", "-g"], { encoding: "utf-8" })
|
|
72
|
+
: spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
73
|
+
if (result.status !== 0) return undefined;
|
|
74
|
+
const root = result.stdout.trim();
|
|
75
|
+
const cliPath = join(root, "@averyyy", "pi-client", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "cli.js");
|
|
76
|
+
return existsSync(cliPath) ? cliPath : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function readReloadState(path) {
|
|
80
|
+
try {
|
|
81
|
+
const value = JSON.parse(readFileSync(path, "utf-8"));
|
|
82
|
+
if (typeof value.sessionId !== "string" || typeof value.updateMarkerPath !== "string") return undefined;
|
|
83
|
+
if (value.sessionDir !== undefined && typeof value.sessionDir !== "string") return undefined;
|
|
84
|
+
return value;
|
|
85
|
+
} catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
35
88
|
}
|
package/bin/update.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import {
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
6
7
|
function defaultPackageRoot() {
|
|
7
8
|
return dirname(dirname(fileURLToPath(import.meta.url)));
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
function defaultRepoRoot() {
|
|
11
|
-
return resolve(defaultPackageRoot(), "..", "..");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
11
|
function readPackageMetadata(packageRoot) {
|
|
15
12
|
return JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf-8"));
|
|
16
13
|
}
|
|
@@ -19,70 +16,53 @@ function runStep(runner, command, args, cwd, stdio = "inherit") {
|
|
|
19
16
|
return runner(command, args, { cwd, stdio, encoding: "utf-8" });
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
function defaultUpdateMarkerPath() {
|
|
20
|
+
return join(process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent"), "pi-client-update.json");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeUpdateMarker(path) {
|
|
24
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
25
|
+
writeFileSync(path, `${JSON.stringify({ version: "latest", updatedAt: new Date().toISOString() })}\n`);
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
function runNpmGlobalUpdate(runner, cwd, stdout, stderr, updateMarkerPath) {
|
|
28
29
|
stdout.write("Updating npm packages: @averyyy/pi-client@latest @averyyy/pi-server@latest\n");
|
|
29
30
|
const result = runStep(
|
|
30
31
|
runner,
|
|
31
32
|
"npm",
|
|
32
|
-
[
|
|
33
|
-
|
|
33
|
+
[
|
|
34
|
+
"install",
|
|
35
|
+
"-g",
|
|
36
|
+
"--ignore-scripts",
|
|
37
|
+
"--legacy-peer-deps",
|
|
38
|
+
"--force",
|
|
39
|
+
"@averyyy/pi-client@latest",
|
|
40
|
+
"@averyyy/pi-server@latest",
|
|
41
|
+
],
|
|
42
|
+
cwd,
|
|
34
43
|
);
|
|
35
44
|
if (result.status !== 0) {
|
|
36
45
|
stderr.write(
|
|
37
|
-
"pi-client update failed: npm install -g --ignore-scripts --legacy-peer-deps @averyyy/pi-client@latest @averyyy/pi-server@latest\n",
|
|
46
|
+
"pi-client update failed: npm install -g --ignore-scripts --legacy-peer-deps --force @averyyy/pi-client@latest @averyyy/pi-server@latest\n",
|
|
38
47
|
);
|
|
39
48
|
return result.status ?? 1;
|
|
40
49
|
}
|
|
50
|
+
writeUpdateMarker(updateMarkerPath);
|
|
51
|
+
stdout.write("Updated package files without stopping active pi-client sessions. Run /reload in each session to switch runtimes.\n");
|
|
41
52
|
stdout.write("pi-client update complete\n");
|
|
42
53
|
return 0;
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
export async function runPiClientUpdate(_args = [], options = {}) {
|
|
46
57
|
const packageRoot = options.packageRoot ?? defaultPackageRoot();
|
|
47
|
-
const repoRoot = options.repoRoot ?? defaultRepoRoot();
|
|
48
58
|
const runner = options.runner ?? spawnSync;
|
|
49
59
|
const stdout = options.stdout ?? process.stdout;
|
|
50
60
|
const stderr = options.stderr ?? process.stderr;
|
|
61
|
+
const updateMarkerPath = options.updateMarkerPath ?? defaultUpdateMarkerPath();
|
|
51
62
|
const pkg = readPackageMetadata(packageRoot);
|
|
52
63
|
const baseVersion = pkg.piClient?.basePiVersion ?? "unknown";
|
|
53
64
|
const baseCommit = pkg.piClient?.basePiCommit ?? "unknown";
|
|
54
65
|
|
|
55
66
|
stdout.write(`pi-client ${pkg.version} (based on pi ${baseVersion}, upstream ${baseCommit})\n`);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const status = runStep(runner, "git", ["status", "--porcelain"], repoRoot, "pipe");
|
|
59
|
-
if (status.status !== 0) {
|
|
60
|
-
if (isMissingGitCheckout(status)) {
|
|
61
|
-
return runNpmGlobalUpdate(runner, repoRoot, stdout, stderr);
|
|
62
|
-
}
|
|
63
|
-
stderr.write("pi-client update failed: unable to inspect git status\n");
|
|
64
|
-
return status.status ?? 1;
|
|
65
|
-
}
|
|
66
|
-
if (String(status.stdout ?? "").trim().length > 0) {
|
|
67
|
-
stderr.write("pi-client update failed: working tree has uncommitted changes\n");
|
|
68
|
-
return 1;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const steps = [
|
|
72
|
-
["git", ["pull", "--ff-only"]],
|
|
73
|
-
["npm", ["install", "--ignore-scripts"]],
|
|
74
|
-
["npm", ["run", "install:pi-client"]],
|
|
75
|
-
["npm", ["run", "install:pi-server"]],
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
for (const [command, args] of steps) {
|
|
79
|
-
const result = runStep(runner, command, args, repoRoot);
|
|
80
|
-
if (result.status !== 0) {
|
|
81
|
-
stderr.write(`pi-client update failed: ${command} ${args.join(" ")}\n`);
|
|
82
|
-
return result.status ?? 1;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
stdout.write("pi-client update complete\n");
|
|
87
|
-
return 0;
|
|
67
|
+
return runNpmGlobalUpdate(runner, process.cwd(), stdout, stderr, updateMarkerPath);
|
|
88
68
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@averyyy/pi-client",
|
|
3
|
-
"version": "0.80.6-piclient.
|
|
3
|
+
"version": "0.80.6-piclient.5",
|
|
4
4
|
"description": "Lightweight CLI wrapper that connects to a pi-server instance",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piClient": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.80.6-piclient.
|
|
28
|
+
"@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.80.6-piclient.5"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"shx": "0.4.0",
|