pi-landstrip 0.17.35 → 0.17.37
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 +40 -5
- package/dist/index.ts +328 -76
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The extension includes a shared [sandbox policy](./sandbox.json) and separate
|
|
|
16
16
|
[agent configuration](./subagents.json). Global and trusted-project files can
|
|
17
17
|
override both.
|
|
18
18
|
|
|
19
|
-
Process-backed subagents require Pi >= 0.
|
|
19
|
+
Process-backed subagents require Pi >= 0.82.0, and Node.js >= 22.19.0.
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
@@ -208,11 +208,44 @@ merged over the packaged [defaults](./subagents.json). The packaged subagent typ
|
|
|
208
208
|
are `explore`, `general`, and the OpenCode-compatible `scout` reconnaissance agent.
|
|
209
209
|
|
|
210
210
|
Sandbox policy remains in `~/.pi/agent/sandbox.json` and `.pi/sandbox.json`. Pi's
|
|
211
|
-
`settings.json`
|
|
212
|
-
entry.
|
|
211
|
+
`settings.json` only needs the normal package entry for the extension:
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"packages": ["npm:pi-landstrip"]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Optional OpenCode import flags live in `subagents.json` as top-level `opencode`:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"maxSubagents": 0,
|
|
224
|
+
"opencode": {
|
|
225
|
+
"showGlobalAgents": false,
|
|
226
|
+
"showLocalAgents": false
|
|
227
|
+
},
|
|
228
|
+
"subagents": {
|
|
229
|
+
"agent": {}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Both flags default to `false`. When enabled:
|
|
235
|
+
|
|
236
|
+
- `showGlobalAgents` imports Markdown agents from the OpenCode global config
|
|
237
|
+
directory (`$OPENCODE_CONFIG_DIR`, or `$XDG_CONFIG_HOME/opencode`, or
|
|
238
|
+
`~/.config/opencode`) under `agent/` and `agents/`.
|
|
239
|
+
- `showLocalAgents` imports Markdown agents from the trusted project's
|
|
240
|
+
`.opencode/agent/` and `.opencode/agents/` directories.
|
|
241
|
+
|
|
242
|
+
OpenCode local agents override OpenCode global agents. Pi agent definitions
|
|
243
|
+
from `subagents.json` (built-in, global, and local) take precedence over OpenCode
|
|
244
|
+
imports with the same name; conflicts are silent. Local OpenCode agents are
|
|
245
|
+
skipped when the project is untrusted, regardless of the flag.
|
|
246
|
+
|
|
247
|
+
`subagents.json` accepts top-level `maxSubagents`, `opencode`, and `subagents`;
|
|
248
|
+
sandbox fields belong in `sandbox.json`.
|
|
216
249
|
|
|
217
250
|
The Windows sandbox fields are:
|
|
218
251
|
|
|
@@ -306,6 +339,8 @@ follows, then remove the old fields:
|
|
|
306
339
|
- Convert legacy `tools` booleans to explicit `permission` rules.
|
|
307
340
|
- Move Markdown agent prompts into each `subagents.agent.<name>.prompt` string.
|
|
308
341
|
- Put the worker limit in top-level `maxSubagents`; zero disables delegation.
|
|
342
|
+
- Move `landstrip.opencode` flags from Pi `settings.json` to top-level
|
|
343
|
+
`opencode` in `subagents.json`.
|
|
309
344
|
- Use `~/.pi/agent/subagents.json` for global configuration and
|
|
310
345
|
`.pi/subagents.json` for trusted project configuration.
|
|
311
346
|
- Leave sandbox policy in `~/.pi/agent/sandbox.json` and `.pi/sandbox.json`;
|
package/dist/index.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { lookup } from "node:dns/promises";
|
|
5
5
|
import {
|
|
6
|
-
existsSync as
|
|
6
|
+
existsSync as existsSync5,
|
|
7
7
|
mkdirSync as mkdirSync3,
|
|
8
8
|
mkdtempSync as mkdtempSync2,
|
|
9
|
-
readFileSync as
|
|
9
|
+
readFileSync as readFileSync5,
|
|
10
10
|
realpathSync as realpathSync2,
|
|
11
11
|
rmSync as rmSync2,
|
|
12
12
|
writeFileSync as writeFileSync3
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
isIP,
|
|
19
19
|
Socket as NetSocket
|
|
20
20
|
} from "node:net";
|
|
21
|
-
import { homedir as
|
|
22
|
-
import { basename as
|
|
21
|
+
import { homedir as homedir4, tmpdir as tmpdir2 } from "node:os";
|
|
22
|
+
import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join6, parse, resolve as resolve2 } from "node:path";
|
|
23
23
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
24
24
|
import { fileURLToPath as fileURLToPath3, URL } from "node:url";
|
|
25
25
|
import {
|
|
@@ -70,18 +70,18 @@ function encodeLandstripContext(context) {
|
|
|
70
70
|
// subagents.ts
|
|
71
71
|
import { randomUUID } from "node:crypto";
|
|
72
72
|
import {
|
|
73
|
-
existsSync as
|
|
73
|
+
existsSync as existsSync4,
|
|
74
74
|
lstatSync,
|
|
75
75
|
mkdirSync as mkdirSync2,
|
|
76
76
|
mkdtempSync,
|
|
77
|
-
readFileSync as
|
|
77
|
+
readFileSync as readFileSync4,
|
|
78
78
|
readlinkSync,
|
|
79
79
|
realpathSync,
|
|
80
80
|
rmSync,
|
|
81
81
|
writeFileSync as writeFileSync2
|
|
82
82
|
} from "node:fs";
|
|
83
|
-
import { homedir as
|
|
84
|
-
import { basename, dirname as dirname2, isAbsolute, join as
|
|
83
|
+
import { homedir as homedir3, tmpdir } from "node:os";
|
|
84
|
+
import { basename as basename2, dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve, sep as sep2 } from "node:path";
|
|
85
85
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
86
86
|
import {
|
|
87
87
|
getAgentDir as getAgentDir3,
|
|
@@ -92,8 +92,8 @@ import { matchesKey, Text, truncateToWidth as truncateToWidth2 } from "@earendil
|
|
|
92
92
|
import { Type } from "typebox";
|
|
93
93
|
|
|
94
94
|
// agents.ts
|
|
95
|
-
import { existsSync as
|
|
96
|
-
import { join as
|
|
95
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
96
|
+
import { join as join4 } from "node:path";
|
|
97
97
|
import { minimatch } from "minimatch";
|
|
98
98
|
import { getAgentDir as getAgentDir2 } from "@earendil-works/pi-coding-agent";
|
|
99
99
|
|
|
@@ -160,6 +160,38 @@ class AsyncQueue {
|
|
|
160
160
|
// config.ts
|
|
161
161
|
var MAX_SUBAGENTS = 16;
|
|
162
162
|
var packageDir = dirname(fileURLToPath(import.meta.url));
|
|
163
|
+
var DEFAULT_OPENCODE = {
|
|
164
|
+
showGlobalAgents: false,
|
|
165
|
+
showLocalAgents: false
|
|
166
|
+
};
|
|
167
|
+
var OPENCODE_KEYS = new Set(["showGlobalAgents", "showLocalAgents"]);
|
|
168
|
+
function readBooleanField(section, key, path) {
|
|
169
|
+
if (!(key in section))
|
|
170
|
+
return;
|
|
171
|
+
const value = section[key];
|
|
172
|
+
if (typeof value === "boolean")
|
|
173
|
+
return value;
|
|
174
|
+
throw new Error(`${path}.${key} must be a boolean`);
|
|
175
|
+
}
|
|
176
|
+
function normalizeOpenCode(value, path) {
|
|
177
|
+
if (value === undefined)
|
|
178
|
+
return {};
|
|
179
|
+
if (!isRecord2(value))
|
|
180
|
+
throw new Error(`${path} must be a JSON object`);
|
|
181
|
+
for (const key of Object.keys(value)) {
|
|
182
|
+
if (!OPENCODE_KEYS.has(key)) {
|
|
183
|
+
throw new Error(`${path}: unknown field ${key}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const result = {};
|
|
187
|
+
const showGlobalAgents = readBooleanField(value, "showGlobalAgents", path);
|
|
188
|
+
if (showGlobalAgents !== undefined)
|
|
189
|
+
result.showGlobalAgents = showGlobalAgents;
|
|
190
|
+
const showLocalAgents = readBooleanField(value, "showLocalAgents", path);
|
|
191
|
+
if (showLocalAgents !== undefined)
|
|
192
|
+
result.showLocalAgents = showLocalAgents;
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
163
195
|
function readConfig(path) {
|
|
164
196
|
if (!existsSync(path))
|
|
165
197
|
return {};
|
|
@@ -173,11 +205,27 @@ function readConfig(path) {
|
|
|
173
205
|
if (!isRecord2(value))
|
|
174
206
|
throw new Error(`${path} must contain a JSON object`);
|
|
175
207
|
for (const key of Object.keys(value)) {
|
|
176
|
-
if (key !== "maxSubagents" && key !== "subagents") {
|
|
208
|
+
if (key !== "maxSubagents" && key !== "subagents" && key !== "opencode") {
|
|
177
209
|
throw new Error(`${path}: unknown top-level field ${key}`);
|
|
178
210
|
}
|
|
179
211
|
}
|
|
180
|
-
|
|
212
|
+
const config = {};
|
|
213
|
+
if ("maxSubagents" in value) {
|
|
214
|
+
if (typeof value.maxSubagents !== "number") {
|
|
215
|
+
throw new Error(`${path}: maxSubagents must be a number`);
|
|
216
|
+
}
|
|
217
|
+
config.maxSubagents = value.maxSubagents;
|
|
218
|
+
}
|
|
219
|
+
if ("subagents" in value) {
|
|
220
|
+
if (!isRecord2(value.subagents)) {
|
|
221
|
+
throw new Error(`${path}: subagents must be an object`);
|
|
222
|
+
}
|
|
223
|
+
config.subagents = value.subagents;
|
|
224
|
+
}
|
|
225
|
+
if ("opencode" in value) {
|
|
226
|
+
config.opencode = normalizeOpenCode(value.opencode, `${path}: opencode`);
|
|
227
|
+
}
|
|
228
|
+
return config;
|
|
181
229
|
}
|
|
182
230
|
function mergeValue(base, override) {
|
|
183
231
|
if (override === undefined)
|
|
@@ -239,7 +287,182 @@ function loadLandstripConfig(cwd, includeProject = true, agentDir = getAgentDir(
|
|
|
239
287
|
}
|
|
240
288
|
if (!isRecord2(config.subagents))
|
|
241
289
|
throw new Error("subagents must be an object");
|
|
242
|
-
|
|
290
|
+
const opencode = {
|
|
291
|
+
...DEFAULT_OPENCODE,
|
|
292
|
+
...config.opencode
|
|
293
|
+
};
|
|
294
|
+
return { ...config, opencode, agentSources };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// opencode-agents.ts
|
|
298
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
|
|
299
|
+
import { homedir as homedir2 } from "node:os";
|
|
300
|
+
import { basename, extname, join as join3, relative, sep } from "node:path";
|
|
301
|
+
import { parse as parseYaml } from "yaml";
|
|
302
|
+
var AGENT_DIR_PREFIXES = ["agent/", "agents/"];
|
|
303
|
+
function openCodeGlobalConfigDir() {
|
|
304
|
+
if (process.env.OPENCODE_CONFIG_DIR)
|
|
305
|
+
return process.env.OPENCODE_CONFIG_DIR;
|
|
306
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
307
|
+
return join3(xdg && xdg.length > 0 ? xdg : join3(homedir2(), ".config"), "opencode");
|
|
308
|
+
}
|
|
309
|
+
function listMarkdownFiles(root) {
|
|
310
|
+
if (!existsSync2(root))
|
|
311
|
+
return [];
|
|
312
|
+
const results = [];
|
|
313
|
+
const visit = (dir) => {
|
|
314
|
+
let entries;
|
|
315
|
+
try {
|
|
316
|
+
entries = readdirSync(dir);
|
|
317
|
+
} catch {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
for (const entry of entries) {
|
|
321
|
+
const path = join3(dir, entry);
|
|
322
|
+
let stats;
|
|
323
|
+
try {
|
|
324
|
+
stats = statSync(path);
|
|
325
|
+
} catch {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (stats.isDirectory())
|
|
329
|
+
visit(path);
|
|
330
|
+
else if (stats.isFile() && extname(entry).toLowerCase() === ".md")
|
|
331
|
+
results.push(path);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
visit(root);
|
|
335
|
+
return results;
|
|
336
|
+
}
|
|
337
|
+
function agentNameFromPath(dir, filePath) {
|
|
338
|
+
const relativePath = relative(dir, filePath).split(sep).join("/");
|
|
339
|
+
for (const prefix of AGENT_DIR_PREFIXES) {
|
|
340
|
+
if (relativePath.startsWith(prefix)) {
|
|
341
|
+
const candidate = relativePath.slice(prefix.length);
|
|
342
|
+
const extension2 = extname(candidate);
|
|
343
|
+
return extension2.length > 0 ? candidate.slice(0, -extension2.length) : candidate;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const base = basename(filePath);
|
|
347
|
+
const extension = extname(base);
|
|
348
|
+
return extension.length > 0 ? base.slice(0, -extension.length) : base;
|
|
349
|
+
}
|
|
350
|
+
function parseFrontmatter(content) {
|
|
351
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/);
|
|
352
|
+
if (!match)
|
|
353
|
+
return { data: {}, body: content };
|
|
354
|
+
const parsed = parseYaml(match[1] ?? "");
|
|
355
|
+
if (parsed === null || parsed === undefined)
|
|
356
|
+
return { data: {}, body: match[2] ?? "" };
|
|
357
|
+
if (!isRecord2(parsed))
|
|
358
|
+
throw new Error("frontmatter must be a YAML mapping");
|
|
359
|
+
return { data: { ...parsed }, body: match[2] ?? "" };
|
|
360
|
+
}
|
|
361
|
+
function convertToolsToPermission(tools) {
|
|
362
|
+
if (!isRecord2(tools))
|
|
363
|
+
throw new Error("tools must be a map of booleans");
|
|
364
|
+
const permission = {};
|
|
365
|
+
for (const [tool, enabled] of Object.entries(tools)) {
|
|
366
|
+
if (typeof enabled !== "boolean") {
|
|
367
|
+
throw new Error(`tools.${tool} must be a boolean`);
|
|
368
|
+
}
|
|
369
|
+
const action = enabled ? "allow" : "deny";
|
|
370
|
+
if (tool === "write" || tool === "edit" || tool === "patch") {
|
|
371
|
+
permission.edit = action;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
permission[tool] = action;
|
|
375
|
+
}
|
|
376
|
+
return permission;
|
|
377
|
+
}
|
|
378
|
+
function normalizeOpenCodeAgentRaw(data, prompt) {
|
|
379
|
+
const known = new Set([
|
|
380
|
+
"name",
|
|
381
|
+
"model",
|
|
382
|
+
"variant",
|
|
383
|
+
"prompt",
|
|
384
|
+
"description",
|
|
385
|
+
"temperature",
|
|
386
|
+
"top_p",
|
|
387
|
+
"mode",
|
|
388
|
+
"hidden",
|
|
389
|
+
"color",
|
|
390
|
+
"steps",
|
|
391
|
+
"maxSteps",
|
|
392
|
+
"options",
|
|
393
|
+
"permission",
|
|
394
|
+
"disable",
|
|
395
|
+
"tools"
|
|
396
|
+
]);
|
|
397
|
+
const options = isRecord2(data.options) ? { ...data.options } : {};
|
|
398
|
+
for (const [key, value] of Object.entries(data)) {
|
|
399
|
+
if (!known.has(key))
|
|
400
|
+
options[key] = value;
|
|
401
|
+
}
|
|
402
|
+
let permission = {};
|
|
403
|
+
if (data.tools !== undefined) {
|
|
404
|
+
permission = convertToolsToPermission(data.tools);
|
|
405
|
+
}
|
|
406
|
+
if (data.permission !== undefined) {
|
|
407
|
+
if (!isRecord2(data.permission))
|
|
408
|
+
throw new Error("permission must be an object");
|
|
409
|
+
permission = { ...permission, ...data.permission };
|
|
410
|
+
}
|
|
411
|
+
const steps = data.steps ?? data.maxSteps;
|
|
412
|
+
const raw = {
|
|
413
|
+
description: data.description,
|
|
414
|
+
prompt,
|
|
415
|
+
mode: data.mode,
|
|
416
|
+
model: data.model,
|
|
417
|
+
variant: data.variant,
|
|
418
|
+
hidden: data.hidden,
|
|
419
|
+
disable: data.disable,
|
|
420
|
+
color: data.color,
|
|
421
|
+
temperature: data.temperature,
|
|
422
|
+
top_p: data.top_p
|
|
423
|
+
};
|
|
424
|
+
if (steps !== undefined)
|
|
425
|
+
raw.steps = steps;
|
|
426
|
+
if (Object.keys(permission).length > 0)
|
|
427
|
+
raw.permission = permission;
|
|
428
|
+
if (Object.keys(options).length > 0)
|
|
429
|
+
raw.options = options;
|
|
430
|
+
for (const key of Object.keys(raw)) {
|
|
431
|
+
if (raw[key] === undefined)
|
|
432
|
+
delete raw[key];
|
|
433
|
+
}
|
|
434
|
+
return raw;
|
|
435
|
+
}
|
|
436
|
+
function loadAgentsFromDir(dir, source, agents, diagnostics) {
|
|
437
|
+
for (const subdir of ["agent", "agents"]) {
|
|
438
|
+
const root = join3(dir, subdir);
|
|
439
|
+
for (const filePath of listMarkdownFiles(root)) {
|
|
440
|
+
try {
|
|
441
|
+
const content = readFileSync2(filePath, "utf8");
|
|
442
|
+
const { data, body } = parseFrontmatter(content);
|
|
443
|
+
const name = agentNameFromPath(dir, filePath);
|
|
444
|
+
if (!name) {
|
|
445
|
+
diagnostics.push(`${filePath}: agent name is empty`);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const raw = normalizeOpenCodeAgentRaw(data, body.trim());
|
|
449
|
+
agents.set(name, { name, source, path: filePath, raw });
|
|
450
|
+
} catch (error) {
|
|
451
|
+
diagnostics.push(`${filePath}: ${formatError(error)}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function loadOpenCodeAgents(options) {
|
|
457
|
+
const agents = new Map;
|
|
458
|
+
const diagnostics = [];
|
|
459
|
+
if (options.includeGlobal) {
|
|
460
|
+
loadAgentsFromDir(options.globalConfigDir ?? openCodeGlobalConfigDir(), "global", agents, diagnostics);
|
|
461
|
+
}
|
|
462
|
+
if (options.includeProject) {
|
|
463
|
+
loadAgentsFromDir(join3(options.cwd, ".opencode"), "local", agents, diagnostics);
|
|
464
|
+
}
|
|
465
|
+
return { agents, diagnostics };
|
|
243
466
|
}
|
|
244
467
|
|
|
245
468
|
// agents.ts
|
|
@@ -290,14 +513,14 @@ function normalizePermissions(value) {
|
|
|
290
513
|
return Object.entries(value).flatMap(([permission, rules]) => permissionEntries(permission, rules));
|
|
291
514
|
}
|
|
292
515
|
function legacyConfigWarnings(piAgentDir) {
|
|
293
|
-
const path =
|
|
294
|
-
if (!
|
|
516
|
+
const path = join4(piAgentDir, "settings.json");
|
|
517
|
+
if (!existsSync3(path))
|
|
295
518
|
return [];
|
|
296
519
|
try {
|
|
297
|
-
const value = JSON.parse(
|
|
520
|
+
const value = JSON.parse(readFileSync3(path, "utf8"));
|
|
298
521
|
if (!isRecord2(value))
|
|
299
522
|
return [];
|
|
300
|
-
const fields = ["agent", "permission", "subagents", "maxSubagents"].filter((field) => value[field] !== undefined);
|
|
523
|
+
const fields = ["agent", "permission", "subagents", "maxSubagents", "landstrip"].filter((field) => value[field] !== undefined);
|
|
301
524
|
if (fields.length === 0)
|
|
302
525
|
return [];
|
|
303
526
|
return [
|
|
@@ -360,21 +583,40 @@ function normalizeAgent(name, raw, source) {
|
|
|
360
583
|
function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = true) {
|
|
361
584
|
const warnings = [
|
|
362
585
|
...legacyConfigWarnings(piAgentDir),
|
|
363
|
-
...includeProject ? legacyConfigWarnings(
|
|
586
|
+
...includeProject ? legacyConfigWarnings(join4(cwd, ".pi")) : []
|
|
364
587
|
];
|
|
365
588
|
const diagnostics = [];
|
|
366
589
|
let maxSubagents = 0;
|
|
367
590
|
let subagents = {};
|
|
368
591
|
let agentSources = new Map;
|
|
592
|
+
let showGlobalAgents = false;
|
|
593
|
+
let showLocalAgents = false;
|
|
369
594
|
try {
|
|
370
595
|
const config = loadLandstripConfig(cwd, includeProject, piAgentDir);
|
|
371
596
|
maxSubagents = config.maxSubagents;
|
|
372
597
|
subagents = config.subagents;
|
|
373
598
|
agentSources = new Map(config.agentSources);
|
|
599
|
+
showGlobalAgents = config.opencode.showGlobalAgents;
|
|
600
|
+
showLocalAgents = config.opencode.showLocalAgents;
|
|
374
601
|
} catch (error) {
|
|
375
602
|
diagnostics.push(formatError(error));
|
|
376
603
|
}
|
|
604
|
+
const openCode = loadOpenCodeAgents({
|
|
605
|
+
cwd,
|
|
606
|
+
includeGlobal: showGlobalAgents,
|
|
607
|
+
includeProject: showLocalAgents && includeProject
|
|
608
|
+
});
|
|
609
|
+
diagnostics.push(...openCode.diagnostics);
|
|
377
610
|
const normalized = new Map;
|
|
611
|
+
for (const imported of openCode.agents.values()) {
|
|
612
|
+
try {
|
|
613
|
+
const agent = normalizeAgent(imported.name, imported.raw, imported.source);
|
|
614
|
+
if (agent)
|
|
615
|
+
normalized.set(imported.name, agent);
|
|
616
|
+
} catch (error) {
|
|
617
|
+
diagnostics.push(`${imported.path}: ${formatError(error)}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
378
620
|
if (subagents.agent !== undefined && !isRecord2(subagents.agent)) {
|
|
379
621
|
diagnostics.push("subagents.agent must be an object");
|
|
380
622
|
} else if (isRecord2(subagents.agent)) {
|
|
@@ -388,6 +630,8 @@ function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = tru
|
|
|
388
630
|
const agent = normalizeAgent(name, value, agentSources.get(name) ?? "built-in");
|
|
389
631
|
if (agent)
|
|
390
632
|
normalized.set(name, agent);
|
|
633
|
+
else
|
|
634
|
+
normalized.delete(name);
|
|
391
635
|
} catch (error) {
|
|
392
636
|
diagnostics.push(formatError(error));
|
|
393
637
|
}
|
|
@@ -878,14 +1122,22 @@ var MAX_DEPTH = 3;
|
|
|
878
1122
|
var packageDir2 = dirname2(fileURLToPath2(import.meta.url));
|
|
879
1123
|
var MAX_TASK_OUTPUT_BYTES = 64 * 1024;
|
|
880
1124
|
var INSPECTOR_BODY_LINES = 16;
|
|
881
|
-
var
|
|
882
|
-
var MIN_SUPPORTED_PI_MINOR = 80;
|
|
883
|
-
var MIN_SUPPORTED_PI_PATCH = 6;
|
|
1125
|
+
var MIN_SUPPORTED_PI_VERSION = [0, 82, 0];
|
|
884
1126
|
var cachedPiPackage;
|
|
885
1127
|
var piPackageResolved = false;
|
|
886
1128
|
function isSupportedPiVersion(version) {
|
|
887
|
-
|
|
888
|
-
|
|
1129
|
+
if (version.length !== 3 || !version.every((part) => Number.isInteger(part) && part >= 0)) {
|
|
1130
|
+
return false;
|
|
1131
|
+
}
|
|
1132
|
+
for (let i = 0;i < 3; i++) {
|
|
1133
|
+
const part = version[i];
|
|
1134
|
+
const min = MIN_SUPPORTED_PI_VERSION[i];
|
|
1135
|
+
if (part > min)
|
|
1136
|
+
return true;
|
|
1137
|
+
if (part < min)
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
return true;
|
|
889
1141
|
}
|
|
890
1142
|
function resolvePiPackage() {
|
|
891
1143
|
if (piPackageResolved)
|
|
@@ -895,8 +1147,8 @@ function resolvePiPackage() {
|
|
|
895
1147
|
const entry = fileURLToPath2(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
|
896
1148
|
let dir2 = dirname2(entry);
|
|
897
1149
|
for (;; ) {
|
|
898
|
-
const pkgPath =
|
|
899
|
-
if (
|
|
1150
|
+
const pkgPath = join5(dir2, "package.json");
|
|
1151
|
+
if (existsSync4(pkgPath)) {
|
|
900
1152
|
const pkg = readPiPackage(pkgPath);
|
|
901
1153
|
if (pkg)
|
|
902
1154
|
return pkg;
|
|
@@ -909,8 +1161,8 @@ function resolvePiPackage() {
|
|
|
909
1161
|
} catch {}
|
|
910
1162
|
let dir = packageDir2;
|
|
911
1163
|
for (;; ) {
|
|
912
|
-
const pkgPath =
|
|
913
|
-
if (
|
|
1164
|
+
const pkgPath = join5(dir, "node_modules", "@earendil-works", "pi-coding-agent", "package.json");
|
|
1165
|
+
if (existsSync4(pkgPath)) {
|
|
914
1166
|
const pkg = readPiPackage(pkgPath);
|
|
915
1167
|
if (pkg)
|
|
916
1168
|
return pkg;
|
|
@@ -923,7 +1175,7 @@ function resolvePiPackage() {
|
|
|
923
1175
|
}
|
|
924
1176
|
function readPiPackage(pkgPath) {
|
|
925
1177
|
try {
|
|
926
|
-
const value = JSON.parse(
|
|
1178
|
+
const value = JSON.parse(readFileSync4(pkgPath, "utf8"));
|
|
927
1179
|
if (!isRecord2(value) || value.name !== "@earendil-works/pi-coding-agent" || typeof value.version !== "string") {
|
|
928
1180
|
return;
|
|
929
1181
|
}
|
|
@@ -933,8 +1185,8 @@ function readPiPackage(pkgPath) {
|
|
|
933
1185
|
const binFile = typeof value.bin === "string" ? value.bin : isRecord2(value.bin) && typeof value.bin.pi === "string" ? value.bin.pi : undefined;
|
|
934
1186
|
if (!binFile)
|
|
935
1187
|
return;
|
|
936
|
-
const cliEntry =
|
|
937
|
-
if (!
|
|
1188
|
+
const cliEntry = join5(dirname2(pkgPath), binFile);
|
|
1189
|
+
if (!existsSync4(cliEntry))
|
|
938
1190
|
return;
|
|
939
1191
|
cachedPiPackage = { cliEntry, version: parts.slice(0, 3) };
|
|
940
1192
|
return cachedPiPackage;
|
|
@@ -1008,7 +1260,7 @@ function messageContentText(message) {
|
|
|
1008
1260
|
`);
|
|
1009
1261
|
}
|
|
1010
1262
|
function taskTranscript(task) {
|
|
1011
|
-
if (!task.sessionFile || !
|
|
1263
|
+
if (!task.sessionFile || !existsSync4(task.sessionFile)) {
|
|
1012
1264
|
const fallback = task.error ?? task.output ?? "Child session is not available yet.";
|
|
1013
1265
|
return fallback.split(`
|
|
1014
1266
|
`);
|
|
@@ -1038,7 +1290,7 @@ function isProjectTrusted(ctx) {
|
|
|
1038
1290
|
return trustContext.isProjectTrusted?.() ?? false;
|
|
1039
1291
|
}
|
|
1040
1292
|
function dependencyRoot(path) {
|
|
1041
|
-
const marker = `${
|
|
1293
|
+
const marker = `${sep2}node_modules${sep2}`;
|
|
1042
1294
|
const index = path.lastIndexOf(marker);
|
|
1043
1295
|
return index < 0 ? undefined : path.slice(0, index + marker.length - 1);
|
|
1044
1296
|
}
|
|
@@ -1059,7 +1311,7 @@ function agentBootstrapPaths(agentDir) {
|
|
|
1059
1311
|
"bin",
|
|
1060
1312
|
"npm",
|
|
1061
1313
|
"git"
|
|
1062
|
-
].map((path) =>
|
|
1314
|
+
].map((path) => join5(agentDir, path));
|
|
1063
1315
|
}
|
|
1064
1316
|
|
|
1065
1317
|
class Semaphore {
|
|
@@ -1253,7 +1505,7 @@ function canonicalPermissionPath(path, seen = new Set) {
|
|
|
1253
1505
|
const parent = dirname2(existing);
|
|
1254
1506
|
if (parent === existing)
|
|
1255
1507
|
return path;
|
|
1256
|
-
missing.unshift(
|
|
1508
|
+
missing.unshift(basename2(existing));
|
|
1257
1509
|
existing = parent;
|
|
1258
1510
|
}
|
|
1259
1511
|
}
|
|
@@ -1275,11 +1527,11 @@ function permissionResource(tool, input, cwd) {
|
|
|
1275
1527
|
return "*";
|
|
1276
1528
|
const projectRoot = canonicalPermissionPath(resolve(cwd));
|
|
1277
1529
|
const absolutePath = canonicalPermissionPath(resolve(cwd, input.path));
|
|
1278
|
-
const projectPath =
|
|
1279
|
-
if (projectPath && !isAbsolute(projectPath) && !projectPath.startsWith(`..${
|
|
1280
|
-
return projectPath.split(
|
|
1530
|
+
const projectPath = relative2(projectRoot, absolutePath);
|
|
1531
|
+
if (projectPath && !isAbsolute(projectPath) && !projectPath.startsWith(`..${sep2}`) && projectPath !== "..") {
|
|
1532
|
+
return projectPath.split(sep2).join("/");
|
|
1281
1533
|
}
|
|
1282
|
-
return absolutePath.split(
|
|
1534
|
+
return absolutePath.split(sep2).join("/");
|
|
1283
1535
|
}
|
|
1284
1536
|
function permissionResources(tool, input, cwd) {
|
|
1285
1537
|
if (tool !== "apply_patch" || typeof input.patchText !== "string") {
|
|
@@ -1811,7 +2063,7 @@ ${catalog.diagnostics.join(`
|
|
|
1811
2063
|
if (task.agent !== agent.name) {
|
|
1812
2064
|
throw new Error(`Task ${task.id} belongs to agent ${task.agent}, not ${agent.name}`);
|
|
1813
2065
|
}
|
|
1814
|
-
if (!this.running.has(task.id) && (!task.sessionDir || !
|
|
2066
|
+
if (!this.running.has(task.id) && (!task.sessionDir || !existsSync4(task.sessionDir)) && (!task.sessionFile || !existsSync4(task.sessionFile))) {
|
|
1815
2067
|
throw new Error(`Task ${task.id} session is unavailable`);
|
|
1816
2068
|
}
|
|
1817
2069
|
} else {
|
|
@@ -1921,7 +2173,7 @@ ${catalog.diagnostics.join(`
|
|
|
1921
2173
|
createRecord(input, ctx, parentTask, agent, depth) {
|
|
1922
2174
|
const parentSession = parentTask?.sessionFile ?? ctx.sessionManager.getSessionFile();
|
|
1923
2175
|
const id = randomUUID();
|
|
1924
|
-
const sessionDir =
|
|
2176
|
+
const sessionDir = join5(getAgentDir3(), "sessions", "pi-landstrip", ctx.sessionManager.getSessionId(), id);
|
|
1925
2177
|
mkdirSync2(sessionDir, { recursive: true });
|
|
1926
2178
|
const task = {
|
|
1927
2179
|
version: 1,
|
|
@@ -2111,7 +2363,7 @@ ${catalog.diagnostics.join(`
|
|
|
2111
2363
|
"--mode",
|
|
2112
2364
|
"rpc",
|
|
2113
2365
|
"--extension",
|
|
2114
|
-
|
|
2366
|
+
join5(packageDir2, "index.ts"),
|
|
2115
2367
|
...workerExtensions.flatMap((extension) => ["--extension", extension.entry]),
|
|
2116
2368
|
...task.sessionFile ? ["--session", task.sessionFile] : task.sessionDir ? ["--session-dir", task.sessionDir] : [],
|
|
2117
2369
|
"--model",
|
|
@@ -2126,7 +2378,7 @@ ${catalog.diagnostics.join(`
|
|
|
2126
2378
|
];
|
|
2127
2379
|
const config = { rules, task, taskEnabled, steps: agent.steps };
|
|
2128
2380
|
const publicContext = this.taskContext(task, ctx);
|
|
2129
|
-
const temp = mkdtempSync(
|
|
2381
|
+
const temp = mkdtempSync(join5(tmpdir(), `pi-landstrip-task-${task.id}-`));
|
|
2130
2382
|
const agentDir = getAgentDir3();
|
|
2131
2383
|
let launch;
|
|
2132
2384
|
let rpc;
|
|
@@ -2158,9 +2410,9 @@ ${catalog.diagnostics.join(`
|
|
|
2158
2410
|
...new Set([
|
|
2159
2411
|
ctx.cwd,
|
|
2160
2412
|
...agentBootstrapPaths(agentDir),
|
|
2161
|
-
|
|
2413
|
+
join5(homedir3(), ".agents", "skills"),
|
|
2162
2414
|
packageDir2,
|
|
2163
|
-
|
|
2415
|
+
join5(packageDir2, "node_modules"),
|
|
2164
2416
|
invocation.command,
|
|
2165
2417
|
cliRoot,
|
|
2166
2418
|
extensionRoot,
|
|
@@ -2258,7 +2510,7 @@ ${catalog.diagnostics.join(`
|
|
|
2258
2510
|
throw new Error("Unable to resolve the running Pi package; process-backed subagents are unavailable");
|
|
2259
2511
|
}
|
|
2260
2512
|
if (!isSupportedPiVersion(pkg.version)) {
|
|
2261
|
-
throw new Error(`Process-backed subagents require Pi >=0.
|
|
2513
|
+
throw new Error(`Process-backed subagents require Pi >=0.82.0; found ${pkg.version.join(".")}`);
|
|
2262
2514
|
}
|
|
2263
2515
|
}
|
|
2264
2516
|
async handleWorkerRequest(task, rules, ctx, request) {
|
|
@@ -2382,7 +2634,7 @@ ${renderTaskResult(task.id, "error", message)}`);
|
|
|
2382
2634
|
if (!directory)
|
|
2383
2635
|
throw new Error(`Task ${task.id} has no artifact directory`);
|
|
2384
2636
|
mkdirSync2(directory, { recursive: true });
|
|
2385
|
-
const artifactPath =
|
|
2637
|
+
const artifactPath = join5(directory, `${kind}.txt`);
|
|
2386
2638
|
const bounded = boundTaskOutput(value, artifactPath);
|
|
2387
2639
|
if (kind === "output")
|
|
2388
2640
|
task.outputFile = artifactPath;
|
|
@@ -2628,23 +2880,23 @@ var NETWORK_PERMISSION_OPTIONS = [
|
|
|
2628
2880
|
{ label: "Abort (keep blocked)", key: "esc", action: "abort" }
|
|
2629
2881
|
];
|
|
2630
2882
|
function loadSandboxConfig(cwd, includeProject) {
|
|
2631
|
-
const projectConfigPath =
|
|
2632
|
-
const globalConfigPath =
|
|
2633
|
-
if (!
|
|
2634
|
-
const templatePath =
|
|
2883
|
+
const projectConfigPath = join6(cwd, ".pi", "sandbox.json");
|
|
2884
|
+
const globalConfigPath = join6(getAgentDir4(), "sandbox.json");
|
|
2885
|
+
if (!existsSync5(globalConfigPath)) {
|
|
2886
|
+
const templatePath = join6(packageDir3, "sandbox.json");
|
|
2635
2887
|
mkdirSync3(dirname3(globalConfigPath), { recursive: true });
|
|
2636
|
-
writeFileSync3(globalConfigPath,
|
|
2888
|
+
writeFileSync3(globalConfigPath, readFileSync5(templatePath, "utf-8"), "utf-8");
|
|
2637
2889
|
}
|
|
2638
|
-
let globalConfig = JSON.parse(
|
|
2890
|
+
let globalConfig = JSON.parse(readFileSync5(join6(packageDir3, "sandbox.json"), "utf-8"));
|
|
2639
2891
|
try {
|
|
2640
|
-
const override = JSON.parse(
|
|
2892
|
+
const override = JSON.parse(readFileSync5(globalConfigPath, "utf-8"));
|
|
2641
2893
|
globalConfig = deepMerge(globalConfig, override);
|
|
2642
2894
|
} catch (error) {
|
|
2643
2895
|
console.error(`Warning: Could not parse ${globalConfigPath}: ${error}`);
|
|
2644
2896
|
}
|
|
2645
|
-
if (includeProject &&
|
|
2897
|
+
if (includeProject && existsSync5(projectConfigPath)) {
|
|
2646
2898
|
try {
|
|
2647
|
-
const projectConfig = JSON.parse(
|
|
2899
|
+
const projectConfig = JSON.parse(readFileSync5(projectConfigPath, "utf-8"));
|
|
2648
2900
|
const merged = deepMerge(globalConfig, projectConfig);
|
|
2649
2901
|
merged.windows.backend = globalConfig.windows.backend;
|
|
2650
2902
|
return merged;
|
|
@@ -2719,10 +2971,10 @@ function getConfigPaths(cwd) {
|
|
|
2719
2971
|
return getPiConfigPaths(cwd, "sandbox.json");
|
|
2720
2972
|
}
|
|
2721
2973
|
function readOrEmptyConfig(configPath) {
|
|
2722
|
-
if (!
|
|
2974
|
+
if (!existsSync5(configPath))
|
|
2723
2975
|
return {};
|
|
2724
2976
|
try {
|
|
2725
|
-
return JSON.parse(
|
|
2977
|
+
return JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
2726
2978
|
} catch {
|
|
2727
2979
|
return {};
|
|
2728
2980
|
}
|
|
@@ -2734,7 +2986,7 @@ function writeConfigFile(configPath, config) {
|
|
|
2734
2986
|
}
|
|
2735
2987
|
function getSandboxConfigWriteTarget(cwd, includeProject = true) {
|
|
2736
2988
|
const { globalPath, projectPath } = getConfigPaths(cwd);
|
|
2737
|
-
if (includeProject &&
|
|
2989
|
+
if (includeProject && existsSync5(projectPath)) {
|
|
2738
2990
|
return { scope: "project", path: projectPath };
|
|
2739
2991
|
}
|
|
2740
2992
|
return { scope: "global", path: globalPath };
|
|
@@ -2810,11 +3062,11 @@ function canonicalizePath(filePath, cwd) {
|
|
|
2810
3062
|
} catch {
|
|
2811
3063
|
const tail = [];
|
|
2812
3064
|
let probe = abs;
|
|
2813
|
-
while (!
|
|
3065
|
+
while (!existsSync5(probe)) {
|
|
2814
3066
|
const parent = dirname3(probe);
|
|
2815
3067
|
if (parent === probe)
|
|
2816
3068
|
return abs;
|
|
2817
|
-
tail.unshift(
|
|
3069
|
+
tail.unshift(basename3(probe));
|
|
2818
3070
|
probe = parent;
|
|
2819
3071
|
}
|
|
2820
3072
|
try {
|
|
@@ -2835,26 +3087,26 @@ function matchesPattern(filePath, patterns, cwd) {
|
|
|
2835
3087
|
const escaped = absPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\/|\*\*|\*/g, (token) => token === "**/" ? "(?:.*/)?" : token === "**" ? ".*" : "[^/]*");
|
|
2836
3088
|
return new RegExp(`^${escaped}$`).test(abs);
|
|
2837
3089
|
}
|
|
2838
|
-
const
|
|
2839
|
-
return abs === absPattern || abs.startsWith(absPattern +
|
|
3090
|
+
const sep3 = absPattern.endsWith("/") ? "" : "/";
|
|
3091
|
+
return abs === absPattern || abs.startsWith(absPattern + sep3);
|
|
2840
3092
|
});
|
|
2841
3093
|
}
|
|
2842
3094
|
function normalizeBlockedPath(path, cwd) {
|
|
2843
3095
|
const nativePath = process.platform === "win32" && /^\/[a-zA-Z](?:\/|$)/.test(path) ? `${path[1]}:${path.slice(2)}` : path;
|
|
2844
|
-
return canonicalizePath(isAbsolute2(nativePath) ? nativePath :
|
|
3096
|
+
return canonicalizePath(isAbsolute2(nativePath) ? nativePath : join6(cwd, nativePath), cwd);
|
|
2845
3097
|
}
|
|
2846
3098
|
function pathUnderDirectory(filePath, dir) {
|
|
2847
3099
|
const normalizedFilePath = normalizePathSeparators(filePath);
|
|
2848
3100
|
const normalizedDir = normalizePathSeparators(dir);
|
|
2849
3101
|
if (normalizedFilePath === normalizedDir)
|
|
2850
3102
|
return true;
|
|
2851
|
-
const
|
|
2852
|
-
return normalizedFilePath.startsWith(normalizedDir +
|
|
3103
|
+
const sep3 = normalizedDir.endsWith("/") ? "" : "/";
|
|
3104
|
+
return normalizedFilePath.startsWith(normalizedDir + sep3);
|
|
2853
3105
|
}
|
|
2854
3106
|
function sessionScopeFor(filePath, baseDirectory) {
|
|
2855
3107
|
const dir = dirname3(filePath);
|
|
2856
3108
|
const normalizedDir = normalizePathSeparators(dir);
|
|
2857
|
-
const home =
|
|
3109
|
+
const home = homedir4();
|
|
2858
3110
|
const boundaries = new Set;
|
|
2859
3111
|
if (home)
|
|
2860
3112
|
boundaries.add(home);
|
|
@@ -2870,7 +3122,7 @@ function sessionScopeFor(filePath, baseDirectory) {
|
|
|
2870
3122
|
const first = rest.split("/")[0];
|
|
2871
3123
|
if (!first)
|
|
2872
3124
|
return filePath;
|
|
2873
|
-
return
|
|
3125
|
+
return join6(boundary, first);
|
|
2874
3126
|
}
|
|
2875
3127
|
}
|
|
2876
3128
|
if (pathUnderDirectory(dir, baseDirectory))
|
|
@@ -3143,8 +3395,8 @@ function writeEnvFile(env, proxyPort, proxyToken) {
|
|
|
3143
3395
|
lines.push("export NO_PROXY=''");
|
|
3144
3396
|
lines.push("export no_proxy=''");
|
|
3145
3397
|
}
|
|
3146
|
-
const dir = mkdtempSync2(
|
|
3147
|
-
const path =
|
|
3398
|
+
const dir = mkdtempSync2(join6(tmpdir2(), "pi-landstrip-env-"));
|
|
3399
|
+
const path = join6(dir, "env.sh");
|
|
3148
3400
|
writeFileSync3(path, lines.join(`
|
|
3149
3401
|
`), "utf-8");
|
|
3150
3402
|
return { dir, path };
|
|
@@ -3285,7 +3537,7 @@ function createLandstripIntegration(options = {}) {
|
|
|
3285
3537
|
if (!isAbsolute2(requestedEntry)) {
|
|
3286
3538
|
throw new Error("Worker extension entry must be an absolute path or file URL");
|
|
3287
3539
|
}
|
|
3288
|
-
if (!
|
|
3540
|
+
if (!existsSync5(requestedEntry)) {
|
|
3289
3541
|
throw new Error(`Worker extension entry does not exist: ${requestedEntry}`);
|
|
3290
3542
|
}
|
|
3291
3543
|
const entry = realpathSync2(requestedEntry);
|
|
@@ -3327,7 +3579,7 @@ function createLandstripIntegration(options = {}) {
|
|
|
3327
3579
|
const paths = mergeAllowances(config.filesystem.allowRead, sessionAllowedReadPaths, allowances?.readPaths);
|
|
3328
3580
|
if (process.platform !== "win32")
|
|
3329
3581
|
return paths;
|
|
3330
|
-
return paths.filter((path) => !path.startsWith("/") && (path.includes("*") ||
|
|
3582
|
+
return paths.filter((path) => !path.startsWith("/") && (path.includes("*") || existsSync5(expandPath(path, cwd))));
|
|
3331
3583
|
}
|
|
3332
3584
|
function getEffectiveAllowWrite(config, allowances) {
|
|
3333
3585
|
const paths = mergeAllowances(config.filesystem.allowWrite, sessionAllowedWritePaths, allowances?.writePaths);
|
|
@@ -3433,8 +3685,8 @@ function createLandstripIntegration(options = {}) {
|
|
|
3433
3685
|
};
|
|
3434
3686
|
}
|
|
3435
3687
|
function writePolicyFile(cwd, proxyPort, allowances) {
|
|
3436
|
-
const dir = mkdtempSync2(
|
|
3437
|
-
const path =
|
|
3688
|
+
const dir = mkdtempSync2(join6(tmpdir2(), "pi-landstrip-"));
|
|
3689
|
+
const path = join6(dir, "policy.json");
|
|
3438
3690
|
writeFileSync3(path, JSON.stringify(buildLandstripPolicy(cwd, proxyPort, allowances), null, 2) + `
|
|
3439
3691
|
`, "utf-8");
|
|
3440
3692
|
return { dir, path };
|
|
@@ -3861,7 +4113,7 @@ function createLandstripIntegration(options = {}) {
|
|
|
3861
4113
|
function createLandstripBashOps(ctx, callbacks = {}, allowances = { domains: [], readPaths: [], writePaths: [], targets: [] }) {
|
|
3862
4114
|
return {
|
|
3863
4115
|
async exec(command, cwd, { onData, signal, timeout, env }) {
|
|
3864
|
-
if (!
|
|
4116
|
+
if (!existsSync5(cwd))
|
|
3865
4117
|
throw new Error(`Working directory does not exist: ${cwd}`);
|
|
3866
4118
|
const { shell, args } = getShellConfig(SettingsManager.create(cwd).getShellPath());
|
|
3867
4119
|
const config = loadConfig(cwd);
|
|
@@ -4225,7 +4477,7 @@ ${message}
|
|
|
4225
4477
|
networkLabel = `${config.network.allowedDomains.length} domains`;
|
|
4226
4478
|
networkColor = "accent";
|
|
4227
4479
|
}
|
|
4228
|
-
const
|
|
4480
|
+
const sep3 = theme.fg("dim", "·");
|
|
4229
4481
|
const net = theme.fg(networkColor, networkLabel);
|
|
4230
4482
|
const write = theme.fg("accent", `${config.filesystem.allowWrite.length} write paths`);
|
|
4231
4483
|
const fields = [`${dot} ${label}`, net, write];
|
|
@@ -4235,7 +4487,7 @@ ${message}
|
|
|
4235
4487
|
const label2 = backend === "restrictedUser" ? "restricted user" : `${config.windows.appContainerMode} AppContainer`;
|
|
4236
4488
|
fields.splice(1, 0, theme.fg(standard ? "warning" : "accent", label2));
|
|
4237
4489
|
}
|
|
4238
|
-
setTuiStatus(ctx, "sandbox", fields.join(` ${
|
|
4490
|
+
setTuiStatus(ctx, "sandbox", fields.join(` ${sep3} `));
|
|
4239
4491
|
}
|
|
4240
4492
|
const headlessWarnings = new Set;
|
|
4241
4493
|
function warnUnsandboxed(ctx, message, level) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.37",
|
|
4
4
|
"description": "Sandbox-aware Pi subagents and interactive command permissions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"landstrip",
|
|
@@ -40,14 +40,15 @@
|
|
|
40
40
|
"ci:test": "npm run test"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@landstrip/landstrip": "^0.17.
|
|
44
|
-
"minimatch": "^10.2.5"
|
|
43
|
+
"@landstrip/landstrip": "^0.17.37",
|
|
44
|
+
"minimatch": "^10.2.5",
|
|
45
|
+
"yaml": "^2.9.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
48
|
+
"@earendil-works/pi-coding-agent": "^0.82.0",
|
|
48
49
|
"@earendil-works/pi-tui": "^0.81.1",
|
|
49
50
|
"@types/node": "^24.0.0",
|
|
50
|
-
"oxfmt": "^0.
|
|
51
|
+
"oxfmt": "^0.60.0",
|
|
51
52
|
"oxlint": "^1.68.0",
|
|
52
53
|
"typebox": "^1.3.6",
|
|
53
54
|
"typescript": "^5.0.0",
|