@vzlentin/harness 0.2.0 → 0.2.2
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 +0 -5
- package/dist/setup.d.ts +4 -5
- package/dist/setup.js +47 -35
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -24,11 +24,6 @@ pi install "$(realpath "$(npm root -g)/@vzlentin/harness/pi")"
|
|
|
24
24
|
|
|
25
25
|
When you install with `--prefix`, give the same `--prefix` to `npm root -g`.
|
|
26
26
|
|
|
27
|
-
### Move from 0.1.0
|
|
28
|
-
|
|
29
|
-
0.1.0 had the extension in the separate Pi package `@vzlentin/pi-subagent`. Update as below: the setup removes that
|
|
30
|
-
package from Pi (`pi remove npm:@vzlentin/pi-subagent`), since it conflicts with the new extension.
|
|
31
|
-
|
|
32
27
|
### Update and remove
|
|
33
28
|
|
|
34
29
|
```sh
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registers `<packageDir>/pi` in Pi when `packageDir` is a top-level global npm install.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* on PATH it writes the stamp, so a user without Pi gets the hint once per version.
|
|
2
|
+
* Registers `<packageDir>/pi` in Pi when `packageDir` is a top-level global npm install. It writes its messages to
|
|
3
|
+
* stderr, and it does not throw for a Pi failure: it writes a warning with the command to run. It writes the stamp
|
|
4
|
+
* that `setupOnce` reads unless `pi` failed, so the next run tries again. Without `pi` on PATH it writes the stamp, so
|
|
5
|
+
* a user without Pi gets the hint once per version.
|
|
7
6
|
*/
|
|
8
7
|
export declare function setup(packageDir: string): void;
|
|
9
8
|
/**
|
package/dist/setup.js
CHANGED
|
@@ -6,23 +6,21 @@ import { spawnSync } from "node:child_process";
|
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import { basename, delimiter, dirname, join, sep } from "node:path";
|
|
9
|
+
import { basename, delimiter, dirname, join, resolve, sep } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
10
11
|
import { Type } from "typebox";
|
|
11
12
|
import { Value } from "typebox/value";
|
|
12
|
-
const PackageJson = Type.Object({
|
|
13
|
-
name: Type.String(),
|
|
14
|
-
version: Type.String(),
|
|
15
|
-
retiredPiPackages: Type.Optional(Type.Array(Type.String())),
|
|
16
|
-
});
|
|
13
|
+
const PackageJson = Type.Object({ name: Type.String(), version: Type.String() });
|
|
17
14
|
const PiSettings = Type.Object({
|
|
18
15
|
packages: Type.Optional(Type.Array(Type.Union([Type.String(), Type.Object({ source: Type.String() })]))),
|
|
19
16
|
});
|
|
17
|
+
/** The prefixes of the Pi package sources that are not local paths, as Pi reads them. */
|
|
18
|
+
const REMOTE = ["npm:", "git:", "github:", "http:", "https:", "ssh:", "builtin:"];
|
|
20
19
|
/**
|
|
21
|
-
* Registers `<packageDir>/pi` in Pi when `packageDir` is a top-level global npm install.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* on PATH it writes the stamp, so a user without Pi gets the hint once per version.
|
|
20
|
+
* Registers `<packageDir>/pi` in Pi when `packageDir` is a top-level global npm install. It writes its messages to
|
|
21
|
+
* stderr, and it does not throw for a Pi failure: it writes a warning with the command to run. It writes the stamp
|
|
22
|
+
* that `setupOnce` reads unless `pi` failed, so the next run tries again. Without `pi` on PATH it writes the stamp, so
|
|
23
|
+
* a user without Pi gets the hint once per version.
|
|
26
24
|
*/
|
|
27
25
|
export function setup(packageDir) {
|
|
28
26
|
const installed = readInstalled(packageDir);
|
|
@@ -76,20 +74,16 @@ function writeStamp(installed) {
|
|
|
76
74
|
mkdirSync(dirname(path), { recursive: true });
|
|
77
75
|
writeFileSync(path, `${installed.dir}\n${installed.json.version}\n`);
|
|
78
76
|
}
|
|
79
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* False when `pi install` failed. It runs no `pi` when Pi already lists the extension, as `pi install` would write the
|
|
79
|
+
* entry again in its own form, relative to the agent directory.
|
|
80
|
+
*/
|
|
80
81
|
function registerExtension({ dir, json }) {
|
|
81
82
|
const label = basename(json.name);
|
|
82
83
|
const extension = join(dir, "pi");
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for (const source of listed.filter((item) => item === retired || item.startsWith(`${retired}@`))) {
|
|
87
|
-
process.stderr.write(`${label}: removing ${source} from Pi, which ${json.name} replaces\n`);
|
|
88
|
-
if (!runPi(label, ["remove", source], `To remove it, run: pi remove ${source}`))
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return runPi(label, ["install", extension], later);
|
|
84
|
+
if (piLists(extension))
|
|
85
|
+
return true;
|
|
86
|
+
return runPi(label, ["install", extension], `To add the Pi extension of ${label}, run: pi install ${extension}`);
|
|
93
87
|
}
|
|
94
88
|
/** Runs `pi`, with a warning when it is missing or fails. False when it failed; true when it worked or is missing. */
|
|
95
89
|
function runPi(label, args, later) {
|
|
@@ -109,25 +103,43 @@ function runPi(label, args, later) {
|
|
|
109
103
|
}
|
|
110
104
|
return true;
|
|
111
105
|
}
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.join(delimiter);
|
|
118
|
-
}
|
|
119
|
-
/** The sources of the packages in Pi's global settings, or none when the file is missing or not valid. */
|
|
120
|
-
function piPackages() {
|
|
106
|
+
/**
|
|
107
|
+
* Whether the `packages` of Pi's global settings list the folder `extension` as a local path, absolute or relative to
|
|
108
|
+
* the agent directory, with `~` or as a `file:` URL, as Pi resolves them.
|
|
109
|
+
*/
|
|
110
|
+
function piLists(extension) {
|
|
121
111
|
const env = process.env.PI_CODING_AGENT_DIR;
|
|
122
|
-
const agentDir = env ? env
|
|
112
|
+
const agentDir = env ? expandHome(env) : join(homedir(), ".pi", "agent");
|
|
123
113
|
let settings;
|
|
124
114
|
try {
|
|
125
115
|
settings = JSON.parse(readFileSync(join(agentDir, "settings.json"), "utf8"));
|
|
126
116
|
}
|
|
127
117
|
catch {
|
|
128
|
-
return
|
|
118
|
+
return false;
|
|
129
119
|
}
|
|
130
120
|
if (!Value.Check(PiSettings, settings))
|
|
131
|
-
return
|
|
132
|
-
return (settings.packages ?? []).
|
|
121
|
+
return false;
|
|
122
|
+
return (settings.packages ?? []).some((item) => {
|
|
123
|
+
const source = (Value.Check(Type.String(), item) ? item : item.source).trim();
|
|
124
|
+
if (REMOTE.some((prefix) => source.startsWith(prefix)))
|
|
125
|
+
return false;
|
|
126
|
+
const path = source.startsWith("file://") ? fileURLToPath(source) : resolve(agentDir, expandHome(source));
|
|
127
|
+
try {
|
|
128
|
+
return realpathSync(path) === extension;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/** `path` with a leading `~` replaced by the home directory. */
|
|
136
|
+
function expandHome(path) {
|
|
137
|
+
return path === "~" || path.startsWith("~/") ? join(homedir(), path.slice(1)) : path;
|
|
138
|
+
}
|
|
139
|
+
/** The `PATH` without the `node_modules/.bin` folders that npm adds: pi-coding-agent there has its own `pi`. */
|
|
140
|
+
function userPath() {
|
|
141
|
+
return (process.env.PATH ?? "")
|
|
142
|
+
.split(delimiter)
|
|
143
|
+
.filter((entry) => !entry.endsWith(join("node_modules", ".bin")))
|
|
144
|
+
.join(delimiter);
|
|
133
145
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzlentin/harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "A durable agent harness on Pi, starting with observable, persistent subagents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,9 +49,6 @@
|
|
|
49
49
|
"typescript": "7.0.2",
|
|
50
50
|
"vitest": "5.0.3"
|
|
51
51
|
},
|
|
52
|
-
"retiredPiPackages": [
|
|
53
|
-
"npm:@vzlentin/pi-subagent"
|
|
54
|
-
],
|
|
55
52
|
"publishConfig": {
|
|
56
53
|
"access": "public"
|
|
57
54
|
}
|