@averyyy/pi-client 0.80.3-piclient.10

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 ADDED
@@ -0,0 +1,13 @@
1
+ ## [Unreleased]
2
+
3
+ ### Added
4
+
5
+ - Initial `pi-client` package as a lightweight wrapper that exposes only the `pi-client` bin without `pi`.
6
+ - Global install wrapper that launches the local forked coding-agent entrypoint while sharing the original `~/.pi/agent` configuration.
7
+ - `pi-client update` command for updating the fork checkout and reinstalling both `pi-client` and `pi-server`.
8
+ - npm global package updates during `pi-client update`.
9
+ - `pi-client web` command that starts the client backend in Tau mirror mode on port `1838` by default.
10
+
11
+ ### Fixed
12
+
13
+ - Used `--legacy-peer-deps` for npm-global fork updates and documented installs so existing upstream Pi installs do not trigger peer override warnings for forked prerelease aliases.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @averyyy/pi-client
2
+
3
+ Client CLI for connecting Pi to a `pi-server` instance.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g --ignore-scripts --legacy-peer-deps @averyyy/pi-client
9
+ ```
10
+
11
+ `--legacy-peer-deps` avoids npm peer override warnings when upstream Pi is already installed globally.
12
+
13
+ ## Use
14
+
15
+ Connect to the hosted server:
16
+
17
+ ```bash
18
+ PI_SERVER_URL=https://pi.yreva.asia pi-client
19
+ ```
20
+
21
+ Send one prompt and exit:
22
+
23
+ ```bash
24
+ PI_SERVER_URL=https://pi.yreva.asia pi-client -p "Say exactly: ok"
25
+ ```
26
+
27
+ Start the browser UI:
28
+
29
+ ```bash
30
+ pi install npm:tau-mirror
31
+ PI_SERVER_URL=https://pi.yreva.asia pi-client web
32
+ ```
33
+
34
+ The web command starts `pi-client` in Tau mirror mode. Tau listens on `http://127.0.0.1:1838` by default and uses the same shared `~/.pi/agent` extension install as local `pi`, so installing Tau with either `pi` or `pi-client` works.
35
+
36
+ ## Server Auth
37
+
38
+ If your server uses an auth token, set it on the client:
39
+
40
+ ```bash
41
+ PI_SERVER_AUTH_TOKEN=your-token PI_SERVER_URL=http://127.0.0.1:4217 pi-client
42
+ ```
43
+
44
+ ## Related Package
45
+
46
+ Install the server separately:
47
+
48
+ ```bash
49
+ npm i -g --ignore-scripts @averyyy/pi-server
50
+ ```
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const args = process.argv.slice(2);
7
+ const modulePromise =
8
+ args[0] === "update"
9
+ ? import("./update.js").then(async ({ runPiClientUpdate }) => {
10
+ process.exitCode = await runPiClientUpdate(args.slice(1));
11
+ })
12
+ : args[0] === "web"
13
+ ? import("./web.js").then(async ({ runPiClientWeb }) => {
14
+ process.exitCode = await runPiClientWeb(args.slice(1));
15
+ })
16
+ : runPiClientCli(args);
17
+
18
+ Promise.resolve(modulePromise).catch((e) => {
19
+ console.error(e);
20
+ process.exit(1);
21
+ });
22
+
23
+ function runPiClientCli(args) {
24
+ const entry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
25
+ const result = spawnSync(process.execPath, [join(dirname(entry), "cli.js"), ...args], {
26
+ env: {
27
+ ...process.env,
28
+ PI_CODING_AGENT: "true",
29
+ PI_SERVER_MODE: "true",
30
+ },
31
+ stdio: "inherit",
32
+ });
33
+ if (result.error) throw result.error;
34
+ process.exitCode = result.status ?? (result.signal === "SIGINT" ? 130 : 1);
35
+ }
package/bin/update.js ADDED
@@ -0,0 +1,88 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ function defaultPackageRoot() {
7
+ return dirname(dirname(fileURLToPath(import.meta.url)));
8
+ }
9
+
10
+ function defaultRepoRoot() {
11
+ return resolve(defaultPackageRoot(), "..", "..");
12
+ }
13
+
14
+ function readPackageMetadata(packageRoot) {
15
+ return JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf-8"));
16
+ }
17
+
18
+ function runStep(runner, command, args, cwd, stdio = "inherit") {
19
+ return runner(command, args, { cwd, stdio, encoding: "utf-8" });
20
+ }
21
+
22
+ function isMissingGitCheckout(result) {
23
+ const text = `${String(result.stdout ?? "")}\n${String(result.stderr ?? "")}`;
24
+ return result.status !== 0 && text.includes("not a git repository");
25
+ }
26
+
27
+ async function runNpmGlobalUpdate(runner, repoRoot, stdout, stderr) {
28
+ stdout.write("Updating npm packages: @averyyy/pi-client@latest @averyyy/pi-server@latest\n");
29
+ const result = runStep(
30
+ runner,
31
+ "npm",
32
+ ["install", "-g", "--ignore-scripts", "--legacy-peer-deps", "@averyyy/pi-client@latest", "@averyyy/pi-server@latest"],
33
+ repoRoot,
34
+ );
35
+ if (result.status !== 0) {
36
+ stderr.write(
37
+ "pi-client update failed: npm install -g --ignore-scripts --legacy-peer-deps @averyyy/pi-client@latest @averyyy/pi-server@latest\n",
38
+ );
39
+ return result.status ?? 1;
40
+ }
41
+ stdout.write("pi-client update complete\n");
42
+ return 0;
43
+ }
44
+
45
+ export async function runPiClientUpdate(_args = [], options = {}) {
46
+ const packageRoot = options.packageRoot ?? defaultPackageRoot();
47
+ const repoRoot = options.repoRoot ?? defaultRepoRoot();
48
+ const runner = options.runner ?? spawnSync;
49
+ const stdout = options.stdout ?? process.stdout;
50
+ const stderr = options.stderr ?? process.stderr;
51
+ const pkg = readPackageMetadata(packageRoot);
52
+ const baseVersion = pkg.piClient?.basePiVersion ?? "unknown";
53
+ const baseCommit = pkg.piClient?.basePiCommit ?? "unknown";
54
+
55
+ stdout.write(`pi-client ${pkg.version} (based on pi ${baseVersion}, upstream ${baseCommit})\n`);
56
+ stdout.write(`Updating checkout: ${repoRoot}\n`);
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;
88
+ }
package/bin/web.js ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const defaultPort = "1838";
9
+ const defaultPiServerUrl = "http://127.0.0.1:4217";
10
+ const tauCodexPackage = "@averyyy/pi-tau-codex";
11
+ const tauCodexInstallTarget = "npm:@averyyy/pi-tau-codex";
12
+ const tauCodexGitTarget = "git:github.com/Averyyy/pi-tau-codex";
13
+
14
+ export async function runPiClientWeb(args = process.argv.slice(2)) {
15
+ const parsed = parseArgs(args);
16
+ if (parsed.help) {
17
+ printHelp();
18
+ return 0;
19
+ }
20
+
21
+ if (!hasTauCodexExtensionInstalled(process.env)) {
22
+ printInstallRequired();
23
+ return 1;
24
+ }
25
+
26
+ process.title = "pi-client web";
27
+ const entry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
28
+ const port = parsed.port;
29
+ const host = process.env.TAU_HOST ?? "127.0.0.1";
30
+ const child = spawn(process.execPath, [join(dirname(entry), "cli.js"), ...parsed.clientArgs], {
31
+ env: piClientWebEnv(process.env, { port, host }),
32
+ stdio: "inherit",
33
+ });
34
+
35
+ console.log(`pi-client web uses Tau at http://${host}:${port}`);
36
+
37
+ return await new Promise((resolve, reject) => {
38
+ child.once("error", reject);
39
+ child.once("exit", (code, signal) => {
40
+ resolve(code ?? (signal === "SIGINT" ? 130 : 1));
41
+ });
42
+ });
43
+ }
44
+
45
+ export function piClientWebEnv(env = process.env, options) {
46
+ return {
47
+ ...env,
48
+ PI_CODING_AGENT: "true",
49
+ PI_SERVER_MODE: "true",
50
+ PI_SERVER_URL: env.PI_SERVER_URL ?? defaultPiServerUrl,
51
+ TAU_HOST: options.host,
52
+ TAU_MIRROR_PORT: options.port,
53
+ };
54
+ }
55
+
56
+ export function hasTauCodexExtensionInstalled(env = process.env) {
57
+ if (env.TAU_STATIC_DIR) return true;
58
+ const settingsPath = env.PI_CODING_AGENT_SETTINGS_PATH ?? join(
59
+ env.PI_CODING_AGENT_DIR ?? join(env.HOME ?? homedir(), ".pi", "agent"),
60
+ "settings.json",
61
+ );
62
+ if (!existsSync(settingsPath)) return false;
63
+ const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
64
+ const packages = Array.isArray(settings.packages) ? settings.packages : [];
65
+ return packages.some(packageSpecMatchesTauCodex);
66
+ }
67
+
68
+ function packageSpecMatchesTauCodex(spec) {
69
+ if (typeof spec === "string") return specMatchesTauCodex(spec);
70
+ if (!spec || typeof spec !== "object") return false;
71
+ return ["source", "package", "name", "spec"].some((key) => specMatchesTauCodex(spec[key]));
72
+ }
73
+
74
+ function specMatchesTauCodex(spec) {
75
+ return typeof spec === "string" && (
76
+ spec === tauCodexPackage ||
77
+ spec === tauCodexInstallTarget ||
78
+ spec.startsWith(`${tauCodexPackage}@`) ||
79
+ spec.startsWith(`${tauCodexInstallTarget}@`) ||
80
+ spec === tauCodexGitTarget ||
81
+ spec.includes("github.com/Averyyy/pi-tau-codex")
82
+ );
83
+ }
84
+
85
+ function parseArgs(args) {
86
+ let port = process.env.TAU_MIRROR_PORT ?? defaultPort;
87
+ const clientArgs = [];
88
+
89
+ for (let i = 0; i < args.length; i += 1) {
90
+ const arg = args[i];
91
+ if (arg === "--help" || arg === "-h") return { help: true, port, clientArgs };
92
+ if (arg === "--") {
93
+ clientArgs.push(...args.slice(i + 1));
94
+ break;
95
+ }
96
+ if (arg === "--port" || arg === "-p") {
97
+ port = requireValue(args, (i += 1), arg);
98
+ continue;
99
+ }
100
+ if (arg.startsWith("--port=")) {
101
+ port = arg.slice("--port=".length);
102
+ continue;
103
+ }
104
+ throw new Error(`Unknown pi-client web argument: ${arg}`);
105
+ }
106
+
107
+ validatePort(port);
108
+ return { help: false, port, clientArgs };
109
+ }
110
+
111
+ function requireValue(args, index, name) {
112
+ const value = args[index];
113
+ if (value === undefined || value.startsWith("-")) throw new Error(`${name} requires a value`);
114
+ return value;
115
+ }
116
+
117
+ function validatePort(port) {
118
+ const parsed = Number(port);
119
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
120
+ throw new Error(`--port must be an integer from 1 to 65535: ${port}`);
121
+ }
122
+ }
123
+
124
+ function printHelp() {
125
+ console.log(`Usage: pi-client web [--port <port>] [-- <pi-client args...>]
126
+
127
+ Start pi-client in Tau mirror mode.
128
+
129
+ Options:
130
+ -p, --port <port> Tau port to listen on (default: ${defaultPort})
131
+ -h, --help Show this help
132
+
133
+ Environment:
134
+ PI_SERVER_URL pi-server URL used by pi-client sessions
135
+ PI_SERVER_AUTH_TOKEN pi-server auth token
136
+ TAU_HOST Tau bind host (default: 127.0.0.1)
137
+ TAU_MIRROR_PORT Tau port (default: ${defaultPort})
138
+
139
+ Tau must be installed in the shared Pi agent settings:
140
+ pi-client install npm:@averyyy/pi-tau-codex
141
+ # or: pi install npm:@averyyy/pi-tau-codex
142
+ # dev fallback: pi-client install git:github.com/Averyyy/pi-tau-codex
143
+ `);
144
+ }
145
+
146
+ function printInstallRequired() {
147
+ console.error(`请安装 ${tauCodexPackage}:`);
148
+ console.error(" pi-client install npm:@averyyy/pi-tau-codex");
149
+ console.error(" # or: pi install npm:@averyyy/pi-tau-codex");
150
+ console.error(" # dev fallback: pi-client install git:github.com/Averyyy/pi-tau-codex");
151
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@averyyy/pi-client",
3
+ "version": "0.80.3-piclient.10",
4
+ "description": "Lightweight CLI wrapper that connects to a pi-server instance",
5
+ "type": "module",
6
+ "piClient": {
7
+ "basePiVersion": "0.80.3",
8
+ "basePiCommit": "85b7c247"
9
+ },
10
+ "bin": {
11
+ "pi-client": "bin/pi-client.js"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "bin",
18
+ "README.md",
19
+ "CHANGELOG.md"
20
+ ],
21
+ "scripts": {
22
+ "clean": "shx echo ok",
23
+ "build": "shx echo ok",
24
+ "test": "vitest --run",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.80.3-piclient.10"
29
+ },
30
+ "devDependencies": {
31
+ "shx": "0.4.0",
32
+ "vitest": "4.1.9"
33
+ },
34
+ "keywords": [
35
+ "pi-client",
36
+ "llm",
37
+ "agent"
38
+ ],
39
+ "author": "Pi Contributors",
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/Averyyy/pi-client",
44
+ "directory": "packages/pi-client"
45
+ },
46
+ "engines": {
47
+ "node": ">=22.19.0"
48
+ }
49
+ }