pi-app 0.7.6 → 0.8.0
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +3 -3
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.js +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/share/[token]/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +3 -3
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/app/{page-275366a69e4447f4.js → page-efc48687d0c38175.js} +2 -2
- package/.next/trace +84 -84
- package/.next/trace-build +1 -1
- package/README.md +15 -0
- package/bin/pi.js +29 -0
- package/package.json +6 -1
- package/scripts/cli-link-common.mjs +110 -0
- package/scripts/install-cli.mjs +108 -0
- package/scripts/uninstall-cli.mjs +35 -0
- /package/.next/static/{ScSrh8Z0sCMWAflRLnYlw → ti8s0DtntFtmi4CivW4V2}/_buildManifest.js +0 -0
- /package/.next/static/{ScSrh8Z0sCMWAflRLnYlw → ti8s0DtntFtmi4CivW4V2}/_ssgManifest.js +0 -0
package/.next/trace-build
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[{"name":"run-webpack","duration":
|
|
1
|
+
[{"name":"run-webpack","duration":28222545,"timestamp":156175997,"id":14,"parentId":1,"tags":{},"startTime":1781182004326,"traceId":"9491902b6c19c8a7"},{"name":"run-typescript","duration":9148112,"timestamp":184402807,"id":8604,"parentId":1,"tags":{},"startTime":1781182032553,"traceId":"9491902b6c19c8a7"},{"name":"static-check","duration":1995937,"timestamp":193610107,"id":8607,"parentId":1,"tags":{},"startTime":1781182041760,"traceId":"9491902b6c19c8a7"},{"name":"static-generation","duration":5157335,"timestamp":196103481,"id":8723,"parentId":1,"tags":{},"startTime":1781182044254,"traceId":"9491902b6c19c8a7"},{"name":"collect-build-traces","duration":43466334,"timestamp":195613027,"id":8720,"parentId":1,"tags":{},"startTime":1781182043763,"traceId":"9491902b6c19c8a7"},{"name":"telemetry-flush","duration":50,"timestamp":239082431,"id":8732,"parentId":1,"tags":{},"startTime":1781182087233,"traceId":"9491902b6c19c8a7"},{"name":"next-build","duration":83045692,"timestamp":156036801,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"webpack","has-custom-webpack-config":"true","use-build-worker":"false"},"startTime":1781182004187,"traceId":"9491902b6c19c8a7"}]
|
package/README.md
CHANGED
|
@@ -19,6 +19,21 @@ pi-app
|
|
|
19
19
|
|
|
20
20
|
启动后打开 [http://localhost:30141](http://localhost:30141)。
|
|
21
21
|
|
|
22
|
+
**一并提供 `pi` 命令行:**
|
|
23
|
+
|
|
24
|
+
全局安装 pi-app 时会顺带提供 `pi` 命令(即 pi 编程智能体 CLI,与 pi-app 内置版本一致),无需单独安装:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g pi-app
|
|
28
|
+
pi --help # 直接使用 pi CLI
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
行为说明:
|
|
32
|
+
|
|
33
|
+
- 安装后仅当系统中**尚未存在** `pi` 命令时,才会创建 `pi`(一个转发到 pi-app 内置 CLI 的独立 shim)。
|
|
34
|
+
- 若你已单独安装过 `pi`(如通过 `npm i -g @earendil-works/pi-coding-agent` 或 Homebrew),则**保留你已有的版本,不会覆盖**。
|
|
35
|
+
- 卸载说明:npm **不会**执行卸载钩子(`postuninstall`),因此 `npm uninstall -g pi-app` 后这个 `pi` shim 会残留。它是独立 shim 而非失效软链——此时运行 `pi` 不会报「文件不存在」,而是友好提示你重新安装 pi-app 或删除该文件(提示里会给出确切路径)。如需立即清理,按提示删除该 `pi` 文件即可(不会影响你自行安装的其他 `pi`)。
|
|
36
|
+
|
|
22
37
|
**可选参数:**
|
|
23
38
|
|
|
24
39
|
```bash
|
package/bin/pi.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
// Thin launcher that forwards to the pi coding-agent CLI bundled with pi-app.
|
|
5
|
+
// Installed (symlinked / shimmed) as the global `pi` command by
|
|
6
|
+
// scripts/install-cli.mjs, but ONLY when no `pi` already exists on the system.
|
|
7
|
+
//
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
let cliPath;
|
|
12
|
+
try {
|
|
13
|
+
cliPath = require.resolve("@earendil-works/pi-coding-agent/dist/cli.js");
|
|
14
|
+
} catch {
|
|
15
|
+
// Fallback to the package-local node_modules layout if resolution fails.
|
|
16
|
+
cliPath = path.join(
|
|
17
|
+
__dirname,
|
|
18
|
+
"..",
|
|
19
|
+
"node_modules",
|
|
20
|
+
"@earendil-works",
|
|
21
|
+
"pi-coding-agent",
|
|
22
|
+
"dist",
|
|
23
|
+
"cli.js",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// cli.js reads process.argv.slice(2) itself; requiring it runs the CLI.
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
29
|
+
require(cliPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Web UI for the pi coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/asiachrispy/pi-app",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
19
|
+
"scripts/install-cli.mjs",
|
|
20
|
+
"scripts/uninstall-cli.mjs",
|
|
21
|
+
"scripts/cli-link-common.mjs",
|
|
19
22
|
".next",
|
|
20
23
|
"!.next/cache",
|
|
21
24
|
"!.next/dev",
|
|
@@ -36,6 +39,8 @@
|
|
|
36
39
|
"test:m2": "node scripts/m2-preflight.mjs",
|
|
37
40
|
"test:m3": "node scripts/m3-preflight.mjs",
|
|
38
41
|
"test:m4": "node scripts/m4-preflight.mjs",
|
|
42
|
+
"postinstall": "node scripts/install-cli.mjs",
|
|
43
|
+
"postuninstall": "node scripts/uninstall-cli.mjs",
|
|
39
44
|
"package:macos": "bash scripts/package-macos-app.sh",
|
|
40
45
|
"tunnel:cloudflare": "bash scripts/tunnel-cloudflared.sh",
|
|
41
46
|
"relay:server": "node --experimental-strip-types scripts/pi-relay-server.ts",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Shared helpers for installing/uninstalling the global `pi` command that
|
|
2
|
+
// forwards to the pi coding-agent CLI bundled with pi-app.
|
|
3
|
+
//
|
|
4
|
+
// Design notes:
|
|
5
|
+
// - We deliberately do NOT declare `bin.pi` in package.json. npm refuses to
|
|
6
|
+
// overwrite a global bin owned by another package (EEXIST) and would make
|
|
7
|
+
// `npm i -g pi-app` fail outright when the user already has `pi` installed.
|
|
8
|
+
// - Instead postinstall conditionally creates the link only when no `pi`
|
|
9
|
+
// exists, and removes it only if we are the ones who made it.
|
|
10
|
+
// - The global `pi` is a STANDALONE shim (not a symlink into the package).
|
|
11
|
+
// npm does not run uninstall lifecycle scripts (postuninstall), so after
|
|
12
|
+
// `npm uninstall -g pi-app` the shim would otherwise dangle. A standalone
|
|
13
|
+
// shim degrades gracefully: running `pi` then prints how to reinstall/remove
|
|
14
|
+
// instead of failing with a confusing "no such file" error.
|
|
15
|
+
|
|
16
|
+
import { execFileSync } from "node:child_process";
|
|
17
|
+
import { chmodSync, lstatSync, readFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join, resolve } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
export const isWindows = process.platform === "win32";
|
|
22
|
+
|
|
23
|
+
/** Marker embedded in every shim we generate, used to detect ownership. */
|
|
24
|
+
export const SHIM_MARKER = "pi-app-managed-cli";
|
|
25
|
+
|
|
26
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
|
|
28
|
+
/** pi-app package root (the dir containing package.json). */
|
|
29
|
+
export function getPkgRoot() {
|
|
30
|
+
return resolve(here, "..");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Absolute path to the launcher the shim forwards to. */
|
|
34
|
+
export function getLauncherPath() {
|
|
35
|
+
return join(getPkgRoot(), "bin", "pi.js");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** True when npm is running this lifecycle script for a global install. */
|
|
39
|
+
export function isGlobalInstall() {
|
|
40
|
+
return process.env.npm_config_global === "true";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the directory npm links global bins into.
|
|
45
|
+
* Unix: <prefix>/bin. Windows: <prefix> itself.
|
|
46
|
+
*/
|
|
47
|
+
export function getGlobalBinDir() {
|
|
48
|
+
const prefix = process.env.npm_config_prefix;
|
|
49
|
+
if (prefix) return isWindows ? prefix : join(prefix, "bin");
|
|
50
|
+
|
|
51
|
+
// Fallback: derive from the install location of this package.
|
|
52
|
+
// Unix: <prefix>/lib/node_modules/pi-app
|
|
53
|
+
// Windows: <prefix>/node_modules/pi-app
|
|
54
|
+
const pkgRoot = getPkgRoot();
|
|
55
|
+
return isWindows
|
|
56
|
+
? resolve(pkgRoot, "..", "..")
|
|
57
|
+
: resolve(pkgRoot, "..", "..", "..", "bin");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Path of the global command file we manage. */
|
|
61
|
+
export function getLinkPath(binDir) {
|
|
62
|
+
return join(binDir, isWindows ? "pi.cmd" : "pi");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Whether an existing global `pi` is a shim created by us (safe to manage). */
|
|
66
|
+
export function shimIsOurs(linkPath) {
|
|
67
|
+
try {
|
|
68
|
+
return readFileSync(linkPath, "utf8").includes(SHIM_MARKER);
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** True if some `pi` command is already resolvable on PATH. */
|
|
75
|
+
export function piExistsOnPath() {
|
|
76
|
+
const finder = isWindows ? "where" : "which";
|
|
77
|
+
try {
|
|
78
|
+
execFileSync(finder, ["pi"], { stdio: "ignore" });
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Best-effort detection of "something already occupies this path". */
|
|
86
|
+
export function pathOccupied(linkPath) {
|
|
87
|
+
try {
|
|
88
|
+
lstatSync(linkPath); // lstat so broken symlinks count as occupied too
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function makeExecutable(file) {
|
|
96
|
+
if (isWindows) return;
|
|
97
|
+
try {
|
|
98
|
+
chmodSync(file, 0o755);
|
|
99
|
+
} catch {
|
|
100
|
+
// best effort
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const TAG = "[pi-app] ";
|
|
105
|
+
export function log(msg) {
|
|
106
|
+
process.stdout.write(`${TAG}${msg}\n`);
|
|
107
|
+
}
|
|
108
|
+
export function warn(msg) {
|
|
109
|
+
process.stderr.write(`${TAG}${msg}\n`);
|
|
110
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// postinstall: make `pi` available globally when (and only when) the user
|
|
2
|
+
// doesn't already have a `pi` command. Never fails the install.
|
|
3
|
+
|
|
4
|
+
import { mkdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
getGlobalBinDir,
|
|
8
|
+
getLauncherPath,
|
|
9
|
+
getLinkPath,
|
|
10
|
+
isGlobalInstall,
|
|
11
|
+
isWindows,
|
|
12
|
+
log,
|
|
13
|
+
makeExecutable,
|
|
14
|
+
pathOccupied,
|
|
15
|
+
piExistsOnPath,
|
|
16
|
+
SHIM_MARKER,
|
|
17
|
+
shimIsOurs,
|
|
18
|
+
warn,
|
|
19
|
+
} from "./cli-link-common.mjs";
|
|
20
|
+
|
|
21
|
+
// A standalone shim (not a symlink into the package). It forwards to the
|
|
22
|
+
// bundled launcher, and—because npm doesn't run postuninstall—degrades
|
|
23
|
+
// gracefully if pi-app is later removed.
|
|
24
|
+
function shimContents(launcher) {
|
|
25
|
+
if (isWindows) {
|
|
26
|
+
const q = `"${launcher}"`;
|
|
27
|
+
return [
|
|
28
|
+
"@ECHO off",
|
|
29
|
+
`REM ${SHIM_MARKER} (do not edit) - forwards to the pi CLI bundled with pi-app`,
|
|
30
|
+
`IF EXIST ${q} (`,
|
|
31
|
+
` node ${q} %*`,
|
|
32
|
+
") ELSE (",
|
|
33
|
+
" echo pi: this command is provided by pi-app, which is no longer installed. Reinstall: npm i -g pi-app 1>&2",
|
|
34
|
+
" EXIT /B 127",
|
|
35
|
+
")",
|
|
36
|
+
"",
|
|
37
|
+
].join("\r\n");
|
|
38
|
+
}
|
|
39
|
+
return [
|
|
40
|
+
"#!/usr/bin/env node",
|
|
41
|
+
`// ${SHIM_MARKER} (do not edit) - forwards to the pi CLI bundled with pi-app`,
|
|
42
|
+
'"use strict";',
|
|
43
|
+
'const fs = require("fs");',
|
|
44
|
+
`const launcher = ${JSON.stringify(launcher)};`,
|
|
45
|
+
"if (!fs.existsSync(launcher)) {",
|
|
46
|
+
' process.stderr.write("pi: this command is provided by pi-app, which is no longer installed.\\n");',
|
|
47
|
+
' process.stderr.write("Reinstall with: npm i -g pi-app (or delete this file: " + process.argv[1] + ")\\n");',
|
|
48
|
+
" process.exit(127);",
|
|
49
|
+
"}",
|
|
50
|
+
"require(launcher);",
|
|
51
|
+
"",
|
|
52
|
+
].join("\n");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createShim(linkPath, launcher) {
|
|
56
|
+
// npm runs postinstall BEFORE it creates the global bin dir and links bins,
|
|
57
|
+
// so on a fresh prefix this dir may not exist yet.
|
|
58
|
+
mkdirSync(dirname(linkPath), { recursive: true });
|
|
59
|
+
writeFileSync(linkPath, shimContents(launcher), "utf8");
|
|
60
|
+
makeExecutable(linkPath);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function main() {
|
|
64
|
+
if (!isGlobalInstall()) return; // only relevant for `npm i -g pi-app`
|
|
65
|
+
|
|
66
|
+
const launcher = getLauncherPath();
|
|
67
|
+
if (!pathOccupied(launcher)) {
|
|
68
|
+
warn(`launcher not found at ${launcher}; skipping pi CLI setup.`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const binDir = getGlobalBinDir();
|
|
73
|
+
const linkPath = getLinkPath(binDir);
|
|
74
|
+
|
|
75
|
+
// Already our shim from a previous install — refresh it (e.g. path changes).
|
|
76
|
+
if (shimIsOurs(linkPath)) {
|
|
77
|
+
try {
|
|
78
|
+
unlinkSync(linkPath);
|
|
79
|
+
} catch {
|
|
80
|
+
// ignore; createShim overwrites anyway
|
|
81
|
+
}
|
|
82
|
+
createShim(linkPath, launcher);
|
|
83
|
+
log(`pi CLI refreshed -> ${linkPath}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Something else owns this path — keep it, don't clobber.
|
|
88
|
+
if (pathOccupied(linkPath)) {
|
|
89
|
+
log(`existing pi found at ${linkPath}; keeping it (not overwriting).`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A pi exists elsewhere on PATH (e.g. Homebrew, a separate global install).
|
|
94
|
+
if (piExistsOnPath()) {
|
|
95
|
+
log("an existing pi command was found on PATH; keeping it.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
createShim(linkPath, launcher);
|
|
100
|
+
log(`pi CLI installed -> ${linkPath}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
main();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// postinstall must never break `npm i -g pi-app`.
|
|
107
|
+
warn(`could not set up the pi CLI automatically: ${err?.message ?? err}`);
|
|
108
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// postuninstall: remove the global `pi` shim ONLY if we created it.
|
|
2
|
+
//
|
|
3
|
+
// NOTE: npm does NOT run uninstall lifecycle scripts, so this will typically
|
|
4
|
+
// NOT execute on `npm uninstall -g pi-app`. It is kept as best-effort for
|
|
5
|
+
// package managers that do honor it (and manual invocation). When it doesn't
|
|
6
|
+
// run, the standalone shim degrades gracefully instead of dangling. Never
|
|
7
|
+
// fails the uninstall.
|
|
8
|
+
|
|
9
|
+
import { unlinkSync } from "node:fs";
|
|
10
|
+
import {
|
|
11
|
+
getGlobalBinDir,
|
|
12
|
+
getLinkPath,
|
|
13
|
+
isGlobalInstall,
|
|
14
|
+
log,
|
|
15
|
+
shimIsOurs,
|
|
16
|
+
warn,
|
|
17
|
+
} from "./cli-link-common.mjs";
|
|
18
|
+
|
|
19
|
+
function main() {
|
|
20
|
+
if (!isGlobalInstall()) return;
|
|
21
|
+
|
|
22
|
+
const binDir = getGlobalBinDir();
|
|
23
|
+
const linkPath = getLinkPath(binDir);
|
|
24
|
+
|
|
25
|
+
if (!shimIsOurs(linkPath)) return; // not ours — leave it alone
|
|
26
|
+
|
|
27
|
+
unlinkSync(linkPath);
|
|
28
|
+
log(`removed pi CLI shim at ${linkPath}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
main();
|
|
33
|
+
} catch (err) {
|
|
34
|
+
warn(`could not clean up the pi CLI shim: ${err?.message ?? err}`);
|
|
35
|
+
}
|
|
File without changes
|
|
File without changes
|