pi-landstrip 0.18.11 → 0.18.13
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/index.ts +146 -287
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -143,11 +143,6 @@ Pi Markdown agents are loaded from `~/.pi/agent/agents/` and `.pi/agents/`.
|
|
|
143
143
|
`landstrip.agent` settings override Markdown agents with the same name.
|
|
144
144
|
Project definitions override global definitions.
|
|
145
145
|
|
|
146
|
-
OpenCode agent imports are enabled by default. Set
|
|
147
|
-
`landstrip.opencode.showGlobalAgents` or `showLocalAgents` to `false` to disable
|
|
148
|
-
a source. OpenCode agents fill missing names; Pi agents take precedence.
|
|
149
|
-
Project imports require a trusted project.
|
|
150
|
-
|
|
151
146
|
See the main [Landstrip documentation](https://github.com/landstrip/landstrip#readme)
|
|
152
147
|
for `sandbox.json` fields and platform policy semantics.
|
|
153
148
|
|
package/dist/index.ts
CHANGED
|
@@ -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 homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
22
|
+
import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as join7, parse, resolve as resolve3 } from "node:path";
|
|
23
23
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
24
24
|
import { fileURLToPath as fileURLToPath2, URL } from "node:url";
|
|
25
25
|
import {
|
|
@@ -80,8 +80,8 @@ import {
|
|
|
80
80
|
rmSync,
|
|
81
81
|
writeFileSync as writeFileSync3
|
|
82
82
|
} from "node:fs";
|
|
83
|
-
import { homedir as
|
|
84
|
-
import { basename
|
|
83
|
+
import { homedir as homedir2, tmpdir } from "node:os";
|
|
84
|
+
import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join6, relative as relative3, resolve as resolve2, sep as sep3 } from "node:path";
|
|
85
85
|
import { fileURLToPath } from "node:url";
|
|
86
86
|
import {
|
|
87
87
|
getAgentDir as getAgentDir4,
|
|
@@ -275,38 +275,69 @@ function colorizeAgentText(color, text, themeFg, fallback = "accent") {
|
|
|
275
275
|
|
|
276
276
|
class AsyncQueue {
|
|
277
277
|
tail = Promise.resolve();
|
|
278
|
+
generation = 0;
|
|
279
|
+
resetWaiters = [];
|
|
278
280
|
async acquire(signal, cancellationMessage = "Request cancelled") {
|
|
279
281
|
if (signal?.aborted)
|
|
280
282
|
throw new Error(cancellationMessage);
|
|
283
|
+
const generation = this.generation;
|
|
281
284
|
let release;
|
|
282
285
|
const previous = this.tail;
|
|
283
286
|
this.tail = new Promise((resolve2) => {
|
|
284
287
|
release = resolve2;
|
|
285
288
|
});
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
const finish = () => {
|
|
290
|
+
release?.();
|
|
291
|
+
};
|
|
292
|
+
let wakeReset;
|
|
293
|
+
const resetSignal = new Promise((resolve2) => {
|
|
294
|
+
wakeReset = resolve2;
|
|
295
|
+
this.resetWaiters.push(resolve2);
|
|
296
|
+
});
|
|
297
|
+
const dropResetWaiter = () => {
|
|
298
|
+
if (!wakeReset)
|
|
299
|
+
return;
|
|
300
|
+
const index = this.resetWaiters.indexOf(wakeReset);
|
|
301
|
+
if (index >= 0)
|
|
302
|
+
this.resetWaiters.splice(index, 1);
|
|
303
|
+
wakeReset = undefined;
|
|
304
|
+
};
|
|
291
305
|
try {
|
|
292
|
-
|
|
293
|
-
previous,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
306
|
+
if (!signal) {
|
|
307
|
+
await Promise.race([previous, resetSignal]);
|
|
308
|
+
} else {
|
|
309
|
+
let abort;
|
|
310
|
+
try {
|
|
311
|
+
await Promise.race([
|
|
312
|
+
previous,
|
|
313
|
+
resetSignal,
|
|
314
|
+
new Promise((_resolve, reject) => {
|
|
315
|
+
abort = () => reject(new Error(cancellationMessage));
|
|
316
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
317
|
+
})
|
|
318
|
+
]);
|
|
319
|
+
} finally {
|
|
320
|
+
if (abort)
|
|
321
|
+
signal.removeEventListener("abort", abort);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (this.generation !== generation) {
|
|
325
|
+
throw new Error(cancellationMessage);
|
|
326
|
+
}
|
|
327
|
+
return finish;
|
|
299
328
|
} catch (error) {
|
|
300
|
-
previous.finally(
|
|
329
|
+
previous.finally(finish);
|
|
301
330
|
throw error;
|
|
302
331
|
} finally {
|
|
303
|
-
|
|
304
|
-
signal.removeEventListener("abort", abort);
|
|
332
|
+
dropResetWaiter();
|
|
305
333
|
}
|
|
306
|
-
return release;
|
|
307
334
|
}
|
|
308
335
|
reset() {
|
|
336
|
+
this.generation += 1;
|
|
309
337
|
this.tail = Promise.resolve();
|
|
338
|
+
const waiters = this.resetWaiters.splice(0);
|
|
339
|
+
for (const wake of waiters)
|
|
340
|
+
wake();
|
|
310
341
|
}
|
|
311
342
|
}
|
|
312
343
|
|
|
@@ -332,38 +363,7 @@ class PermissionPromptCoordinator {
|
|
|
332
363
|
|
|
333
364
|
// config.ts
|
|
334
365
|
var MAX_SUBAGENTS = 16;
|
|
335
|
-
var
|
|
336
|
-
showGlobalAgents: true,
|
|
337
|
-
showLocalAgents: true
|
|
338
|
-
};
|
|
339
|
-
var LANDSTRIP_KEYS = new Set(["maxSubagents", "agent", "permission", "opencode"]);
|
|
340
|
-
var OPENCODE_KEYS = new Set(["showGlobalAgents", "showLocalAgents"]);
|
|
341
|
-
function readBooleanField(section, key, path) {
|
|
342
|
-
if (!(key in section))
|
|
343
|
-
return;
|
|
344
|
-
const value = section[key];
|
|
345
|
-
if (typeof value === "boolean")
|
|
346
|
-
return value;
|
|
347
|
-
throw new Error(`${path}.${key} must be a boolean`);
|
|
348
|
-
}
|
|
349
|
-
function normalizeOpenCode(value, path) {
|
|
350
|
-
if (value === undefined)
|
|
351
|
-
return {};
|
|
352
|
-
if (!isRecord2(value))
|
|
353
|
-
throw new Error(`${path} must be a JSON object`);
|
|
354
|
-
for (const key of Object.keys(value)) {
|
|
355
|
-
if (!OPENCODE_KEYS.has(key))
|
|
356
|
-
throw new Error(`${path}: unknown field ${key}`);
|
|
357
|
-
}
|
|
358
|
-
const result = {};
|
|
359
|
-
const showGlobalAgents = readBooleanField(value, "showGlobalAgents", path);
|
|
360
|
-
if (showGlobalAgents !== undefined)
|
|
361
|
-
result.showGlobalAgents = showGlobalAgents;
|
|
362
|
-
const showLocalAgents = readBooleanField(value, "showLocalAgents", path);
|
|
363
|
-
if (showLocalAgents !== undefined)
|
|
364
|
-
result.showLocalAgents = showLocalAgents;
|
|
365
|
-
return result;
|
|
366
|
-
}
|
|
366
|
+
var LANDSTRIP_KEYS = new Set(["maxSubagents", "agent", "permission"]);
|
|
367
367
|
function readJsonObject(path) {
|
|
368
368
|
let value;
|
|
369
369
|
try {
|
|
@@ -417,7 +417,6 @@ function readLandstripSettings(path) {
|
|
|
417
417
|
}
|
|
418
418
|
if ("permission" in settings.landstrip)
|
|
419
419
|
config.permission = settings.landstrip.permission;
|
|
420
|
-
config.opencode = normalizeOpenCode(settings.landstrip.opencode, `${path}: landstrip.opencode`);
|
|
421
420
|
expandAgentPromptReferences(config, path);
|
|
422
421
|
return config;
|
|
423
422
|
}
|
|
@@ -557,8 +556,7 @@ function loadLandstripConfig(cwd, includeProject = true, agentDir = getAgentDir(
|
|
|
557
556
|
}
|
|
558
557
|
if (!isRecord2(config.agent))
|
|
559
558
|
throw new Error("landstrip.agent must be an object");
|
|
560
|
-
|
|
561
|
-
return { ...config, maxSubagents, agent: config.agent, opencode, agentSources };
|
|
559
|
+
return { ...config, maxSubagents, agent: config.agent, agentSources };
|
|
562
560
|
}
|
|
563
561
|
function loadMaxSubagentsSettings(cwd, includeProject = true, agentDir = getAgentDir()) {
|
|
564
562
|
const global = loadLandstripConfig(cwd, false, agentDir).maxSubagents;
|
|
@@ -574,17 +572,8 @@ function loadMaxSubagentsSettings(cwd, includeProject = true, agentDir = getAgen
|
|
|
574
572
|
|
|
575
573
|
// opencode-agents.ts
|
|
576
574
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
577
|
-
import {
|
|
578
|
-
import { dirname as dirname2, extname, join as join3, relative, sep } from "node:path";
|
|
575
|
+
import { extname, join as join3, relative, sep } from "node:path";
|
|
579
576
|
import { parse as parseYaml } from "yaml";
|
|
580
|
-
import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
|
|
581
|
-
var AGENT_DIR_PREFIXES = ["agent/", "agents/"];
|
|
582
|
-
function openCodeGlobalConfigDir() {
|
|
583
|
-
if (process.env.OPENCODE_CONFIG_DIR)
|
|
584
|
-
return process.env.OPENCODE_CONFIG_DIR;
|
|
585
|
-
const xdg = process.env.XDG_CONFIG_HOME;
|
|
586
|
-
return join3(xdg && xdg.length > 0 ? xdg : join3(homedir2(), ".config"), "opencode");
|
|
587
|
-
}
|
|
588
577
|
function listMarkdownFiles(root) {
|
|
589
578
|
if (!existsSync2(root))
|
|
590
579
|
return [];
|
|
@@ -615,13 +604,6 @@ function listMarkdownFiles(root) {
|
|
|
615
604
|
}
|
|
616
605
|
function agentNameFromPath(dir, filePath) {
|
|
617
606
|
const relativePath = relative(dir, filePath).split(sep).join("/");
|
|
618
|
-
for (const prefix of AGENT_DIR_PREFIXES) {
|
|
619
|
-
if (relativePath.startsWith(prefix)) {
|
|
620
|
-
const candidate = relativePath.slice(prefix.length);
|
|
621
|
-
const extension2 = extname(candidate);
|
|
622
|
-
return extension2.length > 0 ? candidate.slice(0, -extension2.length) : candidate;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
607
|
const extension = extname(relativePath);
|
|
626
608
|
return extension.length > 0 ? relativePath.slice(0, -extension.length) : relativePath;
|
|
627
609
|
}
|
|
@@ -653,7 +635,7 @@ function convertToolsToPermission(tools) {
|
|
|
653
635
|
}
|
|
654
636
|
return permission;
|
|
655
637
|
}
|
|
656
|
-
function
|
|
638
|
+
function normalizeMarkdownAgentRaw(data, prompt) {
|
|
657
639
|
const known = new Set([
|
|
658
640
|
"name",
|
|
659
641
|
"model",
|
|
@@ -713,98 +695,27 @@ function normalizeOpenCodeAgentRaw(data, prompt) {
|
|
|
713
695
|
}
|
|
714
696
|
function parseMarkdownAgentRaw(content) {
|
|
715
697
|
const { data, body } = parseFrontmatter(content);
|
|
716
|
-
return
|
|
717
|
-
}
|
|
718
|
-
function parseOpenCodeConfig(path) {
|
|
719
|
-
const errors = [];
|
|
720
|
-
const value = parseJsonc(readFileSync3(path, "utf8"), errors, {
|
|
721
|
-
allowTrailingComma: true
|
|
722
|
-
});
|
|
723
|
-
if (errors.length > 0) {
|
|
724
|
-
const error = errors[0];
|
|
725
|
-
throw new Error(`${printParseErrorCode(error.error)} at offset ${error.offset}`);
|
|
726
|
-
}
|
|
727
|
-
if (!isRecord2(value))
|
|
728
|
-
throw new Error("config must contain a JSON object");
|
|
729
|
-
return value;
|
|
698
|
+
return normalizeMarkdownAgentRaw(data, body.trim());
|
|
730
699
|
}
|
|
731
|
-
function
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const
|
|
736
|
-
if (config.agent === undefined)
|
|
737
|
-
return;
|
|
738
|
-
if (!isRecord2(config.agent))
|
|
739
|
-
throw new Error("agent must be a JSON object");
|
|
740
|
-
for (const [name, value] of Object.entries(config.agent)) {
|
|
700
|
+
function loadPiMarkdownAgents(options) {
|
|
701
|
+
const agents = new Map;
|
|
702
|
+
const diagnostics = [];
|
|
703
|
+
for (const directory of options.directories) {
|
|
704
|
+
for (const filePath of listMarkdownFiles(directory.path)) {
|
|
741
705
|
try {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
706
|
+
const content = readFileSync3(filePath, "utf8");
|
|
707
|
+
const raw = parseMarkdownAgentRaw(content);
|
|
708
|
+
const name = agentNameFromPath(directory.path, filePath);
|
|
709
|
+
if (!name) {
|
|
710
|
+
diagnostics.push(`${filePath}: agent name is empty`);
|
|
711
|
+
continue;
|
|
748
712
|
}
|
|
749
|
-
|
|
750
|
-
const raw = normalizeOpenCodeAgentRaw(value, prompt);
|
|
751
|
-
agents.set(name, { name, source, path, format: "json", raw });
|
|
713
|
+
agents.set(name, { name, source: directory.source, path: filePath, raw });
|
|
752
714
|
} catch (error) {
|
|
753
|
-
diagnostics.push(`${
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
} catch (error) {
|
|
757
|
-
diagnostics.push(`${path}: ${formatError(error)}`);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
function loadConfigAgentsFromDir(dir, source, agents, diagnostics) {
|
|
761
|
-
for (const file of ["opencode.json", "opencode.jsonc"]) {
|
|
762
|
-
loadAgentsFromConfigFile(join3(dir, file), source, agents, diagnostics);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
function loadAgentsFromRoot(root, nameRoot, source, agents, diagnostics) {
|
|
766
|
-
for (const filePath of listMarkdownFiles(root)) {
|
|
767
|
-
try {
|
|
768
|
-
const content = readFileSync3(filePath, "utf8");
|
|
769
|
-
const raw = parseMarkdownAgentRaw(content);
|
|
770
|
-
const name = agentNameFromPath(nameRoot, filePath);
|
|
771
|
-
if (!name) {
|
|
772
|
-
diagnostics.push(`${filePath}: agent name is empty`);
|
|
773
|
-
continue;
|
|
715
|
+
diagnostics.push(`${filePath}: ${formatError(error)}`);
|
|
774
716
|
}
|
|
775
|
-
agents.set(name, { name, source, path: filePath, format: "markdown", raw });
|
|
776
|
-
} catch (error) {
|
|
777
|
-
diagnostics.push(`${filePath}: ${formatError(error)}`);
|
|
778
717
|
}
|
|
779
718
|
}
|
|
780
|
-
}
|
|
781
|
-
function loadAgentsFromDir(dir, source, agents, diagnostics) {
|
|
782
|
-
for (const subdir of ["agent", "agents"]) {
|
|
783
|
-
loadAgentsFromRoot(join3(dir, subdir), dir, source, agents, diagnostics);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
function loadPiMarkdownAgents(options) {
|
|
787
|
-
const agents = new Map;
|
|
788
|
-
const diagnostics = [];
|
|
789
|
-
for (const directory of options.directories) {
|
|
790
|
-
loadAgentsFromRoot(directory.path, directory.path, directory.source, agents, diagnostics);
|
|
791
|
-
}
|
|
792
|
-
return { agents, diagnostics };
|
|
793
|
-
}
|
|
794
|
-
function loadOpenCodeAgents(options) {
|
|
795
|
-
const agents = new Map;
|
|
796
|
-
const diagnostics = [];
|
|
797
|
-
if (options.includeGlobal) {
|
|
798
|
-
const globalDir = options.globalConfigDir ?? openCodeGlobalConfigDir();
|
|
799
|
-
loadConfigAgentsFromDir(globalDir, "global", agents, diagnostics);
|
|
800
|
-
loadAgentsFromDir(globalDir, "global", agents, diagnostics);
|
|
801
|
-
}
|
|
802
|
-
if (options.includeProject) {
|
|
803
|
-
loadConfigAgentsFromDir(options.cwd, "local", agents, diagnostics);
|
|
804
|
-
const projectDir = join3(options.cwd, ".opencode");
|
|
805
|
-
loadConfigAgentsFromDir(projectDir, "local", agents, diagnostics);
|
|
806
|
-
loadAgentsFromDir(projectDir, "local", agents, diagnostics);
|
|
807
|
-
}
|
|
808
719
|
return { agents, diagnostics };
|
|
809
720
|
}
|
|
810
721
|
|
|
@@ -919,38 +830,16 @@ function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = tru
|
|
|
919
830
|
let configuredAgents = {};
|
|
920
831
|
let configuredPermission;
|
|
921
832
|
let agentSources = new Map;
|
|
922
|
-
let showGlobalAgents = false;
|
|
923
|
-
let showLocalAgents = false;
|
|
924
833
|
try {
|
|
925
834
|
const config = loadLandstripConfig(cwd, includeProject, piAgentDir);
|
|
926
835
|
maxSubagents = config.maxSubagents;
|
|
927
836
|
configuredAgents = config.agent;
|
|
928
837
|
configuredPermission = config.permission;
|
|
929
838
|
agentSources = new Map(config.agentSources);
|
|
930
|
-
showGlobalAgents = config.opencode.showGlobalAgents;
|
|
931
|
-
showLocalAgents = config.opencode.showLocalAgents;
|
|
932
839
|
} catch (error) {
|
|
933
840
|
diagnostics.push(formatError(error));
|
|
934
841
|
}
|
|
935
|
-
const openCode = loadOpenCodeAgents({
|
|
936
|
-
cwd,
|
|
937
|
-
includeGlobal: showGlobalAgents,
|
|
938
|
-
includeProject: showLocalAgents && includeProject
|
|
939
|
-
});
|
|
940
|
-
diagnostics.push(...openCode.diagnostics);
|
|
941
842
|
const normalized = new Map;
|
|
942
|
-
for (const imported of openCode.agents.values()) {
|
|
943
|
-
try {
|
|
944
|
-
const kind = imported.format === "markdown" ? "opencode-markdown" : "opencode-json";
|
|
945
|
-
normalized.set(imported.name, normalizeAgent(imported.name, imported.raw, imported.source, {
|
|
946
|
-
kind,
|
|
947
|
-
source: imported.source,
|
|
948
|
-
path: imported.path
|
|
949
|
-
}));
|
|
950
|
-
} catch (error) {
|
|
951
|
-
diagnostics.push(`${imported.path}: ${formatError(error)}`);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
843
|
const piMarkdown = loadPiMarkdownAgents({
|
|
955
844
|
directories: [
|
|
956
845
|
{ path: join4(piAgentDir, "agents"), source: "global" },
|
|
@@ -1045,13 +934,13 @@ function agentSupportsMode(agent, mode) {
|
|
|
1045
934
|
|
|
1046
935
|
// agent-files.ts
|
|
1047
936
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1048
|
-
import {
|
|
937
|
+
import { dirname as dirname2, join as join5, relative as relative2, sep as sep2 } from "node:path";
|
|
1049
938
|
import { getAgentDir as getAgentDir3, withFileMutationQueue as withFileMutationQueue2 } from "@earendil-works/pi-coding-agent";
|
|
1050
939
|
import {
|
|
1051
940
|
applyEdits,
|
|
1052
941
|
modify,
|
|
1053
|
-
parse as
|
|
1054
|
-
printParseErrorCode
|
|
942
|
+
parse as parseJsonc,
|
|
943
|
+
printParseErrorCode
|
|
1055
944
|
} from "jsonc-parser";
|
|
1056
945
|
var JSON_FORMAT = {
|
|
1057
946
|
formattingOptions: { insertSpaces: true, tabSize: 2, eol: `
|
|
@@ -1059,10 +948,10 @@ var JSON_FORMAT = {
|
|
|
1059
948
|
};
|
|
1060
949
|
function parseJsonDocument(content, label) {
|
|
1061
950
|
const errors = [];
|
|
1062
|
-
const value =
|
|
951
|
+
const value = parseJsonc(content, errors, { allowTrailingComma: true });
|
|
1063
952
|
if (errors.length > 0) {
|
|
1064
953
|
const error = errors[0];
|
|
1065
|
-
throw new Error(`${label}: ${
|
|
954
|
+
throw new Error(`${label}: ${printParseErrorCode(error.error)} at offset ${error.offset}`);
|
|
1066
955
|
}
|
|
1067
956
|
if (!isRecord2(value))
|
|
1068
957
|
throw new Error(`${label} must contain a JSON object`);
|
|
@@ -1077,38 +966,22 @@ function valueAtPath(root, path) {
|
|
|
1077
966
|
}
|
|
1078
967
|
return value;
|
|
1079
968
|
}
|
|
1080
|
-
function agentJsonPath(
|
|
1081
|
-
return
|
|
969
|
+
function agentJsonPath(name) {
|
|
970
|
+
return ["landstrip", "agent", name];
|
|
1082
971
|
}
|
|
1083
|
-
function
|
|
1084
|
-
return
|
|
1085
|
-
}
|
|
1086
|
-
function editorSnippet(kind, name, raw) {
|
|
1087
|
-
const value = kind === "landstrip" ? { landstrip: { agent: { [name]: raw } } } : { agent: { [name]: raw } };
|
|
1088
|
-
return `${JSON.stringify(value, null, 2)}
|
|
972
|
+
function editorSnippet(name, raw) {
|
|
973
|
+
return `${JSON.stringify({ landstrip: { agent: { [name]: raw } } }, null, 2)}
|
|
1089
974
|
`;
|
|
1090
975
|
}
|
|
1091
|
-
function parseEditorSnippet(content,
|
|
976
|
+
function parseEditorSnippet(content, name) {
|
|
1092
977
|
const root = parseJsonDocument(content, "Edited agent");
|
|
1093
|
-
const parent =
|
|
1094
|
-
const container = valueAtPath(root,
|
|
1095
|
-
|
|
1096
|
-
if (Object.keys(root).length !== 1 || root[rootKey] === undefined || !isRecord2(parent) || Object.keys(parent).length !== 1 || parent.agent === undefined || !isRecord2(container) || Object.keys(container).length !== 1 || !isRecord2(container[name])) {
|
|
978
|
+
const parent = valueAtPath(root, ["landstrip"]);
|
|
979
|
+
const container = valueAtPath(root, ["landstrip", "agent"]);
|
|
980
|
+
if (Object.keys(root).length !== 1 || root.landstrip === undefined || !isRecord2(parent) || Object.keys(parent).length !== 1 || parent.agent === undefined || !isRecord2(container) || Object.keys(container).length !== 1 || !isRecord2(container[name])) {
|
|
1097
981
|
throw new Error(`Edited JSON must contain only agent ${name}`);
|
|
1098
982
|
}
|
|
1099
983
|
return container[name];
|
|
1100
984
|
}
|
|
1101
|
-
function validateEditedAgent(kind, name, raw) {
|
|
1102
|
-
if (kind === "landstrip") {
|
|
1103
|
-
validateAgentRaw(name, raw);
|
|
1104
|
-
return;
|
|
1105
|
-
}
|
|
1106
|
-
const prompt = raw.prompt;
|
|
1107
|
-
if (prompt !== undefined && typeof prompt !== "string") {
|
|
1108
|
-
throw new Error(`agent ${name} prompt must be a string`);
|
|
1109
|
-
}
|
|
1110
|
-
validateAgentRaw(name, normalizeOpenCodeAgentRaw(raw, typeof prompt === "string" ? prompt : ""));
|
|
1111
|
-
}
|
|
1112
985
|
function ensureFinalNewline(content) {
|
|
1113
986
|
return content.endsWith(`
|
|
1114
987
|
`) ? content : `${content}
|
|
@@ -1120,7 +993,7 @@ async function writeJsonNode(path, nodePath, value) {
|
|
|
1120
993
|
`;
|
|
1121
994
|
parseJsonDocument(content, path);
|
|
1122
995
|
const edited = applyEdits(content, modify(content, nodePath, value, JSON_FORMAT));
|
|
1123
|
-
mkdirSync2(
|
|
996
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
1124
997
|
writeFileSync2(path, ensureFinalNewline(edited), { mode: 384 });
|
|
1125
998
|
});
|
|
1126
999
|
}
|
|
@@ -1146,11 +1019,11 @@ async function deleteJsonNode(path, nodePath, containerPaths) {
|
|
|
1146
1019
|
});
|
|
1147
1020
|
return deleted;
|
|
1148
1021
|
}
|
|
1149
|
-
function readAgentNode(path,
|
|
1022
|
+
function readAgentNode(path, name) {
|
|
1150
1023
|
if (!existsSync3(path))
|
|
1151
1024
|
return;
|
|
1152
1025
|
const root = parseJsonDocument(readFileSync4(path, "utf8"), path);
|
|
1153
|
-
const value = valueAtPath(root, agentJsonPath(
|
|
1026
|
+
const value = valueAtPath(root, agentJsonPath(name));
|
|
1154
1027
|
return isRecord2(value) ? { ...value } : undefined;
|
|
1155
1028
|
}
|
|
1156
1029
|
function permissionConfig(agent) {
|
|
@@ -1189,7 +1062,7 @@ function relocateGlobalPrompt(raw, agent) {
|
|
|
1189
1062
|
if (agent.origin?.source !== "global" || !agent.origin.path || typeof raw.prompt !== "string") {
|
|
1190
1063
|
return raw;
|
|
1191
1064
|
}
|
|
1192
|
-
return { ...raw, prompt: expandFileReferences(raw.prompt,
|
|
1065
|
+
return { ...raw, prompt: expandFileReferences(raw.prompt, dirname2(agent.origin.path)) };
|
|
1193
1066
|
}
|
|
1194
1067
|
function safeRelative(root, path) {
|
|
1195
1068
|
const value = relative2(root, path);
|
|
@@ -1203,19 +1076,13 @@ function projectMarkdownPath(cwd, agentDir, agent) {
|
|
|
1203
1076
|
const suffix = safeRelative(join5(agentDir, "agents"), origin.path) ?? `${agent.name}.md`;
|
|
1204
1077
|
return join5(cwd, ".pi", "agents", suffix);
|
|
1205
1078
|
}
|
|
1206
|
-
if (origin.kind === "opencode-markdown" && origin.path) {
|
|
1207
|
-
return join5(cwd, ".opencode", "agents", `${agent.name}.md`);
|
|
1208
|
-
}
|
|
1209
1079
|
throw new Error(`Agent ${agent.name} has no Markdown source`);
|
|
1210
1080
|
}
|
|
1211
|
-
function openCodeProjectPath(cwd, sourcePath) {
|
|
1212
|
-
return join5(cwd, ".opencode", basename(sourcePath));
|
|
1213
|
-
}
|
|
1214
1081
|
function projectSettingsPath(cwd, agentDir) {
|
|
1215
1082
|
return getPiConfigPaths(cwd, "settings.json", agentDir).projectPath;
|
|
1216
1083
|
}
|
|
1217
1084
|
function projectSettingsHasAgent(cwd, name, agentDir) {
|
|
1218
|
-
return readAgentNode(projectSettingsPath(cwd, agentDir),
|
|
1085
|
+
return readAgentNode(projectSettingsPath(cwd, agentDir), name) !== undefined;
|
|
1219
1086
|
}
|
|
1220
1087
|
function canDeleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
|
|
1221
1088
|
try {
|
|
@@ -1225,21 +1092,16 @@ function canDeleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
|
|
|
1225
1092
|
return false;
|
|
1226
1093
|
}
|
|
1227
1094
|
const origin = agent.origin;
|
|
1228
|
-
return Boolean(origin?.source === "local" && origin.path &&
|
|
1095
|
+
return Boolean(origin?.source === "local" && origin.path && origin.kind === "pi-markdown");
|
|
1229
1096
|
}
|
|
1230
1097
|
async function deleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
|
|
1231
1098
|
const settingsPath = projectSettingsPath(cwd, agentDir);
|
|
1232
|
-
const deletedOverride = await deleteJsonNode(settingsPath, agentJsonPath(
|
|
1099
|
+
const deletedOverride = await deleteJsonNode(settingsPath, agentJsonPath(agent.name), [
|
|
1100
|
+
["landstrip", "agent"],
|
|
1101
|
+
["landstrip"]
|
|
1102
|
+
]);
|
|
1233
1103
|
const origin = agent.origin;
|
|
1234
|
-
if (origin?.source !== "local" || !origin.path)
|
|
1235
|
-
return deletedOverride;
|
|
1236
|
-
if (origin.kind === "opencode-json") {
|
|
1237
|
-
const deletedSource = await deleteJsonNode(origin.path, agentJsonPath("opencode", agent.name), [
|
|
1238
|
-
["agent"]
|
|
1239
|
-
]);
|
|
1240
|
-
return deletedOverride || deletedSource;
|
|
1241
|
-
}
|
|
1242
|
-
if (origin.kind !== "pi-markdown" && origin.kind !== "opencode-markdown") {
|
|
1104
|
+
if (origin?.source !== "local" || !origin.path || origin.kind !== "pi-markdown") {
|
|
1243
1105
|
return deletedOverride;
|
|
1244
1106
|
}
|
|
1245
1107
|
if (!existsSync3(origin.path))
|
|
@@ -1249,44 +1111,31 @@ async function deleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
|
|
|
1249
1111
|
}
|
|
1250
1112
|
function prepareProjectAgentEditor(cwd, agent, agentDir = getAgentDir3()) {
|
|
1251
1113
|
const origin = agent.origin;
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
const useMarkdown = origin.kind === "pi-markdown" || origin.source === "local" || openCodeProjectEnabled;
|
|
1255
|
-
const target2 = useMarkdown ? projectMarkdownPath(cwd, agentDir, agent) : projectSettingsPath(cwd, agentDir);
|
|
1256
|
-
const projectOverride = useMarkdown ? undefined : readAgentNode(target2, "landstrip", agent.name);
|
|
1257
|
-
const disabledOverride = projectOverride && Object.keys(projectOverride).length === 1 && typeof projectOverride.disable === "boolean" ? projectOverride.disable : undefined;
|
|
1114
|
+
if (origin?.path && origin.kind === "pi-markdown") {
|
|
1115
|
+
const target2 = projectMarkdownPath(cwd, agentDir, agent);
|
|
1258
1116
|
return {
|
|
1259
1117
|
title: `Edit @${agent.name}`,
|
|
1260
1118
|
content: readFileSync4(origin.path, "utf8"),
|
|
1261
1119
|
async save(content) {
|
|
1262
|
-
|
|
1263
|
-
const raw2 = !useMarkdown && edited.disable === undefined && disabledOverride !== undefined ? { ...edited, disable: disabledOverride } : edited;
|
|
1264
|
-
validateAgentRaw(agent.name, raw2);
|
|
1265
|
-
if (!useMarkdown) {
|
|
1266
|
-
await writeJsonNode(target2, agentJsonPath("landstrip", agent.name), raw2);
|
|
1267
|
-
return;
|
|
1268
|
-
}
|
|
1120
|
+
validateAgentRaw(agent.name, parseMarkdownAgentRaw(content));
|
|
1269
1121
|
await withFileMutationQueue2(target2, async () => {
|
|
1270
|
-
mkdirSync2(
|
|
1122
|
+
mkdirSync2(dirname2(target2), { recursive: true });
|
|
1271
1123
|
writeFileSync2(target2, ensureFinalNewline(content), { mode: 384 });
|
|
1272
1124
|
});
|
|
1273
1125
|
}
|
|
1274
1126
|
};
|
|
1275
1127
|
}
|
|
1276
|
-
const useOpenCode = origin?.kind === "opencode-json" && origin.path !== undefined && (origin.source === "local" || openCodeProjectEnabled);
|
|
1277
|
-
const kind = useOpenCode ? "opencode" : "landstrip";
|
|
1278
1128
|
const sourcePath = origin?.path;
|
|
1279
|
-
const
|
|
1280
|
-
const
|
|
1281
|
-
const
|
|
1282
|
-
const target = kind === "opencode" && sourcePath ? origin?.source === "local" ? sourcePath : openCodeProjectPath(cwd, sourcePath) : projectSettingsPath(cwd, agentDir);
|
|
1129
|
+
const sourceRaw = origin?.kind === "settings" && sourcePath ? readAgentNode(sourcePath, agent.name) : undefined;
|
|
1130
|
+
const raw = relocateGlobalPrompt(sourceRaw ? sourceRaw : agent.raw ? { ...agent.raw } : normalizedAgentRaw(agent), agent);
|
|
1131
|
+
const target = projectSettingsPath(cwd, agentDir);
|
|
1283
1132
|
return {
|
|
1284
1133
|
title: `Edit @${agent.name}`,
|
|
1285
|
-
content: editorSnippet(
|
|
1134
|
+
content: editorSnippet(agent.name, raw),
|
|
1286
1135
|
async save(content) {
|
|
1287
|
-
const edited = parseEditorSnippet(content,
|
|
1288
|
-
|
|
1289
|
-
await writeJsonNode(target, agentJsonPath(
|
|
1136
|
+
const edited = parseEditorSnippet(content, agent.name);
|
|
1137
|
+
validateAgentRaw(agent.name, edited);
|
|
1138
|
+
await writeJsonNode(target, agentJsonPath(agent.name), edited);
|
|
1290
1139
|
}
|
|
1291
1140
|
};
|
|
1292
1141
|
}
|
|
@@ -1744,7 +1593,7 @@ var PRIMARY_AGENT_ENTRY = "landstrip.primary-agent";
|
|
|
1744
1593
|
var WORKER_ENV = "PI_LANDSTRIP_WORKER";
|
|
1745
1594
|
var CONTROL_TITLE = "pi-landstrip:control:v1";
|
|
1746
1595
|
var MAX_DEPTH = 3;
|
|
1747
|
-
var packageDir =
|
|
1596
|
+
var packageDir = dirname3(fileURLToPath(import.meta.url));
|
|
1748
1597
|
var MAX_TASK_OUTPUT_BYTES = 64 * 1024;
|
|
1749
1598
|
var INSPECTOR_BODY_LINES = 16;
|
|
1750
1599
|
var PI_THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
@@ -1790,7 +1639,7 @@ function resolvePiPackage() {
|
|
|
1790
1639
|
piPackageResolved = true;
|
|
1791
1640
|
try {
|
|
1792
1641
|
const entry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
|
|
1793
|
-
let dir2 =
|
|
1642
|
+
let dir2 = dirname3(entry);
|
|
1794
1643
|
for (;; ) {
|
|
1795
1644
|
const pkgPath = join6(dir2, "package.json");
|
|
1796
1645
|
if (existsSync4(pkgPath)) {
|
|
@@ -1798,7 +1647,7 @@ function resolvePiPackage() {
|
|
|
1798
1647
|
if (pkg)
|
|
1799
1648
|
return pkg;
|
|
1800
1649
|
}
|
|
1801
|
-
const parent =
|
|
1650
|
+
const parent = dirname3(dir2);
|
|
1802
1651
|
if (parent === dir2)
|
|
1803
1652
|
break;
|
|
1804
1653
|
dir2 = parent;
|
|
@@ -1812,7 +1661,7 @@ function resolvePiPackage() {
|
|
|
1812
1661
|
if (pkg)
|
|
1813
1662
|
return pkg;
|
|
1814
1663
|
}
|
|
1815
|
-
const parent =
|
|
1664
|
+
const parent = dirname3(dir);
|
|
1816
1665
|
if (parent === dir)
|
|
1817
1666
|
return;
|
|
1818
1667
|
dir = parent;
|
|
@@ -1830,7 +1679,7 @@ function readPiPackage(pkgPath) {
|
|
|
1830
1679
|
const binFile = typeof value.bin === "string" ? value.bin : isRecord2(value.bin) && typeof value.bin.pi === "string" ? value.bin.pi : undefined;
|
|
1831
1680
|
if (!binFile)
|
|
1832
1681
|
return;
|
|
1833
|
-
const cliEntry = join6(
|
|
1682
|
+
const cliEntry = join6(dirname3(pkgPath), binFile);
|
|
1834
1683
|
if (!existsSync4(cliEntry))
|
|
1835
1684
|
return;
|
|
1836
1685
|
cachedPiPackage = { cliEntry, version: parts.slice(0, 3) };
|
|
@@ -2085,14 +1934,14 @@ class Semaphore {
|
|
|
2085
1934
|
if (this.active >= this.limit)
|
|
2086
1935
|
return;
|
|
2087
1936
|
this.active += 1;
|
|
2088
|
-
return
|
|
1937
|
+
return this.releaseOnce();
|
|
2089
1938
|
}
|
|
2090
1939
|
async acquire(signal) {
|
|
2091
1940
|
if (signal?.aborted)
|
|
2092
1941
|
throw new Error("Task cancelled");
|
|
2093
1942
|
if (this.active < this.limit) {
|
|
2094
1943
|
this.active += 1;
|
|
2095
|
-
return
|
|
1944
|
+
return this.releaseOnce();
|
|
2096
1945
|
}
|
|
2097
1946
|
await new Promise((resolve3, reject) => {
|
|
2098
1947
|
const abort = () => {
|
|
@@ -2109,11 +1958,17 @@ class Semaphore {
|
|
|
2109
1958
|
this.waiters.push(start);
|
|
2110
1959
|
signal?.addEventListener("abort", abort, { once: true });
|
|
2111
1960
|
});
|
|
2112
|
-
return
|
|
1961
|
+
return this.releaseOnce();
|
|
2113
1962
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
1963
|
+
releaseOnce() {
|
|
1964
|
+
let released = false;
|
|
1965
|
+
return () => {
|
|
1966
|
+
if (released)
|
|
1967
|
+
return;
|
|
1968
|
+
released = true;
|
|
1969
|
+
this.active -= 1;
|
|
1970
|
+
this.waiters.shift()?.();
|
|
1971
|
+
};
|
|
2117
1972
|
}
|
|
2118
1973
|
}
|
|
2119
1974
|
|
|
@@ -2261,15 +2116,15 @@ function canonicalPermissionPath(path, seen = new Set) {
|
|
|
2261
2116
|
if (seen.has(existing))
|
|
2262
2117
|
return path;
|
|
2263
2118
|
seen.add(existing);
|
|
2264
|
-
const target = resolve2(
|
|
2119
|
+
const target = resolve2(dirname3(existing), readlinkSync(existing), ...missing);
|
|
2265
2120
|
return canonicalPermissionPath(target, seen);
|
|
2266
2121
|
}
|
|
2267
2122
|
break;
|
|
2268
2123
|
} catch {
|
|
2269
|
-
const parent =
|
|
2124
|
+
const parent = dirname3(existing);
|
|
2270
2125
|
if (parent === existing)
|
|
2271
2126
|
return path;
|
|
2272
|
-
missing.unshift(
|
|
2127
|
+
missing.unshift(basename(existing));
|
|
2273
2128
|
existing = parent;
|
|
2274
2129
|
}
|
|
2275
2130
|
}
|
|
@@ -2708,7 +2563,9 @@ ${this.primaryAgent.prompt}` };
|
|
|
2708
2563
|
const dirty = dirtySettings.has(index) ? theme.fg("warning", " *") : "";
|
|
2709
2564
|
lines2.push(`${cursor} ${value} ${label}${dirty}`);
|
|
2710
2565
|
}
|
|
2711
|
-
|
|
2566
|
+
if (selectedSetting === 1) {
|
|
2567
|
+
lines2.push("", theme.fg("dim", rows[1]?.inherited ? "Controls OS sandboxing. Use /sandbox to inspect its policy. Using the Global value." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
|
|
2568
|
+
}
|
|
2712
2569
|
if (saving)
|
|
2713
2570
|
lines2.push("", theme.fg("dim", "Saving…"));
|
|
2714
2571
|
return box(lines2);
|
|
@@ -3564,11 +3421,11 @@ ${catalog.diagnostics.join(`
|
|
|
3564
3421
|
return;
|
|
3565
3422
|
});
|
|
3566
3423
|
try {
|
|
3567
|
-
const sessionWritePath = task.sessionDir ?? (task.sessionFile ?
|
|
3424
|
+
const sessionWritePath = task.sessionDir ?? (task.sessionFile ? dirname3(task.sessionFile) : undefined);
|
|
3568
3425
|
if (!sessionWritePath)
|
|
3569
3426
|
throw new Error("Subagent task has no session directory or file");
|
|
3570
3427
|
const cliEntry = invocation.args[0] ?? invocation.command;
|
|
3571
|
-
const cliRoot = dependencyRoot(cliEntry) ??
|
|
3428
|
+
const cliRoot = dependencyRoot(cliEntry) ?? dirname3(dirname3(cliEntry));
|
|
3572
3429
|
const extensionRoot = dependencyRoot(packageDir);
|
|
3573
3430
|
launch = await this.integration.prepareRpcWorker({
|
|
3574
3431
|
command: invocation.command,
|
|
@@ -3588,7 +3445,7 @@ ${catalog.diagnostics.join(`
|
|
|
3588
3445
|
...new Set([
|
|
3589
3446
|
ctx.cwd,
|
|
3590
3447
|
...agentBootstrapPaths(agentDir),
|
|
3591
|
-
join6(
|
|
3448
|
+
join6(homedir2(), ".agents", "skills"),
|
|
3592
3449
|
packageDir,
|
|
3593
3450
|
join6(packageDir, "node_modules"),
|
|
3594
3451
|
invocation.command,
|
|
@@ -3596,7 +3453,7 @@ ${catalog.diagnostics.join(`
|
|
|
3596
3453
|
extensionRoot,
|
|
3597
3454
|
...workerExtensions.flatMap((extension) => [
|
|
3598
3455
|
extension.entry,
|
|
3599
|
-
|
|
3456
|
+
dirname3(extension.entry),
|
|
3600
3457
|
dependencyRoot(extension.entry)
|
|
3601
3458
|
]),
|
|
3602
3459
|
task.sessionDir,
|
|
@@ -3718,7 +3575,9 @@ ${catalog.diagnostics.join(`
|
|
|
3718
3575
|
});
|
|
3719
3576
|
} finally {
|
|
3720
3577
|
if (handedOff)
|
|
3721
|
-
await this.restoreLease(task.id
|
|
3578
|
+
await this.restoreLease(task.id).catch(() => {
|
|
3579
|
+
return;
|
|
3580
|
+
});
|
|
3722
3581
|
}
|
|
3723
3582
|
response = {
|
|
3724
3583
|
ok: true,
|
|
@@ -3808,7 +3667,7 @@ ${renderTaskResult(task.id, "error", message)}`);
|
|
|
3808
3667
|
storeTaskText(task, value, kind) {
|
|
3809
3668
|
if (Buffer.byteLength(value) <= MAX_TASK_OUTPUT_BYTES)
|
|
3810
3669
|
return value;
|
|
3811
|
-
const directory = task.sessionDir ?? (task.sessionFile ?
|
|
3670
|
+
const directory = task.sessionDir ?? (task.sessionFile ? dirname3(task.sessionFile) : undefined);
|
|
3812
3671
|
if (!directory)
|
|
3813
3672
|
throw new Error(`Task ${task.id} has no artifact directory`);
|
|
3814
3673
|
mkdirSync3(directory, { recursive: true });
|
|
@@ -4015,11 +3874,11 @@ ${renderTaskResult(task.id, failed ? "error" : "completed", value)}`;
|
|
|
4015
3874
|
lease.release = undefined;
|
|
4016
3875
|
return true;
|
|
4017
3876
|
}
|
|
4018
|
-
async restoreLease(taskId
|
|
3877
|
+
async restoreLease(taskId) {
|
|
4019
3878
|
const lease = this.leases.get(taskId);
|
|
4020
3879
|
if (!lease || lease.release)
|
|
4021
3880
|
return;
|
|
4022
|
-
const release = await this.semaphore.acquire(
|
|
3881
|
+
const release = await this.semaphore.acquire();
|
|
4023
3882
|
const current = this.leases.get(taskId);
|
|
4024
3883
|
if (!current || current.release) {
|
|
4025
3884
|
release();
|
|
@@ -4070,7 +3929,7 @@ var PROXY_ENVIRONMENT_VARIABLES = [
|
|
|
4070
3929
|
"https_proxy",
|
|
4071
3930
|
"all_proxy"
|
|
4072
3931
|
];
|
|
4073
|
-
var packageDir2 =
|
|
3932
|
+
var packageDir2 = dirname4(fileURLToPath2(import.meta.url));
|
|
4074
3933
|
var PERMISSION_OPTIONS = [
|
|
4075
3934
|
{ label: "Allow once", action: "once" },
|
|
4076
3935
|
{ label: "Allow for this session", action: "session" },
|
|
@@ -4098,7 +3957,7 @@ function loadSandboxConfig(cwd, includeProject) {
|
|
|
4098
3957
|
const globalConfigPath = join7(getAgentDir5(), "sandbox.json");
|
|
4099
3958
|
if (!existsSync5(globalConfigPath)) {
|
|
4100
3959
|
const templatePath = join7(packageDir2, "sandbox.json");
|
|
4101
|
-
mkdirSync4(
|
|
3960
|
+
mkdirSync4(dirname4(globalConfigPath), { recursive: true });
|
|
4102
3961
|
writeFileSync4(globalConfigPath, readFileSync6(templatePath, "utf-8"), "utf-8");
|
|
4103
3962
|
}
|
|
4104
3963
|
let globalConfig = JSON.parse(readFileSync6(join7(packageDir2, "sandbox.json"), "utf-8"));
|
|
@@ -4194,12 +4053,12 @@ function readOrEmptyConfig(configPath) {
|
|
|
4194
4053
|
return {};
|
|
4195
4054
|
try {
|
|
4196
4055
|
return JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
4197
|
-
} catch {
|
|
4198
|
-
|
|
4056
|
+
} catch (error) {
|
|
4057
|
+
throw new Error(`${configPath}: ${formatError(error)}`);
|
|
4199
4058
|
}
|
|
4200
4059
|
}
|
|
4201
4060
|
function writeConfigFile(configPath, config) {
|
|
4202
|
-
mkdirSync4(
|
|
4061
|
+
mkdirSync4(dirname4(configPath), { recursive: true });
|
|
4203
4062
|
writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
|
|
4204
4063
|
`, "utf-8");
|
|
4205
4064
|
}
|
|
@@ -4295,10 +4154,10 @@ function canonicalizePath(filePath, cwd) {
|
|
|
4295
4154
|
const tail = [];
|
|
4296
4155
|
let probe = abs;
|
|
4297
4156
|
while (!existsSync5(probe)) {
|
|
4298
|
-
const parent =
|
|
4157
|
+
const parent = dirname4(probe);
|
|
4299
4158
|
if (parent === probe)
|
|
4300
4159
|
return abs;
|
|
4301
|
-
tail.unshift(
|
|
4160
|
+
tail.unshift(basename2(probe));
|
|
4302
4161
|
probe = parent;
|
|
4303
4162
|
}
|
|
4304
4163
|
try {
|
|
@@ -4336,9 +4195,9 @@ function pathUnderDirectory(filePath, dir) {
|
|
|
4336
4195
|
return normalizedFilePath.startsWith(normalizedDir + sep4);
|
|
4337
4196
|
}
|
|
4338
4197
|
function sessionScopeFor(filePath, baseDirectory) {
|
|
4339
|
-
const dir =
|
|
4198
|
+
const dir = dirname4(filePath);
|
|
4340
4199
|
const normalizedDir = normalizePathSeparators(dir);
|
|
4341
|
-
const home =
|
|
4200
|
+
const home = homedir3();
|
|
4342
4201
|
const boundaries = new Set;
|
|
4343
4202
|
if (home)
|
|
4344
4203
|
boundaries.add(home);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.13",
|
|
4
4
|
"description": "Sandboxed Bash and process-backed agents for Pi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"landstrip",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ci:test": "npm run test"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@landstrip/landstrip": "^0.18.
|
|
42
|
+
"@landstrip/landstrip": "^0.18.13",
|
|
43
43
|
"jsonc-parser": "^3.3.1",
|
|
44
44
|
"minimatch": "^10.2.5",
|
|
45
45
|
"yaml": "^2.9.0"
|