@modootoday/pmcp 0.16.1 → 0.17.1
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/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +7 -1
- package/dist/cli.js +535 -320
- package/dist/http.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +269 -107
- package/dist/stdio.js +1 -1
- package/docs/project-configuration.md +35 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/plugins/pmcp/.claude-plugin/plugin.json +1 -1
- package/plugins/pmcp/gemini-extension.json +1 -1
- package/plugins/pmcp/plugin.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1940,45 +1940,45 @@ var init_validation2 = __esm({
|
|
|
1940
1940
|
// src/harness/groups/store.ts
|
|
1941
1941
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1942
1942
|
import {
|
|
1943
|
-
closeSync as
|
|
1943
|
+
closeSync as closeSync5,
|
|
1944
1944
|
constants as constants3,
|
|
1945
1945
|
fstatSync as fstatSync2,
|
|
1946
1946
|
fsyncSync as fsyncSync2,
|
|
1947
|
-
openSync as
|
|
1948
|
-
readFileSync as
|
|
1949
|
-
realpathSync as
|
|
1947
|
+
openSync as openSync5,
|
|
1948
|
+
readFileSync as readFileSync35,
|
|
1949
|
+
realpathSync as realpathSync7,
|
|
1950
1950
|
renameSync as renameSync4,
|
|
1951
1951
|
writeFileSync as writeFileSync9
|
|
1952
1952
|
} from "fs";
|
|
1953
|
-
import { dirname as dirname21, join as
|
|
1953
|
+
import { dirname as dirname21, join as join32 } from "path";
|
|
1954
1954
|
function privateJson(path, value) {
|
|
1955
1955
|
const temporary = `${path}.${randomUUID5()}.new`;
|
|
1956
|
-
const descriptor =
|
|
1956
|
+
const descriptor = openSync5(temporary, "wx", 384);
|
|
1957
1957
|
try {
|
|
1958
1958
|
writeFileSync9(descriptor, `${JSON.stringify(value, null, 2)}
|
|
1959
1959
|
`);
|
|
1960
1960
|
fsyncSync2(descriptor);
|
|
1961
1961
|
} finally {
|
|
1962
|
-
|
|
1962
|
+
closeSync5(descriptor);
|
|
1963
1963
|
}
|
|
1964
1964
|
renameSync4(temporary, path);
|
|
1965
|
-
const directory =
|
|
1965
|
+
const directory = openSync5(dirname21(path), "r");
|
|
1966
1966
|
try {
|
|
1967
1967
|
fsyncSync2(directory);
|
|
1968
1968
|
} finally {
|
|
1969
|
-
|
|
1969
|
+
closeSync5(directory);
|
|
1970
1970
|
}
|
|
1971
1971
|
}
|
|
1972
1972
|
function readPrivateJson2(path) {
|
|
1973
|
-
const descriptor =
|
|
1973
|
+
const descriptor = openSync5(path, constants3.O_RDONLY | constants3.O_NOFOLLOW);
|
|
1974
1974
|
try {
|
|
1975
1975
|
const stat = fstatSync2(descriptor);
|
|
1976
1976
|
if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 2097152)
|
|
1977
1977
|
throw new Error("unsafe_state_file");
|
|
1978
1978
|
if (stat.uid !== process.getuid?.()) throw new Error("foreign_state_file");
|
|
1979
|
-
return JSON.parse(
|
|
1979
|
+
return JSON.parse(readFileSync35(descriptor, "utf8"));
|
|
1980
1980
|
} finally {
|
|
1981
|
-
|
|
1981
|
+
closeSync5(descriptor);
|
|
1982
1982
|
}
|
|
1983
1983
|
}
|
|
1984
1984
|
var GroupStore;
|
|
@@ -1991,10 +1991,10 @@ var init_store2 = __esm({
|
|
|
1991
1991
|
path;
|
|
1992
1992
|
state;
|
|
1993
1993
|
constructor(path) {
|
|
1994
|
-
this.path =
|
|
1994
|
+
this.path = realpathSync7(path);
|
|
1995
1995
|
this.state = validateState(readPrivateJson2(path));
|
|
1996
1996
|
const metadata2 = object8(
|
|
1997
|
-
readPrivateJson2(
|
|
1997
|
+
readPrivateJson2(join32(dirname21(this.path), "harness.json"))
|
|
1998
1998
|
);
|
|
1999
1999
|
if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
|
|
2000
2000
|
throw new Error("foreign_group");
|
|
@@ -2014,18 +2014,18 @@ var init_store2 = __esm({
|
|
|
2014
2014
|
});
|
|
2015
2015
|
|
|
2016
2016
|
// src/harness/adapters/process/serialized.ts
|
|
2017
|
-
import { spawnSync as
|
|
2017
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
2018
2018
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
2019
|
-
import { existsSync as
|
|
2020
|
-
import { dirname as dirname22, join as
|
|
2019
|
+
import { existsSync as existsSync26, unlinkSync as unlinkSync2 } from "fs";
|
|
2020
|
+
import { dirname as dirname22, join as join33 } from "path";
|
|
2021
2021
|
function serialized(operation) {
|
|
2022
2022
|
const store = new GroupStore(text(operation.groupFile, "group_file"));
|
|
2023
2023
|
const directory = dirname22(store.path);
|
|
2024
|
-
const operationFile =
|
|
2024
|
+
const operationFile = join33(directory, `operation-${randomUUID6()}.json`);
|
|
2025
2025
|
privateJson(operationFile, operation);
|
|
2026
2026
|
const worker = artifact("worker");
|
|
2027
2027
|
try {
|
|
2028
|
-
const result =
|
|
2028
|
+
const result = spawnSync5(
|
|
2029
2029
|
"flock",
|
|
2030
2030
|
[
|
|
2031
2031
|
"--exclusive",
|
|
@@ -2033,7 +2033,7 @@ function serialized(operation) {
|
|
|
2033
2033
|
"--no-fork",
|
|
2034
2034
|
"--conflict-exit-code",
|
|
2035
2035
|
"73",
|
|
2036
|
-
|
|
2036
|
+
join33(directory, "group-control.lock"),
|
|
2037
2037
|
process.execPath,
|
|
2038
2038
|
worker,
|
|
2039
2039
|
operationFile
|
|
@@ -2049,7 +2049,7 @@ function serialized(operation) {
|
|
|
2049
2049
|
if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
|
|
2050
2050
|
return JSON.parse(result.stdout);
|
|
2051
2051
|
} finally {
|
|
2052
|
-
if (
|
|
2052
|
+
if (existsSync26(operationFile)) unlinkSync2(operationFile);
|
|
2053
2053
|
}
|
|
2054
2054
|
}
|
|
2055
2055
|
var init_serialized = __esm({
|
|
@@ -2129,11 +2129,11 @@ var init_command = __esm({
|
|
|
2129
2129
|
});
|
|
2130
2130
|
|
|
2131
2131
|
// src/harness/adapters/process/identity.ts
|
|
2132
|
-
import { readFileSync as
|
|
2132
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
2133
2133
|
function processIdentity(pid) {
|
|
2134
2134
|
if (!Number.isSafeInteger(pid) || pid < 1)
|
|
2135
2135
|
throw new Error("invalid_process_id");
|
|
2136
|
-
const stat =
|
|
2136
|
+
const stat = readFileSync36(`/proc/${pid}/stat`, "utf8");
|
|
2137
2137
|
const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
2138
2138
|
if (!/^\d+$/.test(fields[19] ?? ""))
|
|
2139
2139
|
throw new Error("process_identity_unknown");
|
|
@@ -2368,14 +2368,14 @@ import {
|
|
|
2368
2368
|
lstatSync as lstatSync6,
|
|
2369
2369
|
mkdirSync as mkdirSync9,
|
|
2370
2370
|
readdirSync as readdirSync10,
|
|
2371
|
-
realpathSync as
|
|
2371
|
+
realpathSync as realpathSync8,
|
|
2372
2372
|
writeFileSync as writeFileSync10
|
|
2373
2373
|
} from "fs";
|
|
2374
2374
|
import { homedir as homedir7 } from "os";
|
|
2375
|
-
import { join as
|
|
2375
|
+
import { join as join34 } from "path";
|
|
2376
2376
|
function stateDirectory() {
|
|
2377
|
-
return
|
|
2378
|
-
process.env.XDG_STATE_HOME ??
|
|
2377
|
+
return join34(
|
|
2378
|
+
process.env.XDG_STATE_HOME ?? join34(homedir7(), ".local/state"),
|
|
2379
2379
|
"pmcp/harness"
|
|
2380
2380
|
);
|
|
2381
2381
|
}
|
|
@@ -2405,12 +2405,12 @@ var init_records = __esm({
|
|
|
2405
2405
|
const stat = lstatSync6(directory);
|
|
2406
2406
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
2407
2407
|
throw new Error("unsafe_group_directory");
|
|
2408
|
-
this.directory =
|
|
2409
|
-
this.socket =
|
|
2408
|
+
this.directory = realpathSync8(directory);
|
|
2409
|
+
this.socket = join34(this.directory, "s");
|
|
2410
2410
|
if (Buffer.byteLength(this.socket) > 100)
|
|
2411
2411
|
throw new Error("socket_path_too_long");
|
|
2412
|
-
this.metadataPath =
|
|
2413
|
-
this.configPath =
|
|
2412
|
+
this.metadataPath = join34(this.directory, "harness.json");
|
|
2413
|
+
this.configPath = join34(this.directory, "tmux.conf");
|
|
2414
2414
|
this.owner = text(
|
|
2415
2415
|
object8(readPrivateJson2(this.metadataPath)).owner,
|
|
2416
2416
|
"owner"
|
|
@@ -2420,17 +2420,17 @@ var init_records = __esm({
|
|
|
2420
2420
|
static create() {
|
|
2421
2421
|
const root = stateDirectory();
|
|
2422
2422
|
mkdirSync9(root, { recursive: true, mode: 448 });
|
|
2423
|
-
const directory =
|
|
2424
|
-
if (Buffer.byteLength(
|
|
2423
|
+
const directory = join34(root, randomUUID7().slice(0, 16));
|
|
2424
|
+
if (Buffer.byteLength(join34(directory, "s")) > 100)
|
|
2425
2425
|
throw new Error("socket_path_too_long");
|
|
2426
2426
|
mkdirSync9(directory, { mode: 448 });
|
|
2427
|
-
mkdirSync9(
|
|
2428
|
-
privateJson(
|
|
2427
|
+
mkdirSync9(join34(directory, "sessions"), { mode: 448 });
|
|
2428
|
+
privateJson(join34(directory, "harness.json"), {
|
|
2429
2429
|
owner: randomUUID7(),
|
|
2430
2430
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2431
2431
|
});
|
|
2432
2432
|
writeFileSync10(
|
|
2433
|
-
|
|
2433
|
+
join34(directory, "tmux.conf"),
|
|
2434
2434
|
[
|
|
2435
2435
|
"set -g remain-on-exit on",
|
|
2436
2436
|
"set -g history-limit 2000",
|
|
@@ -2446,7 +2446,7 @@ var init_records = __esm({
|
|
|
2446
2446
|
}
|
|
2447
2447
|
path(id2) {
|
|
2448
2448
|
runtimeUnit(id2);
|
|
2449
|
-
return
|
|
2449
|
+
return join34(this.directory, "sessions", `${id2}.json`);
|
|
2450
2450
|
}
|
|
2451
2451
|
read(id2) {
|
|
2452
2452
|
const record = object8(readPrivateJson2(this.path(id2)));
|
|
@@ -2468,7 +2468,7 @@ var init_records = __esm({
|
|
|
2468
2468
|
return record;
|
|
2469
2469
|
}
|
|
2470
2470
|
all() {
|
|
2471
|
-
return readdirSync10(
|
|
2471
|
+
return readdirSync10(join34(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
|
|
2472
2472
|
}
|
|
2473
2473
|
save(record) {
|
|
2474
2474
|
privateJson(this.path(record.id), record);
|
|
@@ -2550,8 +2550,8 @@ var init_launch_identity = __esm({
|
|
|
2550
2550
|
|
|
2551
2551
|
// src/harness/launch/journal.ts
|
|
2552
2552
|
import { createHash as createHash16 } from "crypto";
|
|
2553
|
-
import { existsSync as
|
|
2554
|
-
import { join as
|
|
2553
|
+
import { existsSync as existsSync27, readdirSync as readdirSync11 } from "fs";
|
|
2554
|
+
import { join as join35 } from "path";
|
|
2555
2555
|
function launchJournals(records2) {
|
|
2556
2556
|
return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
|
|
2557
2557
|
}
|
|
@@ -2559,7 +2559,7 @@ function pendingLaunches(records2) {
|
|
|
2559
2559
|
return launchJournals(records2).filter((journal) => {
|
|
2560
2560
|
const aborted = journal.abortRecord();
|
|
2561
2561
|
if (aborted) return aborted.state !== "stopped";
|
|
2562
|
-
if (!
|
|
2562
|
+
if (!existsSync27(journal.path("committed"))) return true;
|
|
2563
2563
|
journal.assertCommitted();
|
|
2564
2564
|
return false;
|
|
2565
2565
|
});
|
|
@@ -2586,7 +2586,7 @@ var init_journal = __esm({
|
|
|
2586
2586
|
path(kind2) {
|
|
2587
2587
|
if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
|
|
2588
2588
|
throw new Error("invalid_launch_artifact");
|
|
2589
|
-
return
|
|
2589
|
+
return join35(this.records.directory, `${this.id}.${kind2}.json`);
|
|
2590
2590
|
}
|
|
2591
2591
|
intent() {
|
|
2592
2592
|
const value = object8(readPrivateJson2(this.path("intent")));
|
|
@@ -2615,13 +2615,13 @@ var init_journal = __esm({
|
|
|
2615
2615
|
throw new Error("invalid_docker_bridge_digest");
|
|
2616
2616
|
if (!bridge && creation.bridgeSha256 !== void 0)
|
|
2617
2617
|
throw new Error("ungranted_docker_bridge_digest");
|
|
2618
|
-
const policy = new GroupStore(
|
|
2618
|
+
const policy = new GroupStore(join35(this.records.directory, "group.json")).state.group.dockerPolicy;
|
|
2619
2619
|
const profile = selectedProfile(
|
|
2620
2620
|
policy,
|
|
2621
2621
|
creation.privateProfile,
|
|
2622
2622
|
text(value.runtime, "runtime")
|
|
2623
2623
|
);
|
|
2624
|
-
const expectedState = profile?.directory ??
|
|
2624
|
+
const expectedState = profile?.directory ?? join35(
|
|
2625
2625
|
this.records.directory,
|
|
2626
2626
|
"containers",
|
|
2627
2627
|
text(value.nonce, "nonce"),
|
|
@@ -2667,7 +2667,7 @@ var init_journal = __esm({
|
|
|
2667
2667
|
}
|
|
2668
2668
|
assertRegistered() {
|
|
2669
2669
|
const intent = this.intent();
|
|
2670
|
-
const group = new GroupStore(
|
|
2670
|
+
const group = new GroupStore(join35(this.records.directory, "group.json")).state.group;
|
|
2671
2671
|
if (group.id !== intent.groupId || group.generation !== intent.generation)
|
|
2672
2672
|
throw new Error("launch_group_generation_mismatch");
|
|
2673
2673
|
if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
|
|
@@ -2683,12 +2683,12 @@ var init_journal = __esm({
|
|
|
2683
2683
|
throw new Error("launch_record_mismatch");
|
|
2684
2684
|
}
|
|
2685
2685
|
authorize() {
|
|
2686
|
-
if (
|
|
2686
|
+
if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
|
|
2687
2687
|
this.assertRegistered();
|
|
2688
2688
|
privateJson(this.path("grant"), this.grant());
|
|
2689
2689
|
}
|
|
2690
2690
|
assertGrant() {
|
|
2691
|
-
if (
|
|
2691
|
+
if (existsSync27(this.path("aborted"))) throw new Error("launch_was_aborted");
|
|
2692
2692
|
const grant = object8(readPrivateJson2(this.path("grant")));
|
|
2693
2693
|
if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
|
|
2694
2694
|
throw new Error("launch_grant_mismatch");
|
|
@@ -2705,7 +2705,7 @@ var init_journal = __esm({
|
|
|
2705
2705
|
throw new Error("launch_commit_mismatch");
|
|
2706
2706
|
}
|
|
2707
2707
|
abortRecord() {
|
|
2708
|
-
if (!
|
|
2708
|
+
if (!existsSync27(this.path("aborted"))) return;
|
|
2709
2709
|
const value = object8(readPrivateJson2(this.path("aborted")));
|
|
2710
2710
|
if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
|
|
2711
2711
|
throw new Error("foreign_launch_abort");
|
|
@@ -2752,30 +2752,30 @@ var init_lifecycle = __esm({
|
|
|
2752
2752
|
// src/harness/adapters/docker/private-profile.ts
|
|
2753
2753
|
import { homedir as homedir8 } from "os";
|
|
2754
2754
|
import {
|
|
2755
|
-
closeSync as
|
|
2755
|
+
closeSync as closeSync6,
|
|
2756
2756
|
constants as constants4,
|
|
2757
|
-
existsSync as
|
|
2757
|
+
existsSync as existsSync28,
|
|
2758
2758
|
fstatSync as fstatSync3,
|
|
2759
2759
|
lstatSync as lstatSync7,
|
|
2760
|
-
openSync as
|
|
2761
|
-
realpathSync as
|
|
2760
|
+
openSync as openSync6,
|
|
2761
|
+
realpathSync as realpathSync9,
|
|
2762
2762
|
statfsSync
|
|
2763
2763
|
} from "fs";
|
|
2764
|
-
import { join as
|
|
2764
|
+
import { join as join36, relative as relative11 } from "path";
|
|
2765
2765
|
function overlap(left, right) {
|
|
2766
2766
|
const scope = relative11(left, right);
|
|
2767
2767
|
return scope === "" || scope !== ".." && !scope.startsWith("../");
|
|
2768
2768
|
}
|
|
2769
2769
|
function profileIdentity(directory, projectRoot) {
|
|
2770
|
-
if (directory === "/" || directory ===
|
|
2770
|
+
if (directory === "/" || directory === realpathSync9(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync9(directory) !== directory)
|
|
2771
2771
|
throw new Error("private_profile_scope_not_admitted");
|
|
2772
2772
|
const stat = lstatSync7(directory);
|
|
2773
2773
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
|
|
2774
2774
|
throw new Error("unsafe_private_profile_directory");
|
|
2775
2775
|
if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
|
|
2776
2776
|
throw new Error("remote_private_profile_not_admitted");
|
|
2777
|
-
const lock =
|
|
2778
|
-
|
|
2777
|
+
const lock = openSync6(
|
|
2778
|
+
join36(directory, privateProfileLock),
|
|
2779
2779
|
constants4.O_RDONLY | constants4.O_NOFOLLOW
|
|
2780
2780
|
);
|
|
2781
2781
|
try {
|
|
@@ -2784,18 +2784,18 @@ function profileIdentity(directory, projectRoot) {
|
|
|
2784
2784
|
throw new Error("unsafe_private_profile_lock");
|
|
2785
2785
|
return `${stat.dev}:${stat.ino}:${value.dev}:${value.ino}`;
|
|
2786
2786
|
} finally {
|
|
2787
|
-
|
|
2787
|
+
closeSync6(lock);
|
|
2788
2788
|
}
|
|
2789
2789
|
}
|
|
2790
2790
|
function captureProfile(grant, projectRoot) {
|
|
2791
|
-
const directory =
|
|
2792
|
-
if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory ===
|
|
2791
|
+
const directory = realpathSync9(grant.directory);
|
|
2792
|
+
if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync9(homedir8()))
|
|
2793
2793
|
throw new Error("private_profile_scope_not_admitted");
|
|
2794
2794
|
const stat = lstatSync7(directory);
|
|
2795
2795
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
|
|
2796
2796
|
throw new Error("unsafe_private_profile_directory");
|
|
2797
|
-
const lock =
|
|
2798
|
-
if (!
|
|
2797
|
+
const lock = join36(directory, privateProfileLock);
|
|
2798
|
+
if (!existsSync28(lock)) closeSync6(openSync6(lock, "wx", 384));
|
|
2799
2799
|
const identity = profileIdentity(directory, projectRoot);
|
|
2800
2800
|
if (grant.identity && grant.identity !== identity)
|
|
2801
2801
|
throw new Error("private_profile_identity_changed");
|
|
@@ -2813,7 +2813,7 @@ function assertProfileAvailable(grant, projectRoot) {
|
|
|
2813
2813
|
"--nonblock",
|
|
2814
2814
|
"--conflict-exit-code",
|
|
2815
2815
|
"73",
|
|
2816
|
-
|
|
2816
|
+
join36(grant.directory, privateProfileLock),
|
|
2817
2817
|
"true"
|
|
2818
2818
|
]);
|
|
2819
2819
|
} catch (error) {
|
|
@@ -2833,7 +2833,7 @@ var init_private_profile = __esm({
|
|
|
2833
2833
|
|
|
2834
2834
|
// src/harness/adapters/docker/profile.ts
|
|
2835
2835
|
import { homedir as homedir9 } from "os";
|
|
2836
|
-
import { lstatSync as lstatSync8, realpathSync as
|
|
2836
|
+
import { lstatSync as lstatSync8, realpathSync as realpathSync10 } from "fs";
|
|
2837
2837
|
import { relative as relative12 } from "path";
|
|
2838
2838
|
function assertLocalDocker() {
|
|
2839
2839
|
if (process.env.DOCKER_HOST)
|
|
@@ -2850,12 +2850,12 @@ function assertLocalDocker() {
|
|
|
2850
2850
|
throw new Error("remote_docker_context_not_admitted");
|
|
2851
2851
|
}
|
|
2852
2852
|
function loadDockerPolicy(path, projectRoot, runtimes) {
|
|
2853
|
-
if (projectRoot === "/" || projectRoot ===
|
|
2853
|
+
if (projectRoot === "/" || projectRoot === realpathSync10(homedir9()))
|
|
2854
2854
|
throw new Error("docker_project_root_too_broad");
|
|
2855
2855
|
const inputPath = text(path, "docker_policy_file");
|
|
2856
2856
|
if (lstatSync8(inputPath).isSymbolicLink())
|
|
2857
2857
|
throw new Error("symlink_docker_policy_not_admitted");
|
|
2858
|
-
const source2 =
|
|
2858
|
+
const source2 = realpathSync10(inputPath);
|
|
2859
2859
|
const scope = relative12(projectRoot, source2);
|
|
2860
2860
|
if (scope === ".." || scope.startsWith("../"))
|
|
2861
2861
|
throw new Error("docker_policy_outside_project");
|
|
@@ -2889,14 +2889,14 @@ var init_profile = __esm({
|
|
|
2889
2889
|
});
|
|
2890
2890
|
|
|
2891
2891
|
// src/harness/adapters/docker/mounts.ts
|
|
2892
|
-
import { lstatSync as lstatSync9, realpathSync as
|
|
2893
|
-
import { join as
|
|
2892
|
+
import { lstatSync as lstatSync9, realpathSync as realpathSync11 } from "fs";
|
|
2893
|
+
import { join as join37 } from "path";
|
|
2894
2894
|
function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
|
|
2895
2895
|
const state = text(statePath, "container_state_directory");
|
|
2896
2896
|
const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
|
|
2897
2897
|
if (profile) assertProfileIdentity(profile, projectRoot);
|
|
2898
|
-
const expected = profile?.directory ??
|
|
2899
|
-
if (state !== expected ||
|
|
2898
|
+
const expected = profile?.directory ?? join37(directory, "containers", nonce, "profile");
|
|
2899
|
+
if (state !== expected || realpathSync11(state) !== expected)
|
|
2900
2900
|
throw new Error("foreign_container_state_directory");
|
|
2901
2901
|
const stat = lstatSync9(state);
|
|
2902
2902
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
@@ -2990,8 +2990,8 @@ var init_entrypoint = __esm({
|
|
|
2990
2990
|
});
|
|
2991
2991
|
|
|
2992
2992
|
// src/harness/adapters/docker/admission.ts
|
|
2993
|
-
import { lstatSync as lstatSync10, realpathSync as
|
|
2994
|
-
import { join as
|
|
2993
|
+
import { lstatSync as lstatSync10, realpathSync as realpathSync12 } from "fs";
|
|
2994
|
+
import { join as join38, relative as relative13 } from "path";
|
|
2995
2995
|
function admitResource(path, group, role, directory, memoryMb, runtime2) {
|
|
2996
2996
|
if (group.profile !== "docker" || !group.dockerPolicy)
|
|
2997
2997
|
throw new Error("container_requires_docker_profile");
|
|
@@ -3000,7 +3000,7 @@ function admitResource(path, group, role, directory, memoryMb, runtime2) {
|
|
|
3000
3000
|
const inputPath = text(path, "container_file");
|
|
3001
3001
|
if (lstatSync10(inputPath).isSymbolicLink())
|
|
3002
3002
|
throw new Error("symlink_container_file_not_admitted");
|
|
3003
|
-
const source2 =
|
|
3003
|
+
const source2 = realpathSync12(inputPath);
|
|
3004
3004
|
const scope = relative13(projectRoot, source2);
|
|
3005
3005
|
if (scope === ".." || scope.startsWith("../"))
|
|
3006
3006
|
throw new Error("container_file_outside_project");
|
|
@@ -3047,7 +3047,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
|
|
|
3047
3047
|
)
|
|
3048
3048
|
))
|
|
3049
3049
|
throw new Error("container_runtime_image_not_granted");
|
|
3050
|
-
if (config.WorkingDir !==
|
|
3050
|
+
if (config.WorkingDir !== join38("/workspace", relative13(projectRoot, cwd)))
|
|
3051
3051
|
throw new Error("container_cwd_not_granted");
|
|
3052
3052
|
assertContainerMounts(
|
|
3053
3053
|
actual,
|
|
@@ -3183,20 +3183,20 @@ var init_engine = __esm({
|
|
|
3183
3183
|
// src/harness/adapters/docker/bridge-artifact.ts
|
|
3184
3184
|
import { createHash as createHash17 } from "crypto";
|
|
3185
3185
|
import {
|
|
3186
|
-
closeSync as
|
|
3186
|
+
closeSync as closeSync7,
|
|
3187
3187
|
constants as constants5,
|
|
3188
3188
|
fstatSync as fstatSync4,
|
|
3189
3189
|
fsyncSync as fsyncSync3,
|
|
3190
|
-
openSync as
|
|
3191
|
-
readFileSync as
|
|
3190
|
+
openSync as openSync7,
|
|
3191
|
+
readFileSync as readFileSync37,
|
|
3192
3192
|
writeFileSync as writeFileSync11
|
|
3193
3193
|
} from "fs";
|
|
3194
|
-
import { join as
|
|
3194
|
+
import { join as join39 } from "path";
|
|
3195
3195
|
function source() {
|
|
3196
3196
|
const path = artifact("egress-bridge");
|
|
3197
3197
|
if (!path.endsWith(".js"))
|
|
3198
3198
|
throw new Error("docker_bridge_requires_compiled_cli");
|
|
3199
|
-
return
|
|
3199
|
+
return readFileSync37(path);
|
|
3200
3200
|
}
|
|
3201
3201
|
function digest(value) {
|
|
3202
3202
|
return createHash17("sha256").update(value).digest("hex");
|
|
@@ -3209,8 +3209,8 @@ function prepareBridge(creation) {
|
|
|
3209
3209
|
const content = source();
|
|
3210
3210
|
if (digest(content) !== creation.bridgeSha256)
|
|
3211
3211
|
throw new Error("docker_bridge_source_changed");
|
|
3212
|
-
const descriptor =
|
|
3213
|
-
|
|
3212
|
+
const descriptor = openSync7(
|
|
3213
|
+
join39(creation.state, bridgeName(creation.nonce)),
|
|
3214
3214
|
"wx",
|
|
3215
3215
|
384
|
|
3216
3216
|
);
|
|
@@ -3218,21 +3218,21 @@ function prepareBridge(creation) {
|
|
|
3218
3218
|
writeFileSync11(descriptor, content);
|
|
3219
3219
|
fsyncSync3(descriptor);
|
|
3220
3220
|
} finally {
|
|
3221
|
-
|
|
3221
|
+
closeSync7(descriptor);
|
|
3222
3222
|
}
|
|
3223
3223
|
}
|
|
3224
3224
|
function assertBridge(creation) {
|
|
3225
3225
|
if (!creation.egress?.bridgeExecutable) return;
|
|
3226
|
-
const descriptor =
|
|
3227
|
-
|
|
3226
|
+
const descriptor = openSync7(
|
|
3227
|
+
join39(creation.state, bridgeName(creation.nonce)),
|
|
3228
3228
|
constants5.O_RDONLY | constants5.O_NOFOLLOW
|
|
3229
3229
|
);
|
|
3230
3230
|
try {
|
|
3231
3231
|
const stat = fstatSync4(descriptor);
|
|
3232
|
-
if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(
|
|
3232
|
+
if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(readFileSync37(descriptor)) !== creation.bridgeSha256)
|
|
3233
3233
|
throw new Error("docker_bridge_artifact_changed");
|
|
3234
3234
|
} finally {
|
|
3235
|
-
|
|
3235
|
+
closeSync7(descriptor);
|
|
3236
3236
|
}
|
|
3237
3237
|
}
|
|
3238
3238
|
var init_bridge_artifact = __esm({
|
|
@@ -3244,7 +3244,7 @@ var init_bridge_artifact = __esm({
|
|
|
3244
3244
|
});
|
|
3245
3245
|
|
|
3246
3246
|
// src/harness/adapters/docker/creation-plan.ts
|
|
3247
|
-
import { join as
|
|
3247
|
+
import { join as join40, relative as relative14 } from "path";
|
|
3248
3248
|
function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
|
|
3249
3249
|
if (group.profile !== "docker" || !group.dockerPolicy)
|
|
3250
3250
|
throw new Error("container_requires_docker_profile");
|
|
@@ -3261,7 +3261,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
|
|
|
3261
3261
|
throw new Error("invalid_container_image_environment");
|
|
3262
3262
|
const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
|
|
3263
3263
|
if (profile) assertProfileAvailable(profile, group.projectRoot);
|
|
3264
|
-
const state = profile?.directory ??
|
|
3264
|
+
const state = profile?.directory ?? join40(directory, "containers", nonce, "profile");
|
|
3265
3265
|
return {
|
|
3266
3266
|
name: containerName(group.owner, id2),
|
|
3267
3267
|
nonce,
|
|
@@ -3278,7 +3278,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
|
|
|
3278
3278
|
preparation.privateProfile
|
|
3279
3279
|
),
|
|
3280
3280
|
Cmd: preparation.args.length ? preparation.args : null,
|
|
3281
|
-
WorkingDir:
|
|
3281
|
+
WorkingDir: join40("/workspace", relative14(group.projectRoot, cwd)),
|
|
3282
3282
|
User: `${process.getuid?.()}:${process.getgid?.()}`,
|
|
3283
3283
|
Env: [
|
|
3284
3284
|
...environment2.filter(
|
|
@@ -3377,12 +3377,12 @@ var init_creation_plan = __esm({
|
|
|
3377
3377
|
|
|
3378
3378
|
// src/harness/adapters/docker/creation.ts
|
|
3379
3379
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
3380
|
-
import { join as
|
|
3380
|
+
import { join as join41 } from "path";
|
|
3381
3381
|
async function prepareCreation(journal) {
|
|
3382
3382
|
const intent = journal.intent();
|
|
3383
3383
|
const creation = intent.dockerCreation;
|
|
3384
3384
|
if (!creation) throw new Error("container_creation_missing");
|
|
3385
|
-
const group = new GroupStore(
|
|
3385
|
+
const group = new GroupStore(join41(journal.records.directory, "group.json")).state.group;
|
|
3386
3386
|
const profile = selectedProfile(
|
|
3387
3387
|
group.dockerPolicy,
|
|
3388
3388
|
creation.privateProfile,
|
|
@@ -3396,7 +3396,7 @@ async function prepareCreation(journal) {
|
|
|
3396
3396
|
creation.name,
|
|
3397
3397
|
creation.configuration
|
|
3398
3398
|
);
|
|
3399
|
-
const current = new GroupStore(
|
|
3399
|
+
const current = new GroupStore(join41(journal.records.directory, "group.json")).state.group;
|
|
3400
3400
|
if (current.id !== intent.groupId || current.generation !== intent.generation)
|
|
3401
3401
|
throw new Error("launch_group_generation_mismatch");
|
|
3402
3402
|
const resource = {
|
|
@@ -3464,7 +3464,7 @@ function findCreation(journal) {
|
|
|
3464
3464
|
assertResourceOwner(resource, actual);
|
|
3465
3465
|
assertCreationConfiguration(actual, creation);
|
|
3466
3466
|
if (!intent.resource) {
|
|
3467
|
-
const group = new GroupStore(
|
|
3467
|
+
const group = new GroupStore(join41(journal.records.directory, "group.json")).state.group;
|
|
3468
3468
|
return {
|
|
3469
3469
|
...admitClaim(
|
|
3470
3470
|
{ ...resource, runtime: intent.runtime, state: creation.state },
|
|
@@ -3501,7 +3501,7 @@ var init_creation = __esm({
|
|
|
3501
3501
|
});
|
|
3502
3502
|
|
|
3503
3503
|
// src/harness/launch/recovery.ts
|
|
3504
|
-
import { existsSync as
|
|
3504
|
+
import { existsSync as existsSync29 } from "fs";
|
|
3505
3505
|
function abortLaunch(connection, journal) {
|
|
3506
3506
|
const intent = journal.intent();
|
|
3507
3507
|
if (!controllerExited(intent.controllerIdentity))
|
|
@@ -3518,7 +3518,7 @@ function abortLaunch(connection, journal) {
|
|
|
3518
3518
|
inputReplayed: false
|
|
3519
3519
|
};
|
|
3520
3520
|
}
|
|
3521
|
-
if (!
|
|
3521
|
+
if (!existsSync29(journal.path("proof"))) {
|
|
3522
3522
|
const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
|
|
3523
3523
|
if (paneUnknown || unitActive(intent.unit))
|
|
3524
3524
|
throw new Error("launch_identity_unknown");
|
|
@@ -3545,7 +3545,7 @@ function abortLaunch(connection, journal) {
|
|
|
3545
3545
|
assertProof(connection, proof);
|
|
3546
3546
|
connection.tmux(["kill-pane", "-t", proof.session.paneId]);
|
|
3547
3547
|
}
|
|
3548
|
-
if (
|
|
3548
|
+
if (existsSync29(journal.records.path(intent.id)))
|
|
3549
3549
|
journal.records.save({
|
|
3550
3550
|
...proof.session,
|
|
3551
3551
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -3572,9 +3572,9 @@ var init_recovery = __esm({
|
|
|
3572
3572
|
|
|
3573
3573
|
// src/harness/adapters/tmux/lifecycle.ts
|
|
3574
3574
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
3575
|
-
import { existsSync as
|
|
3575
|
+
import { existsSync as existsSync30 } from "fs";
|
|
3576
3576
|
import { setTimeout as setTimeout2 } from "timers/promises";
|
|
3577
|
-
import { join as
|
|
3577
|
+
import { join as join42 } from "path";
|
|
3578
3578
|
async function start(connection, spec) {
|
|
3579
3579
|
const records2 = connection.records;
|
|
3580
3580
|
if (spec.budgetSlice !== budgetSlice(records2.owner))
|
|
@@ -3588,7 +3588,7 @@ async function start(connection, spec) {
|
|
|
3588
3588
|
const nonce = randomUUID8();
|
|
3589
3589
|
const journal = new LaunchJournal(records2, id2);
|
|
3590
3590
|
const dockerCreation = spec.dockerPreparation ? creationPlan(
|
|
3591
|
-
new GroupStore(
|
|
3591
|
+
new GroupStore(join42(records2.directory, "group.json")).state.group,
|
|
3592
3592
|
records2.directory,
|
|
3593
3593
|
id2,
|
|
3594
3594
|
nonce,
|
|
@@ -3648,7 +3648,7 @@ async function start(connection, spec) {
|
|
|
3648
3648
|
`exec ${argv.map(quote2).join(" ")}`
|
|
3649
3649
|
]);
|
|
3650
3650
|
const deadline = Date.now() + 5e3;
|
|
3651
|
-
while (!
|
|
3651
|
+
while (!existsSync30(journal.path("proof"))) {
|
|
3652
3652
|
if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
|
|
3653
3653
|
await setTimeout2(25);
|
|
3654
3654
|
}
|
|
@@ -3659,7 +3659,7 @@ async function start(connection, spec) {
|
|
|
3659
3659
|
}
|
|
3660
3660
|
function stop(connection, id2) {
|
|
3661
3661
|
const journal = new LaunchJournal(connection.records, id2);
|
|
3662
|
-
if (
|
|
3662
|
+
if (existsSync30(journal.path("intent"))) {
|
|
3663
3663
|
abortLaunch(connection, journal);
|
|
3664
3664
|
return;
|
|
3665
3665
|
}
|
|
@@ -3686,7 +3686,7 @@ function cleanup(connection) {
|
|
|
3686
3686
|
}
|
|
3687
3687
|
for (const record of records2) {
|
|
3688
3688
|
if (record.resource) stopResource(record.resource);
|
|
3689
|
-
if (record.stoppedAt || !
|
|
3689
|
+
if (record.stoppedAt || !existsSync30(
|
|
3690
3690
|
new LaunchJournal(connection.records, record.id).path("intent")
|
|
3691
3691
|
)) {
|
|
3692
3692
|
stopUnit(record.unit);
|
|
@@ -3717,8 +3717,8 @@ var init_lifecycle2 = __esm({
|
|
|
3717
3717
|
|
|
3718
3718
|
// src/harness/adapters/tmux/input.ts
|
|
3719
3719
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
3720
|
-
import { closeSync as
|
|
3721
|
-
import { join as
|
|
3720
|
+
import { closeSync as closeSync8, openSync as openSync8, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
|
|
3721
|
+
import { join as join43 } from "path";
|
|
3722
3722
|
function validateText(text3) {
|
|
3723
3723
|
if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
|
|
3724
3724
|
throw new Error("invalid_text");
|
|
@@ -3749,8 +3749,8 @@ function write(connection, id2, text3, submit) {
|
|
|
3749
3749
|
validateText(text3);
|
|
3750
3750
|
const session = connection.inspect(id2);
|
|
3751
3751
|
if (!session.alive) throw new Error("runtime_exited");
|
|
3752
|
-
const lock =
|
|
3753
|
-
const descriptor =
|
|
3752
|
+
const lock = join43(connection.records.directory, `${id2}.input.lock`);
|
|
3753
|
+
const descriptor = openSync8(lock, "wx", 384);
|
|
3754
3754
|
const buffer = `pmcp-${randomUUID9()}`;
|
|
3755
3755
|
try {
|
|
3756
3756
|
writeFileSync12(
|
|
@@ -3791,7 +3791,7 @@ function write(connection, id2, text3, submit) {
|
|
|
3791
3791
|
connection.tmux(["delete-buffer", "-b", buffer]);
|
|
3792
3792
|
} catch {
|
|
3793
3793
|
}
|
|
3794
|
-
|
|
3794
|
+
closeSync8(descriptor);
|
|
3795
3795
|
unlinkSync3(lock);
|
|
3796
3796
|
}
|
|
3797
3797
|
}
|
|
@@ -3908,10 +3908,10 @@ var init_backend = __esm({
|
|
|
3908
3908
|
});
|
|
3909
3909
|
|
|
3910
3910
|
// src/harness/adapters/systemd/budget.ts
|
|
3911
|
-
import { join as
|
|
3911
|
+
import { join as join44 } from "path";
|
|
3912
3912
|
async function budgetOperation(directory, operation) {
|
|
3913
3913
|
const backend = openBackend(directory);
|
|
3914
|
-
const path =
|
|
3914
|
+
const path = join44(directory, "runtime-policy.json");
|
|
3915
3915
|
const metadata2 = object8(readPrivateJson2(backend.metadataPath));
|
|
3916
3916
|
const slice = budgetSlice(backend.owner);
|
|
3917
3917
|
if (operation.action === "configure") {
|
|
@@ -4012,7 +4012,7 @@ var init_budget = __esm({
|
|
|
4012
4012
|
|
|
4013
4013
|
// src/harness/adapters/systemd/configure.ts
|
|
4014
4014
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
4015
|
-
import { join as
|
|
4015
|
+
import { join as join45 } from "path";
|
|
4016
4016
|
function configureBudget(context, input) {
|
|
4017
4017
|
const { store, backend } = context;
|
|
4018
4018
|
assertOwnedRecords(backend);
|
|
@@ -4034,7 +4034,7 @@ function configureBudget(context, input) {
|
|
|
4034
4034
|
throw new Error("capacity_below_active_sessions");
|
|
4035
4035
|
if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
|
|
4036
4036
|
throw new Error("budget_below_reserved_memory");
|
|
4037
|
-
const path =
|
|
4037
|
+
const path = join45(backend.runDirectory, "runtime-policy.json");
|
|
4038
4038
|
const policy = object8(readPrivateJson2(path));
|
|
4039
4039
|
const metadata2 = object8(readPrivateJson2(backend.metadataPath));
|
|
4040
4040
|
const slice = budgetSlice(backend.owner);
|
|
@@ -4071,14 +4071,14 @@ var init_configure = __esm({
|
|
|
4071
4071
|
});
|
|
4072
4072
|
|
|
4073
4073
|
// src/harness/adapters/process/executable.ts
|
|
4074
|
-
import { accessSync, constants as constants6, statSync as
|
|
4074
|
+
import { accessSync, constants as constants6, statSync as statSync17 } from "fs";
|
|
4075
4075
|
import { delimiter, resolve as resolve18 } from "path";
|
|
4076
4076
|
function locateExecutable(binary, cwd = process.cwd()) {
|
|
4077
4077
|
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
4078
4078
|
if (!directory) continue;
|
|
4079
4079
|
const path = resolve18(cwd, directory, binary);
|
|
4080
4080
|
try {
|
|
4081
|
-
if (!
|
|
4081
|
+
if (!statSync17(path).isFile()) continue;
|
|
4082
4082
|
accessSync(path, constants6.X_OK);
|
|
4083
4083
|
return path;
|
|
4084
4084
|
} catch {
|
|
@@ -4129,11 +4129,11 @@ var init_admission2 = __esm({
|
|
|
4129
4129
|
|
|
4130
4130
|
// src/harness/groups/service.ts
|
|
4131
4131
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
4132
|
-
import { realpathSync as
|
|
4133
|
-
import { join as
|
|
4132
|
+
import { realpathSync as realpathSync13, statSync as statSync18, unlinkSync as unlinkSync4 } from "fs";
|
|
4133
|
+
import { join as join46 } from "path";
|
|
4134
4134
|
async function createGroup(input) {
|
|
4135
|
-
const projectRoot =
|
|
4136
|
-
if (!
|
|
4135
|
+
const projectRoot = realpathSync13(text(input.projectRoot, "project_root"));
|
|
4136
|
+
if (!statSync18(projectRoot).isDirectory())
|
|
4137
4137
|
throw new Error("invalid_project_root");
|
|
4138
4138
|
const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
|
|
4139
4139
|
const profile = input.profile ?? "native";
|
|
@@ -4163,7 +4163,7 @@ async function createGroup(input) {
|
|
|
4163
4163
|
sessions: [],
|
|
4164
4164
|
...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
|
|
4165
4165
|
};
|
|
4166
|
-
const groupFile =
|
|
4166
|
+
const groupFile = join46(backend.runDirectory, "group.json");
|
|
4167
4167
|
privateJson(groupFile, { group, leases: [], deliveries: [] });
|
|
4168
4168
|
try {
|
|
4169
4169
|
await budgetOperation(backend.runDirectory, {
|
|
@@ -4176,7 +4176,7 @@ async function createGroup(input) {
|
|
|
4176
4176
|
await budgetOperation(backend.runDirectory, { action: "cleanup" });
|
|
4177
4177
|
unlinkSync4(groupFile);
|
|
4178
4178
|
} catch (cleanupError) {
|
|
4179
|
-
privateJson(
|
|
4179
|
+
privateJson(join46(backend.runDirectory, "initialization-failure.json"), {
|
|
4180
4180
|
error: String(error),
|
|
4181
4181
|
cleanupError: String(cleanupError),
|
|
4182
4182
|
recoveryRequired: true
|
|
@@ -4238,16 +4238,16 @@ var init_service2 = __esm({
|
|
|
4238
4238
|
});
|
|
4239
4239
|
|
|
4240
4240
|
// src/harness/groups/catalog.ts
|
|
4241
|
-
import { existsSync as
|
|
4242
|
-
import { join as
|
|
4241
|
+
import { existsSync as existsSync31, readdirSync as readdirSync12 } from "fs";
|
|
4242
|
+
import { join as join47 } from "path";
|
|
4243
4243
|
function listGroups() {
|
|
4244
4244
|
const directory = stateDirectory();
|
|
4245
|
-
if (!
|
|
4245
|
+
if (!existsSync31(directory)) return { groups: [] };
|
|
4246
4246
|
const groups2 = [];
|
|
4247
4247
|
for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
|
|
4248
4248
|
if (!entry2.isDirectory()) continue;
|
|
4249
|
-
const path =
|
|
4250
|
-
if (!
|
|
4249
|
+
const path = join47(directory, entry2.name, "group.json");
|
|
4250
|
+
if (!existsSync31(path)) continue;
|
|
4251
4251
|
try {
|
|
4252
4252
|
const store = new GroupStore(path);
|
|
4253
4253
|
groups2.push({ groupFile: store.path, group: store.state.group });
|
|
@@ -4444,7 +4444,7 @@ var init_watch = __esm({
|
|
|
4444
4444
|
|
|
4445
4445
|
// src/harness/control/leases.ts
|
|
4446
4446
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
4447
|
-
import { join as
|
|
4447
|
+
import { join as join48 } from "path";
|
|
4448
4448
|
function resolveLease(context, file2, allowExpired = false) {
|
|
4449
4449
|
const credential2 = object8(readPrivateJson2(file2));
|
|
4450
4450
|
const lease = context.store.state.leases.find(
|
|
@@ -4488,7 +4488,7 @@ function acquireControl(context, sessionId, controller, mode) {
|
|
|
4488
4488
|
);
|
|
4489
4489
|
store.state.leases.push(lease);
|
|
4490
4490
|
store.save();
|
|
4491
|
-
const leaseFile =
|
|
4491
|
+
const leaseFile = join48(backend.runDirectory, `lease-${randomUUID11()}.json`);
|
|
4492
4492
|
privateJson(leaseFile, { token: lease.token });
|
|
4493
4493
|
return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
|
|
4494
4494
|
}
|
|
@@ -4635,12 +4635,12 @@ var init_service5 = __esm({
|
|
|
4635
4635
|
});
|
|
4636
4636
|
|
|
4637
4637
|
// src/harness/adapters/process/recover-lock.ts
|
|
4638
|
-
import { existsSync as
|
|
4639
|
-
import { join as
|
|
4638
|
+
import { existsSync as existsSync32, lstatSync as lstatSync11, unlinkSync as unlinkSync5 } from "fs";
|
|
4639
|
+
import { join as join49 } from "path";
|
|
4640
4640
|
function recoverInputLock(backend, sessionId) {
|
|
4641
4641
|
backend.inspect(sessionId);
|
|
4642
|
-
const path =
|
|
4643
|
-
if (!
|
|
4642
|
+
const path = join49(backend.runDirectory, `${sessionId}.input.lock`);
|
|
4643
|
+
if (!existsSync32(path)) return;
|
|
4644
4644
|
const before = lstatSync11(path);
|
|
4645
4645
|
const record = object8(readPrivateJson2(path));
|
|
4646
4646
|
if (record.id !== sessionId) throw new Error("foreign_writer_lock");
|
|
@@ -4660,7 +4660,7 @@ var init_recover_lock = __esm({
|
|
|
4660
4660
|
});
|
|
4661
4661
|
|
|
4662
4662
|
// src/harness/launch/service.ts
|
|
4663
|
-
import { existsSync as
|
|
4663
|
+
import { existsSync as existsSync33 } from "fs";
|
|
4664
4664
|
function assertLaunchAdmission(records2) {
|
|
4665
4665
|
for (const journal of launchJournals(records2)) {
|
|
4666
4666
|
if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
|
|
@@ -4685,12 +4685,12 @@ function inspectPendingStarts(context) {
|
|
|
4685
4685
|
role: journal.intent().role,
|
|
4686
4686
|
runtime: journal.intent().runtime,
|
|
4687
4687
|
controllerExited: controllerExited(journal.intent().controllerIdentity),
|
|
4688
|
-
proofPresent:
|
|
4689
|
-
recordPresent:
|
|
4688
|
+
proofPresent: existsSync33(journal.path("proof")),
|
|
4689
|
+
recordPresent: existsSync33(journal.records.path(journal.id)),
|
|
4690
4690
|
registered: context.store.state.group.sessions.some(
|
|
4691
4691
|
(session) => session.id === journal.id
|
|
4692
4692
|
),
|
|
4693
|
-
authorizationPresent:
|
|
4693
|
+
authorizationPresent: existsSync33(journal.path("grant")),
|
|
4694
4694
|
processingConfirmed: false,
|
|
4695
4695
|
inputReplayed: false
|
|
4696
4696
|
}));
|
|
@@ -4708,7 +4708,7 @@ var init_service6 = __esm({
|
|
|
4708
4708
|
});
|
|
4709
4709
|
|
|
4710
4710
|
// src/harness/sessions/service.ts
|
|
4711
|
-
import { realpathSync as
|
|
4711
|
+
import { realpathSync as realpathSync14 } from "fs";
|
|
4712
4712
|
import { relative as relative15 } from "path";
|
|
4713
4713
|
async function startSession(context, input) {
|
|
4714
4714
|
const { store, backend } = context;
|
|
@@ -4742,7 +4742,7 @@ async function startSession(context, input) {
|
|
|
4742
4742
|
);
|
|
4743
4743
|
if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
|
|
4744
4744
|
}
|
|
4745
|
-
const cwd =
|
|
4745
|
+
const cwd = realpathSync14(text(input.cwd ?? group.projectRoot, "cwd"));
|
|
4746
4746
|
const scope = relative15(group.projectRoot, cwd);
|
|
4747
4747
|
if (scope === ".." || scope.startsWith("../"))
|
|
4748
4748
|
throw new Error("cwd_outside_project");
|
|
@@ -5205,9 +5205,9 @@ var init_invoke = __esm({
|
|
|
5205
5205
|
});
|
|
5206
5206
|
|
|
5207
5207
|
// src/tui/adapters/process/runtime.ts
|
|
5208
|
-
import { dirname as dirname24, join as
|
|
5208
|
+
import { dirname as dirname24, join as join50 } from "path";
|
|
5209
5209
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5210
|
-
import { existsSync as
|
|
5210
|
+
import { existsSync as existsSync34, realpathSync as realpathSync15 } from "fs";
|
|
5211
5211
|
function dependencies() {
|
|
5212
5212
|
const tmux = locateExecutable("tmux");
|
|
5213
5213
|
const flock = locateExecutable("flock");
|
|
@@ -5241,9 +5241,9 @@ function runtime() {
|
|
|
5241
5241
|
throw new Error("tui_dependencies_unavailable");
|
|
5242
5242
|
const current = fileURLToPath4(import.meta.url);
|
|
5243
5243
|
const directory = dirname24(current);
|
|
5244
|
-
const bundled =
|
|
5245
|
-
if (!
|
|
5246
|
-
return { node:
|
|
5244
|
+
const bundled = join50(directory, "tui-worker.js");
|
|
5245
|
+
if (!existsSync34(bundled)) throw new Error("tui_build_required");
|
|
5246
|
+
return { node: realpathSync15(available.node), worker: bundled };
|
|
5247
5247
|
}
|
|
5248
5248
|
var init_runtime2 = __esm({
|
|
5249
5249
|
"src/tui/adapters/process/runtime.ts"() {
|
|
@@ -5330,32 +5330,32 @@ var init_diagnostics = __esm({
|
|
|
5330
5330
|
// src/tui/state/store.ts
|
|
5331
5331
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
5332
5332
|
import {
|
|
5333
|
-
existsSync as
|
|
5333
|
+
existsSync as existsSync35,
|
|
5334
5334
|
lstatSync as lstatSync12,
|
|
5335
5335
|
mkdirSync as mkdirSync11,
|
|
5336
|
-
realpathSync as
|
|
5336
|
+
realpathSync as realpathSync16,
|
|
5337
5337
|
unlinkSync as unlinkSync6,
|
|
5338
5338
|
writeFileSync as writeFileSync13
|
|
5339
5339
|
} from "fs";
|
|
5340
5340
|
import { homedir as homedir10 } from "os";
|
|
5341
|
-
import { dirname as dirname25, join as
|
|
5341
|
+
import { dirname as dirname25, join as join51 } from "path";
|
|
5342
5342
|
function createDirectory() {
|
|
5343
|
-
const root =
|
|
5344
|
-
process.env.XDG_STATE_HOME ??
|
|
5343
|
+
const root = join51(
|
|
5344
|
+
process.env.XDG_STATE_HOME ?? join51(homedir10(), ".local/state"),
|
|
5345
5345
|
"pmcp/tui"
|
|
5346
5346
|
);
|
|
5347
5347
|
mkdirSync11(root, { recursive: true, mode: 448 });
|
|
5348
|
-
const directory =
|
|
5349
|
-
if (Buffer.byteLength(
|
|
5348
|
+
const directory = join51(realpathSync16(root), randomUUID13().slice(0, 16));
|
|
5349
|
+
if (Buffer.byteLength(join51(directory, "s")) > 100)
|
|
5350
5350
|
throw new Error("socket_path_too_long");
|
|
5351
5351
|
mkdirSync11(directory, { mode: 448 });
|
|
5352
5352
|
const owner = randomUUID13();
|
|
5353
|
-
privateJson(
|
|
5353
|
+
privateJson(join51(directory, "owner.json"), {
|
|
5354
5354
|
owner,
|
|
5355
5355
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5356
5356
|
});
|
|
5357
5357
|
writeFileSync13(
|
|
5358
|
-
|
|
5358
|
+
join51(directory, "tmux.conf"),
|
|
5359
5359
|
[
|
|
5360
5360
|
"set -g remain-on-exit on",
|
|
5361
5361
|
"set -g history-limit 2000",
|
|
@@ -5370,13 +5370,13 @@ function createDirectory() {
|
|
|
5370
5370
|
return { directory, owner };
|
|
5371
5371
|
}
|
|
5372
5372
|
function readView(path) {
|
|
5373
|
-
const file2 =
|
|
5373
|
+
const file2 = realpathSync16(path);
|
|
5374
5374
|
const directory = dirname25(file2);
|
|
5375
5375
|
const stat = lstatSync12(directory);
|
|
5376
5376
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
5377
5377
|
throw new Error("unsafe_view_directory");
|
|
5378
5378
|
const value = object8(readPrivateJson2(file2));
|
|
5379
|
-
const metadata2 = object8(readPrivateJson2(
|
|
5379
|
+
const metadata2 = object8(readPrivateJson2(join51(directory, "owner.json")));
|
|
5380
5380
|
if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
|
|
5381
5381
|
throw new Error("foreign_view_owner");
|
|
5382
5382
|
for (const key of [
|
|
@@ -5413,8 +5413,8 @@ function readView(path) {
|
|
|
5413
5413
|
throw new Error("invalid_view_identity");
|
|
5414
5414
|
}
|
|
5415
5415
|
const view = value;
|
|
5416
|
-
const fallback =
|
|
5417
|
-
if (
|
|
5416
|
+
const fallback = join51(directory, "callback-failure.json");
|
|
5417
|
+
if (existsSync35(fallback)) {
|
|
5418
5418
|
const recorded = object8(readPrivateJson2(fallback));
|
|
5419
5419
|
const failure2 = object8(recorded.failure);
|
|
5420
5420
|
if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
|
|
@@ -5425,8 +5425,8 @@ function readView(path) {
|
|
|
5425
5425
|
return { file: file2, view };
|
|
5426
5426
|
}
|
|
5427
5427
|
function dismissFailure(file2, view) {
|
|
5428
|
-
const fallback =
|
|
5429
|
-
if (
|
|
5428
|
+
const fallback = join51(view.directory, "callback-failure.json");
|
|
5429
|
+
if (existsSync35(fallback)) unlinkSync6(fallback);
|
|
5430
5430
|
delete view.failure;
|
|
5431
5431
|
saveView(file2, view);
|
|
5432
5432
|
}
|
|
@@ -5443,7 +5443,7 @@ var init_store3 = __esm({
|
|
|
5443
5443
|
});
|
|
5444
5444
|
|
|
5445
5445
|
// src/tui/adapters/process/lock.ts
|
|
5446
|
-
import { spawnSync as
|
|
5446
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
5447
5447
|
function serializedView(invocation2) {
|
|
5448
5448
|
const { file: file2, view } = readView(invocation2.viewFile);
|
|
5449
5449
|
const args = ["locked", invocation2.action, "--view", file2];
|
|
@@ -5454,7 +5454,7 @@ function serializedView(invocation2) {
|
|
|
5454
5454
|
["confirm-session", invocation2.input.confirmSession]
|
|
5455
5455
|
])
|
|
5456
5456
|
if (value !== void 0) args.push(`--${name}`, String(value));
|
|
5457
|
-
const child =
|
|
5457
|
+
const child = spawnSync6(
|
|
5458
5458
|
"flock",
|
|
5459
5459
|
[
|
|
5460
5460
|
"--wait",
|
|
@@ -5488,7 +5488,7 @@ var init_lock = __esm({
|
|
|
5488
5488
|
});
|
|
5489
5489
|
|
|
5490
5490
|
// src/tui/adapters/process/budget.ts
|
|
5491
|
-
import { readFileSync as
|
|
5491
|
+
import { readFileSync as readFileSync38 } from "fs";
|
|
5492
5492
|
function viewUnit(owner) {
|
|
5493
5493
|
if (!/^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(owner))
|
|
5494
5494
|
throw new Error("invalid_view_owner");
|
|
@@ -5516,7 +5516,7 @@ function startPresentation(owner, args, env) {
|
|
|
5516
5516
|
);
|
|
5517
5517
|
}
|
|
5518
5518
|
function assertPresentationBudget(owner, pid) {
|
|
5519
|
-
if (!
|
|
5519
|
+
if (!readFileSync38(`/proc/${pid}/cgroup`, "utf8").includes(
|
|
5520
5520
|
`/${viewUnit(owner)}
|
|
5521
5521
|
`
|
|
5522
5522
|
))
|
|
@@ -5531,7 +5531,7 @@ var init_budget2 = __esm({
|
|
|
5531
5531
|
|
|
5532
5532
|
// src/tui/adapters/tmux/connection.ts
|
|
5533
5533
|
import { spawn as spawn3 } from "child_process";
|
|
5534
|
-
import { join as
|
|
5534
|
+
import { join as join52 } from "path";
|
|
5535
5535
|
var ViewConnection;
|
|
5536
5536
|
var init_connection2 = __esm({
|
|
5537
5537
|
"src/tui/adapters/tmux/connection.ts"() {
|
|
@@ -5544,7 +5544,7 @@ var init_connection2 = __esm({
|
|
|
5544
5544
|
this.directory = directory;
|
|
5545
5545
|
this.owner = owner;
|
|
5546
5546
|
this.identity = identity;
|
|
5547
|
-
this.socket =
|
|
5547
|
+
this.socket = join52(directory, "s");
|
|
5548
5548
|
}
|
|
5549
5549
|
directory;
|
|
5550
5550
|
owner;
|
|
@@ -5557,14 +5557,14 @@ var init_connection2 = __esm({
|
|
|
5557
5557
|
"-S",
|
|
5558
5558
|
this.socket,
|
|
5559
5559
|
"-f",
|
|
5560
|
-
|
|
5560
|
+
join52(this.directory, "tmux.conf"),
|
|
5561
5561
|
...args
|
|
5562
5562
|
];
|
|
5563
5563
|
if (args[0] === "new-session" && !this.identity)
|
|
5564
5564
|
return startPresentation(this.owner, options2, env).trim();
|
|
5565
5565
|
return command(
|
|
5566
5566
|
"tmux",
|
|
5567
|
-
["-S", this.socket, "-f",
|
|
5567
|
+
["-S", this.socket, "-f", join52(this.directory, "tmux.conf"), ...args],
|
|
5568
5568
|
{ env }
|
|
5569
5569
|
).trim();
|
|
5570
5570
|
}
|
|
@@ -5628,7 +5628,7 @@ var init_connection2 = __esm({
|
|
|
5628
5628
|
});
|
|
5629
5629
|
|
|
5630
5630
|
// src/tui/application/feedback.ts
|
|
5631
|
-
import { join as
|
|
5631
|
+
import { join as join53 } from "path";
|
|
5632
5632
|
function describeFailure(action, error) {
|
|
5633
5633
|
const message = error instanceof Error ? error.message : "view_action_failed";
|
|
5634
5634
|
const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
|
|
@@ -6080,16 +6080,16 @@ var init_common = __esm({
|
|
|
6080
6080
|
});
|
|
6081
6081
|
|
|
6082
6082
|
// src/tui/application/create.ts
|
|
6083
|
-
import { join as
|
|
6084
|
-
import { realpathSync as
|
|
6083
|
+
import { join as join54 } from "path";
|
|
6084
|
+
import { realpathSync as realpathSync17 } from "fs";
|
|
6085
6085
|
async function createView(input) {
|
|
6086
6086
|
const plan2 = planLayout(input.columns, input.rows);
|
|
6087
6087
|
const helpers = runtime();
|
|
6088
6088
|
const mailbox = input.mailbox ? {
|
|
6089
|
-
config:
|
|
6090
|
-
actorFile:
|
|
6089
|
+
config: realpathSync17(input.mailbox.config),
|
|
6090
|
+
actorFile: realpathSync17(input.mailbox.actorFile)
|
|
6091
6091
|
} : void 0;
|
|
6092
|
-
const groupFile =
|
|
6092
|
+
const groupFile = realpathSync17(input.groupFile);
|
|
6093
6093
|
const group = new GroupStore(groupFile).state.group;
|
|
6094
6094
|
const common2 = new CommonHarness({
|
|
6095
6095
|
groupFile,
|
|
@@ -6102,7 +6102,7 @@ async function createView(input) {
|
|
|
6102
6102
|
);
|
|
6103
6103
|
if (!main2) throw new Error("view_requires_live_main");
|
|
6104
6104
|
const { directory, owner } = createDirectory();
|
|
6105
|
-
const file2 =
|
|
6105
|
+
const file2 = join54(directory, "view.json");
|
|
6106
6106
|
const connection = new ViewConnection(directory, owner);
|
|
6107
6107
|
const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
|
|
6108
6108
|
const result = connection.raw([
|
|
@@ -6330,7 +6330,7 @@ var init_navigation = __esm({
|
|
|
6330
6330
|
});
|
|
6331
6331
|
|
|
6332
6332
|
// src/tui/application/history.ts
|
|
6333
|
-
import { join as
|
|
6333
|
+
import { join as join55 } from "path";
|
|
6334
6334
|
async function observeHistory(context) {
|
|
6335
6335
|
const { view, renderer, common: common2, file: file2 } = context;
|
|
6336
6336
|
await releaseControl(context);
|
|
@@ -6338,7 +6338,7 @@ async function observeHistory(context) {
|
|
|
6338
6338
|
sessionId: view.target,
|
|
6339
6339
|
input: { lines: 2e3, maxBytes: 65536 }
|
|
6340
6340
|
});
|
|
6341
|
-
privateJson(
|
|
6341
|
+
privateJson(join55(view.directory, "history.json"), {
|
|
6342
6342
|
sessionId: view.target,
|
|
6343
6343
|
content: history.content,
|
|
6344
6344
|
truncated: history.truncated
|
|
@@ -6414,8 +6414,8 @@ var init_mailbox = __esm({
|
|
|
6414
6414
|
});
|
|
6415
6415
|
|
|
6416
6416
|
// src/tui/application/lifecycle.ts
|
|
6417
|
-
import { existsSync as
|
|
6418
|
-
import { join as
|
|
6417
|
+
import { existsSync as existsSync36, unlinkSync as unlinkSync7 } from "fs";
|
|
6418
|
+
import { join as join56 } from "path";
|
|
6419
6419
|
function resizeView({ view, renderer, input }) {
|
|
6420
6420
|
renderer.resize(view, planLayout(input.columns, input.rows));
|
|
6421
6421
|
}
|
|
@@ -6430,8 +6430,8 @@ async function stopTarget(context) {
|
|
|
6430
6430
|
async function closeView(context) {
|
|
6431
6431
|
await returnToMain(context);
|
|
6432
6432
|
context.renderer.connection.cleanup();
|
|
6433
|
-
const history =
|
|
6434
|
-
if (
|
|
6433
|
+
const history = join56(context.view.directory, "history.json");
|
|
6434
|
+
if (existsSync36(history)) unlinkSync7(history);
|
|
6435
6435
|
context.view.state = "closed";
|
|
6436
6436
|
saveView(context.file, context.view);
|
|
6437
6437
|
}
|
|
@@ -6688,7 +6688,7 @@ var init_worker_start = __esm({
|
|
|
6688
6688
|
});
|
|
6689
6689
|
|
|
6690
6690
|
// src/tui/startup/config.ts
|
|
6691
|
-
import { realpathSync as
|
|
6691
|
+
import { realpathSync as realpathSync18 } from "fs";
|
|
6692
6692
|
import { resolve as resolve19 } from "path";
|
|
6693
6693
|
function startupSettings(request3) {
|
|
6694
6694
|
const path = request3.config ? resolve19(request3.cwd, request3.config) : findConfig(request3.cwd);
|
|
@@ -6741,7 +6741,7 @@ function startupSettings(request3) {
|
|
|
6741
6741
|
fail2("session memory exceeds the group budget");
|
|
6742
6742
|
const groupFile = string("group_file");
|
|
6743
6743
|
return {
|
|
6744
|
-
projectRoot:
|
|
6744
|
+
projectRoot: realpathSync18(config?.dir ?? request3.cwd),
|
|
6745
6745
|
...config ? { configFile: config.path } : {},
|
|
6746
6746
|
runtime: runtime2,
|
|
6747
6747
|
allowedRuntimes: [.../* @__PURE__ */ new Set([...available, runtime2])],
|
|
@@ -6770,16 +6770,16 @@ var init_config3 = __esm({
|
|
|
6770
6770
|
|
|
6771
6771
|
// src/tui/startup/state.ts
|
|
6772
6772
|
import { createHash as createHash19 } from "crypto";
|
|
6773
|
-
import { existsSync as
|
|
6773
|
+
import { existsSync as existsSync37, lstatSync as lstatSync13, mkdirSync as mkdirSync12 } from "fs";
|
|
6774
6774
|
import { homedir as homedir11 } from "os";
|
|
6775
|
-
import { dirname as dirname26, join as
|
|
6775
|
+
import { dirname as dirname26, join as join57 } from "path";
|
|
6776
6776
|
function startupFile(projectRoot) {
|
|
6777
|
-
const root =
|
|
6778
|
-
process.env.XDG_STATE_HOME ??
|
|
6777
|
+
const root = join57(
|
|
6778
|
+
process.env.XDG_STATE_HOME ?? join57(homedir11(), ".local/state"),
|
|
6779
6779
|
"pmcp/launcher"
|
|
6780
6780
|
);
|
|
6781
6781
|
const key = createHash19("sha256").update(projectRoot).digest("hex");
|
|
6782
|
-
return
|
|
6782
|
+
return join57(root, `${key}.json`);
|
|
6783
6783
|
}
|
|
6784
6784
|
function ensureStartupDirectory(file2) {
|
|
6785
6785
|
const directory = dirname26(file2);
|
|
@@ -6789,7 +6789,7 @@ function ensureStartupDirectory(file2) {
|
|
|
6789
6789
|
throw new Error("unsafe_startup_directory");
|
|
6790
6790
|
}
|
|
6791
6791
|
function readStartup(file2, projectRoot) {
|
|
6792
|
-
if (!
|
|
6792
|
+
if (!existsSync37(file2)) return null;
|
|
6793
6793
|
const value = object8(readPrivateJson2(file2));
|
|
6794
6794
|
if (value.schemaVersion !== 1 || value.projectRoot !== projectRoot)
|
|
6795
6795
|
throw new Error("foreign_startup_state");
|
|
@@ -7009,14 +7009,14 @@ var init_host = __esm({
|
|
|
7009
7009
|
});
|
|
7010
7010
|
|
|
7011
7011
|
// src/tui/startup/lock.ts
|
|
7012
|
-
import { spawnSync as
|
|
7013
|
-
import { closeSync as
|
|
7012
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
7013
|
+
import { closeSync as closeSync9, constants as constants7, fstatSync as fstatSync5, openSync as openSync9 } from "fs";
|
|
7014
7014
|
function serializedStartup(request3) {
|
|
7015
7015
|
const settings2 = startupSettings(request3);
|
|
7016
7016
|
const file2 = startupFile(settings2.projectRoot);
|
|
7017
7017
|
ensureStartupDirectory(file2);
|
|
7018
7018
|
const lock = `${file2}.lock`;
|
|
7019
|
-
const descriptor =
|
|
7019
|
+
const descriptor = openSync9(
|
|
7020
7020
|
lock,
|
|
7021
7021
|
constants7.O_CREAT | constants7.O_WRONLY | constants7.O_NOFOLLOW,
|
|
7022
7022
|
384
|
|
@@ -7026,11 +7026,11 @@ function serializedStartup(request3) {
|
|
|
7026
7026
|
if (!stat.isFile() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
7027
7027
|
throw new Error("unsafe_startup_lock");
|
|
7028
7028
|
} finally {
|
|
7029
|
-
|
|
7029
|
+
closeSync9(descriptor);
|
|
7030
7030
|
}
|
|
7031
7031
|
const helpers = runtime();
|
|
7032
7032
|
const encoded = Buffer.from(JSON.stringify(request3)).toString("base64url");
|
|
7033
|
-
const child =
|
|
7033
|
+
const child = spawnSync7(
|
|
7034
7034
|
"flock",
|
|
7035
7035
|
[
|
|
7036
7036
|
"--wait",
|
|
@@ -9859,7 +9859,7 @@ function registerMailboxTools(server, service) {
|
|
|
9859
9859
|
|
|
9860
9860
|
// src/mcp.ts
|
|
9861
9861
|
var SERVER_NAME = "pmcp";
|
|
9862
|
-
var SERVER_VERSION = "0.
|
|
9862
|
+
var SERVER_VERSION = "0.17.1";
|
|
9863
9863
|
function registerSkillTools(server, tools) {
|
|
9864
9864
|
registerCatalogTool(server, tools);
|
|
9865
9865
|
registerFindTool(server, tools);
|
|
@@ -12002,7 +12002,7 @@ import { homedir as homedir6 } from "os";
|
|
|
12002
12002
|
// src/project/apply.ts
|
|
12003
12003
|
import { createHash as createHash13 } from "crypto";
|
|
12004
12004
|
import {
|
|
12005
|
-
existsSync as
|
|
12005
|
+
existsSync as existsSync21,
|
|
12006
12006
|
lstatSync as lstatSync5,
|
|
12007
12007
|
mkdirSync as mkdirSync7,
|
|
12008
12008
|
readFileSync as readFileSync25,
|
|
@@ -12010,7 +12010,7 @@ import {
|
|
|
12010
12010
|
readlinkSync as readlinkSync2,
|
|
12011
12011
|
rmSync as rmSync5
|
|
12012
12012
|
} from "fs";
|
|
12013
|
-
import { dirname as dirname18, join as
|
|
12013
|
+
import { dirname as dirname18, join as join25, posix as posix2 } from "path";
|
|
12014
12014
|
import { parse as parse5 } from "smol-toml";
|
|
12015
12015
|
|
|
12016
12016
|
// src/project/backup.ts
|
|
@@ -12202,6 +12202,56 @@ function splitKeyed(path) {
|
|
|
12202
12202
|
|
|
12203
12203
|
// src/project/mcp.ts
|
|
12204
12204
|
import { isAbsolute as isAbsolute5, relative as relative9 } from "path";
|
|
12205
|
+
|
|
12206
|
+
// src/project/adapters/opencode.ts
|
|
12207
|
+
import { existsSync as existsSync17 } from "fs";
|
|
12208
|
+
import { join as join22 } from "path";
|
|
12209
|
+
var OPENCODE_CONFIG = "opencode.json";
|
|
12210
|
+
function checkOpenCodeConfig(root) {
|
|
12211
|
+
if (existsSync17(join22(root, "opencode.jsonc"))) {
|
|
12212
|
+
throw new ProjectError(
|
|
12213
|
+
"OpenCode JSONC projection is not supported; reconcile opencode.jsonc before projecting opencode.json"
|
|
12214
|
+
);
|
|
12215
|
+
}
|
|
12216
|
+
}
|
|
12217
|
+
function opencodeMcpEntry(server, started) {
|
|
12218
|
+
if (server.tools !== void 0) {
|
|
12219
|
+
throw new ProjectError(
|
|
12220
|
+
"OpenCode per-server tool filtering is not qualified; limit this server's targets"
|
|
12221
|
+
);
|
|
12222
|
+
}
|
|
12223
|
+
const timeout = server.native?.opencode;
|
|
12224
|
+
if (server.url !== void 0) {
|
|
12225
|
+
const headers = { ...server.headers };
|
|
12226
|
+
for (const [key, name] of Object.entries(server.headerEnv ?? {})) {
|
|
12227
|
+
headers[key] = `{env:${name}}`;
|
|
12228
|
+
}
|
|
12229
|
+
if (server.bearerTokenEnvVar) {
|
|
12230
|
+
headers["Authorization"] = `Bearer {env:${server.bearerTokenEnvVar}}`;
|
|
12231
|
+
}
|
|
12232
|
+
return {
|
|
12233
|
+
type: "remote",
|
|
12234
|
+
url: server.url,
|
|
12235
|
+
enabled: true,
|
|
12236
|
+
...Object.keys(headers).length > 0 ? { headers } : {},
|
|
12237
|
+
...timeout
|
|
12238
|
+
};
|
|
12239
|
+
}
|
|
12240
|
+
if (!started) {
|
|
12241
|
+
throw new ProjectError(
|
|
12242
|
+
"OpenCode local MCP projection requires a launch specification"
|
|
12243
|
+
);
|
|
12244
|
+
}
|
|
12245
|
+
return {
|
|
12246
|
+
type: "local",
|
|
12247
|
+
command: [started.command, ...started.args],
|
|
12248
|
+
enabled: true,
|
|
12249
|
+
...Object.keys(started.env).length > 0 ? { environment: { ...started.env } } : {},
|
|
12250
|
+
...timeout
|
|
12251
|
+
};
|
|
12252
|
+
}
|
|
12253
|
+
|
|
12254
|
+
// src/project/mcp.ts
|
|
12205
12255
|
var runtimeByTool = {
|
|
12206
12256
|
claude: "claude-code",
|
|
12207
12257
|
codex: "codex-cli",
|
|
@@ -12209,6 +12259,15 @@ var runtimeByTool = {
|
|
|
12209
12259
|
grok: "grok-cli",
|
|
12210
12260
|
antigravity: "antigravity"
|
|
12211
12261
|
};
|
|
12262
|
+
function mailboxRuntime(tool) {
|
|
12263
|
+
const runtime2 = runtimeByTool[tool];
|
|
12264
|
+
if (!runtime2) {
|
|
12265
|
+
throw new ProjectError(
|
|
12266
|
+
"OpenCode mailbox runtime binding is not qualified; limit this server's targets"
|
|
12267
|
+
);
|
|
12268
|
+
}
|
|
12269
|
+
return runtime2;
|
|
12270
|
+
}
|
|
12212
12271
|
var ROOT_VAR = "${PROJECT_ROOT}";
|
|
12213
12272
|
var ANTIGRAVITY_MCP_PLUGIN = ".agents/plugins/pmcp-mcp";
|
|
12214
12273
|
var VARIABLE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/gu;
|
|
@@ -12344,18 +12403,19 @@ function httpEntry(server, tool) {
|
|
|
12344
12403
|
function nativeEntry(spec, alias, server, tool) {
|
|
12345
12404
|
let result;
|
|
12346
12405
|
if (server.url !== void 0) {
|
|
12406
|
+
if (tool === "opencode") return opencodeMcpEntry(server);
|
|
12347
12407
|
result = httpEntry(server, tool);
|
|
12348
12408
|
} else {
|
|
12349
12409
|
const env = { ...server.env };
|
|
12350
12410
|
if (server.mailbox) {
|
|
12351
|
-
env["PMCP_MAILBOX_RUNTIME"] =
|
|
12411
|
+
env["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
|
|
12352
12412
|
}
|
|
12353
12413
|
let started;
|
|
12354
12414
|
if (Object.keys(server.envFrom ?? {}).length > 0) {
|
|
12355
12415
|
const entry2 = relative9(spec.root, process.argv[1] ?? "");
|
|
12356
12416
|
const driverEnv = {};
|
|
12357
12417
|
if (server.mailbox) {
|
|
12358
|
-
driverEnv["PMCP_MAILBOX_RUNTIME"] =
|
|
12418
|
+
driverEnv["PMCP_MAILBOX_RUNTIME"] = mailboxRuntime(tool);
|
|
12359
12419
|
}
|
|
12360
12420
|
if (!isAbsolute5(entry2) && !entry2.startsWith("..")) {
|
|
12361
12421
|
started = launch({
|
|
@@ -12373,6 +12433,7 @@ function nativeEntry(spec, alias, server, tool) {
|
|
|
12373
12433
|
} else {
|
|
12374
12434
|
started = launch({ ...server, env });
|
|
12375
12435
|
}
|
|
12436
|
+
if (tool === "opencode") return opencodeMcpEntry(server, started);
|
|
12376
12437
|
result = { command: started.command, args: [...started.args] };
|
|
12377
12438
|
if (Object.keys(started.env).length > 0) {
|
|
12378
12439
|
result["env"] = { ...started.env };
|
|
@@ -12429,9 +12490,11 @@ function mcpOutputs(spec) {
|
|
|
12429
12490
|
const jsonFiles = {
|
|
12430
12491
|
claude: ".mcp.json",
|
|
12431
12492
|
gemini: ".gemini/settings.json",
|
|
12432
|
-
antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json
|
|
12493
|
+
antigravity: `${ANTIGRAVITY_MCP_PLUGIN}/mcp_config.json`,
|
|
12494
|
+
opencode: OPENCODE_CONFIG
|
|
12433
12495
|
};
|
|
12434
12496
|
for (const tool of spec.tools) {
|
|
12497
|
+
if (tool === "opencode") checkOpenCodeConfig(spec.root);
|
|
12435
12498
|
const servers = serversForTool(spec, tool);
|
|
12436
12499
|
if (servers.length === 0) {
|
|
12437
12500
|
continue;
|
|
@@ -12450,7 +12513,7 @@ function mcpOutputs(spec) {
|
|
|
12450
12513
|
outputs.push(
|
|
12451
12514
|
jsonOutput(
|
|
12452
12515
|
file2,
|
|
12453
|
-
["mcpServers", alias],
|
|
12516
|
+
[tool === "opencode" ? "mcp" : "mcpServers", alias],
|
|
12454
12517
|
nativeEntry(spec, alias, server, tool)
|
|
12455
12518
|
)
|
|
12456
12519
|
);
|
|
@@ -12466,14 +12529,14 @@ function mcpOutputs(spec) {
|
|
|
12466
12529
|
|
|
12467
12530
|
// src/project/hooks.ts
|
|
12468
12531
|
import { createHash as createHash12 } from "crypto";
|
|
12469
|
-
import { existsSync as
|
|
12470
|
-
import { join as
|
|
12532
|
+
import { existsSync as existsSync19, readFileSync as readFileSync23 } from "fs";
|
|
12533
|
+
import { join as join23 } from "path";
|
|
12471
12534
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12472
12535
|
|
|
12473
12536
|
// src/project/hook-adapter.ts
|
|
12474
12537
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12475
12538
|
import { createHash as createHash11 } from "crypto";
|
|
12476
|
-
import { existsSync as
|
|
12539
|
+
import { existsSync as existsSync18, readFileSync as readFileSync22, realpathSync as realpathSync5, statSync as statSync10 } from "fs";
|
|
12477
12540
|
import { dirname as dirname16, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve12 } from "path";
|
|
12478
12541
|
var denied = () => ({
|
|
12479
12542
|
decision: "deny",
|
|
@@ -12490,7 +12553,7 @@ function guardPath(root, command3) {
|
|
|
12490
12553
|
if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
|
|
12491
12554
|
throw new Error("Hook guard must stay under .agents/guards/");
|
|
12492
12555
|
}
|
|
12493
|
-
if (!
|
|
12556
|
+
if (!existsSync18(absolute2) || !statSync10(absolute2).isFile()) {
|
|
12494
12557
|
throw new Error("Hook guard script must exist");
|
|
12495
12558
|
}
|
|
12496
12559
|
const actual = relative10(base, realpathSync5(absolute2));
|
|
@@ -12685,8 +12748,8 @@ function object4(value) {
|
|
|
12685
12748
|
return value;
|
|
12686
12749
|
}
|
|
12687
12750
|
function groups(root, file2, key) {
|
|
12688
|
-
const absolute2 =
|
|
12689
|
-
if (!
|
|
12751
|
+
const absolute2 = join23(root, file2);
|
|
12752
|
+
if (!existsSync19(absolute2)) {
|
|
12690
12753
|
return [];
|
|
12691
12754
|
}
|
|
12692
12755
|
let value;
|
|
@@ -12715,15 +12778,15 @@ function adapterSource() {
|
|
|
12715
12778
|
const sourceBuild = fileURLToPath2(
|
|
12716
12779
|
new URL("../../dist/project/hook-adapter.js", import.meta.url)
|
|
12717
12780
|
);
|
|
12718
|
-
const file2 =
|
|
12719
|
-
if (!
|
|
12781
|
+
const file2 = existsSync19(published) ? published : sourceBuild;
|
|
12782
|
+
if (!existsSync19(file2)) {
|
|
12720
12783
|
throw new ProjectError("Build pmcp before projecting shell hook adapters");
|
|
12721
12784
|
}
|
|
12722
12785
|
return readFileSync23(file2, "utf8");
|
|
12723
12786
|
}
|
|
12724
12787
|
function hookOutputs(spec) {
|
|
12725
|
-
const lockFile =
|
|
12726
|
-
const lock =
|
|
12788
|
+
const lockFile = join23(spec.root, "pmcp.lock");
|
|
12789
|
+
const lock = existsSync19(lockFile) ? JSON.parse(readFileSync23(lockFile, "utf8")) : {};
|
|
12727
12790
|
const previous = new Map(
|
|
12728
12791
|
(lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
|
|
12729
12792
|
);
|
|
@@ -12838,33 +12901,44 @@ ${lines2.join("\n")}
|
|
|
12838
12901
|
}
|
|
12839
12902
|
|
|
12840
12903
|
// src/project/plan.ts
|
|
12841
|
-
import { existsSync as
|
|
12842
|
-
import { join as
|
|
12904
|
+
import { existsSync as existsSync20, readFileSync as readFileSync24, readdirSync as readdirSync8 } from "fs";
|
|
12905
|
+
import { join as join24 } from "path";
|
|
12906
|
+
|
|
12907
|
+
// src/project/adapters/opencode-skills.ts
|
|
12908
|
+
function opencodeSkillOutputs(spec, names) {
|
|
12909
|
+
if (!spec.tools.has("opencode")) return [];
|
|
12910
|
+
return names.filter((name) => spec.skills[name]?.invocation === "user").map(
|
|
12911
|
+
(name) => jsonOutput(OPENCODE_CONFIG, ["permission", "skill", name], "deny")
|
|
12912
|
+
);
|
|
12913
|
+
}
|
|
12914
|
+
|
|
12915
|
+
// src/project/plan.ts
|
|
12843
12916
|
var AGENT_FIELDS = {
|
|
12844
12917
|
claude: ["name", "description", "tools", "model", "effort"],
|
|
12845
12918
|
gemini: ["name", "description"]
|
|
12846
12919
|
};
|
|
12847
12920
|
var SETTINGS = ".claude/settings.json";
|
|
12848
12921
|
function listDirs(dir) {
|
|
12849
|
-
if (!
|
|
12922
|
+
if (!existsSync20(dir)) return [];
|
|
12850
12923
|
return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
12851
12924
|
}
|
|
12852
12925
|
var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
|
|
12853
12926
|
function skillNames(root) {
|
|
12854
|
-
const dir =
|
|
12927
|
+
const dir = join24(root, ".agents/skills");
|
|
12855
12928
|
return listDirs(dir).filter(
|
|
12856
|
-
(name) =>
|
|
12929
|
+
(name) => existsSync20(join24(dir, name, "SKILL.md"))
|
|
12857
12930
|
);
|
|
12858
12931
|
}
|
|
12859
12932
|
function agentNames(root) {
|
|
12860
|
-
const dir =
|
|
12933
|
+
const dir = join24(root, ".agents/agents");
|
|
12861
12934
|
return listDirs(dir).filter(
|
|
12862
|
-
(name) =>
|
|
12935
|
+
(name) => existsSync20(join24(dir, name, "agent.md"))
|
|
12863
12936
|
);
|
|
12864
12937
|
}
|
|
12865
12938
|
function skillOutputs(spec) {
|
|
12866
|
-
const
|
|
12867
|
-
|
|
12939
|
+
const names = skillNames(spec.root);
|
|
12940
|
+
const outputs = opencodeSkillOutputs(spec, names);
|
|
12941
|
+
for (const name of names) {
|
|
12868
12942
|
const target2 = `../../.agents/skills/${name}`;
|
|
12869
12943
|
if (spec.tools.has("claude")) {
|
|
12870
12944
|
outputs.push({ kind: "link", path: `.claude/skills/${name}`, target: target2 });
|
|
@@ -12893,13 +12967,13 @@ policy:
|
|
|
12893
12967
|
}
|
|
12894
12968
|
function ruleOutputs(spec) {
|
|
12895
12969
|
if (!spec.tools.has("claude")) return [];
|
|
12896
|
-
const dir =
|
|
12897
|
-
if (!
|
|
12970
|
+
const dir = join24(spec.root, ".agents/rules");
|
|
12971
|
+
if (!existsSync20(dir)) return [];
|
|
12898
12972
|
const outputs = [];
|
|
12899
12973
|
for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
|
|
12900
12974
|
const rel = `.agents/rules/${file2}`;
|
|
12901
12975
|
const { meta, body } = splitFrontmatter(
|
|
12902
|
-
readFileSync24(
|
|
12976
|
+
readFileSync24(join24(spec.root, rel), "utf8")
|
|
12903
12977
|
);
|
|
12904
12978
|
const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
|
|
12905
12979
|
const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
|
|
@@ -12918,7 +12992,7 @@ function agentOutputs(spec) {
|
|
|
12918
12992
|
for (const name of agentNames(spec.root)) {
|
|
12919
12993
|
const rel = `.agents/agents/${name}/agent.md`;
|
|
12920
12994
|
const { meta, body } = splitFrontmatter(
|
|
12921
|
-
readFileSync24(
|
|
12995
|
+
readFileSync24(join24(spec.root, rel), "utf8")
|
|
12922
12996
|
);
|
|
12923
12997
|
const overrides = spec.agents[name] ?? {};
|
|
12924
12998
|
const note = `<!-- ${MARK} from ${rel} -->
|
|
@@ -13058,8 +13132,8 @@ var PREDECESSORS = [
|
|
|
13058
13132
|
];
|
|
13059
13133
|
var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
|
|
13060
13134
|
function readLock(root) {
|
|
13061
|
-
const path =
|
|
13062
|
-
if (!
|
|
13135
|
+
const path = join25(root, LOCK_FILE);
|
|
13136
|
+
if (!existsSync21(path)) return [];
|
|
13063
13137
|
const lock = JSON.parse(readFileSync25(path, "utf8"));
|
|
13064
13138
|
return lock.outputs ?? [];
|
|
13065
13139
|
}
|
|
@@ -13070,9 +13144,9 @@ function linkOf(abs) {
|
|
|
13070
13144
|
return null;
|
|
13071
13145
|
}
|
|
13072
13146
|
}
|
|
13073
|
-
var exists = (abs) =>
|
|
13147
|
+
var exists = (abs) => existsSync21(abs) || linkOf(abs) !== null;
|
|
13074
13148
|
function readJson2(abs) {
|
|
13075
|
-
if (!
|
|
13149
|
+
if (!existsSync21(abs)) return {};
|
|
13076
13150
|
let value;
|
|
13077
13151
|
try {
|
|
13078
13152
|
value = JSON.parse(readFileSync25(abs, "utf8"));
|
|
@@ -13127,32 +13201,44 @@ function withoutBlock(text3) {
|
|
|
13127
13201
|
return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
|
|
13128
13202
|
}
|
|
13129
13203
|
function present(root, output) {
|
|
13130
|
-
const abs =
|
|
13204
|
+
const abs = join25(root, "file" in output ? output.file : output.path);
|
|
13131
13205
|
switch (output.kind) {
|
|
13132
13206
|
case "link":
|
|
13133
13207
|
return linkOf(abs) === output.target;
|
|
13134
13208
|
case "file":
|
|
13135
|
-
return
|
|
13209
|
+
return existsSync21(abs) && linkOf(abs) === null && readFileSync25(abs, "utf8") === output.content;
|
|
13136
13210
|
case "json":
|
|
13137
13211
|
if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
|
|
13138
13212
|
return getKey(readJson2(abs), output.key) === void 0;
|
|
13139
13213
|
}
|
|
13140
13214
|
return same(getKey(readJson2(abs), output.key), output.value);
|
|
13141
13215
|
case "block":
|
|
13142
|
-
return
|
|
13216
|
+
return existsSync21(abs) && readFileSync25(abs, "utf8").includes(block(output.content));
|
|
13143
13217
|
}
|
|
13144
13218
|
}
|
|
13145
13219
|
function replaceable(root, output, owned) {
|
|
13146
13220
|
if (output.kind === "json") {
|
|
13147
|
-
if (output.
|
|
13221
|
+
if (output.file === "opencode.json") {
|
|
13222
|
+
let parent = readJson2(join25(root, output.file));
|
|
13223
|
+
for (const part of output.key.slice(0, -1)) {
|
|
13224
|
+
if (parent === void 0) break;
|
|
13225
|
+
if (parent === null || typeof parent !== "object" || Array.isArray(parent))
|
|
13226
|
+
return false;
|
|
13227
|
+
parent = parent[part];
|
|
13228
|
+
}
|
|
13229
|
+
if (parent !== void 0 && (parent === null || typeof parent !== "object" || Array.isArray(parent)))
|
|
13230
|
+
return false;
|
|
13231
|
+
}
|
|
13232
|
+
const guarded2 = output.key[0] === "mcpServers" || output.file === "opencode.json";
|
|
13233
|
+
if (!guarded2 || owned.has(output.path)) {
|
|
13148
13234
|
return true;
|
|
13149
13235
|
}
|
|
13150
|
-
const existing = getKey(readJson2(
|
|
13236
|
+
const existing = getKey(readJson2(join25(root, output.file)), output.key);
|
|
13151
13237
|
return existing === void 0 || same(existing, output.value);
|
|
13152
13238
|
}
|
|
13153
13239
|
if (output.kind === "block") {
|
|
13154
|
-
const absolute2 =
|
|
13155
|
-
const text4 =
|
|
13240
|
+
const absolute2 = join25(root, output.file);
|
|
13241
|
+
const text4 = existsSync21(absolute2) ? readFileSync25(absolute2, "utf8") : "";
|
|
13156
13242
|
const outside = parse5(withoutBlock(text4));
|
|
13157
13243
|
const proposed = parse5(output.content);
|
|
13158
13244
|
const existing = objectKeys(outside["mcp_servers"]);
|
|
@@ -13161,7 +13247,7 @@ function replaceable(root, output, owned) {
|
|
|
13161
13247
|
);
|
|
13162
13248
|
}
|
|
13163
13249
|
if (owned.has(output.path)) return true;
|
|
13164
|
-
const abs =
|
|
13250
|
+
const abs = join25(root, output.path);
|
|
13165
13251
|
if (!exists(abs)) return true;
|
|
13166
13252
|
const target2 = linkOf(abs);
|
|
13167
13253
|
if (target2 !== null) {
|
|
@@ -13182,15 +13268,15 @@ function undeclared(spec, outputs) {
|
|
|
13182
13268
|
const declared = new Set(outputs.map((o) => o.path));
|
|
13183
13269
|
const found = [];
|
|
13184
13270
|
for (const dir of censusDirs(spec)) {
|
|
13185
|
-
const abs =
|
|
13186
|
-
if (!
|
|
13271
|
+
const abs = join25(spec.root, dir);
|
|
13272
|
+
if (!existsSync21(abs)) continue;
|
|
13187
13273
|
for (const entry2 of readdirSync9(abs)) {
|
|
13188
13274
|
const path = `${dir}/${entry2}`;
|
|
13189
13275
|
if (!declared.has(path)) found.push(path);
|
|
13190
13276
|
}
|
|
13191
13277
|
}
|
|
13192
|
-
const rules =
|
|
13193
|
-
if (spec.tools.has("grok") &&
|
|
13278
|
+
const rules = join25(spec.root, RULES_DIR);
|
|
13279
|
+
if (spec.tools.has("grok") && existsSync21(rules)) {
|
|
13194
13280
|
for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
|
|
13195
13281
|
if (entry2.isDirectory()) continue;
|
|
13196
13282
|
found.push(`${RULES_DIR}/${entry2.name}`);
|
|
@@ -13217,8 +13303,8 @@ function deadGlobs(root, outputs) {
|
|
|
13217
13303
|
if (rule.kind === "file") {
|
|
13218
13304
|
text3 = rule.content;
|
|
13219
13305
|
} else if (rule.kind === "link") {
|
|
13220
|
-
const abs =
|
|
13221
|
-
if (!
|
|
13306
|
+
const abs = join25(root, posix2.join(posix2.dirname(rule.path), rule.target));
|
|
13307
|
+
if (!existsSync21(abs)) continue;
|
|
13222
13308
|
text3 = readFileSync25(abs, "utf8");
|
|
13223
13309
|
} else {
|
|
13224
13310
|
continue;
|
|
@@ -13269,7 +13355,7 @@ function project(spec, options2) {
|
|
|
13269
13355
|
}
|
|
13270
13356
|
}
|
|
13271
13357
|
for (const path of jsonPaths) {
|
|
13272
|
-
readJson2(
|
|
13358
|
+
readJson2(join25(spec.root, path));
|
|
13273
13359
|
}
|
|
13274
13360
|
if (options2.check) {
|
|
13275
13361
|
const problems = [
|
|
@@ -13290,6 +13376,7 @@ function project(spec, options2) {
|
|
|
13290
13376
|
return { code: 3, problems: refused, outputs: outputs.length, pruned: 0 };
|
|
13291
13377
|
}
|
|
13292
13378
|
const mcpPaths = [
|
|
13379
|
+
"opencode.json",
|
|
13293
13380
|
".mcp.json",
|
|
13294
13381
|
".gemini/settings.json",
|
|
13295
13382
|
".agents/mcp_config.json",
|
|
@@ -13331,7 +13418,7 @@ function project(spec, options2) {
|
|
|
13331
13418
|
for (const output of outputs) {
|
|
13332
13419
|
if (output.kind !== "link" && output.kind !== "file") continue;
|
|
13333
13420
|
if (present(spec.root, output)) continue;
|
|
13334
|
-
const abs =
|
|
13421
|
+
const abs = join25(spec.root, output.path);
|
|
13335
13422
|
mkdirSync7(dirname18(abs), { recursive: true });
|
|
13336
13423
|
if (output.kind === "link") {
|
|
13337
13424
|
replaceLink(abs, output.target);
|
|
@@ -13359,12 +13446,12 @@ function project(spec, options2) {
|
|
|
13359
13446
|
group.drops.push(keyed.key);
|
|
13360
13447
|
jsonFiles.set(keyed.file, group);
|
|
13361
13448
|
} else {
|
|
13362
|
-
rmSync5(
|
|
13449
|
+
rmSync5(join25(spec.root, entry2.path), { force: true, recursive: true });
|
|
13363
13450
|
}
|
|
13364
13451
|
pruned += 1;
|
|
13365
13452
|
}
|
|
13366
13453
|
for (const [file2, { sets, drops }] of jsonFiles) {
|
|
13367
|
-
const abs =
|
|
13454
|
+
const abs = join25(spec.root, file2);
|
|
13368
13455
|
const before = readJson2(abs);
|
|
13369
13456
|
const after = structuredClone(before);
|
|
13370
13457
|
for (const key of drops) deleteKey(after, key);
|
|
@@ -13379,14 +13466,14 @@ function project(spec, options2) {
|
|
|
13379
13466
|
}
|
|
13380
13467
|
}
|
|
13381
13468
|
if (same(before, after)) continue;
|
|
13382
|
-
if (!
|
|
13469
|
+
if (!existsSync21(abs) && Object.keys(after).length === 0) continue;
|
|
13383
13470
|
mkdirSync7(dirname18(abs), { recursive: true });
|
|
13384
13471
|
replaceFile(abs, `${JSON.stringify(after, null, 2)}
|
|
13385
13472
|
`);
|
|
13386
13473
|
}
|
|
13387
13474
|
for (const [file2, content] of blockFiles) {
|
|
13388
|
-
const abs =
|
|
13389
|
-
const before =
|
|
13475
|
+
const abs = join25(spec.root, file2);
|
|
13476
|
+
const before = existsSync21(abs) ? readFileSync25(abs, "utf8") : "";
|
|
13390
13477
|
let after = withoutBlock(before);
|
|
13391
13478
|
if (content !== null) {
|
|
13392
13479
|
const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
|
|
@@ -13405,15 +13492,15 @@ function project(spec, options2) {
|
|
|
13405
13492
|
)
|
|
13406
13493
|
);
|
|
13407
13494
|
for (const [name, target2] of legacyLinks(spec.root)) {
|
|
13408
|
-
const abs =
|
|
13409
|
-
const broken = !
|
|
13495
|
+
const abs = join25(spec.root, RULES_DIR, `${name}.md`);
|
|
13496
|
+
const broken = !existsSync21(abs);
|
|
13410
13497
|
if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
|
|
13411
13498
|
rmSync5(abs, { force: true });
|
|
13412
13499
|
pruned += 1;
|
|
13413
13500
|
}
|
|
13414
13501
|
}
|
|
13415
13502
|
replaceFile(
|
|
13416
|
-
|
|
13503
|
+
join25(spec.root, LOCK_FILE),
|
|
13417
13504
|
`${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
|
|
13418
13505
|
`
|
|
13419
13506
|
);
|
|
@@ -13510,7 +13597,8 @@ var TOOLS = [
|
|
|
13510
13597
|
"codex",
|
|
13511
13598
|
"gemini",
|
|
13512
13599
|
"grok",
|
|
13513
|
-
"antigravity"
|
|
13600
|
+
"antigravity",
|
|
13601
|
+
"opencode"
|
|
13514
13602
|
];
|
|
13515
13603
|
var NAME = /^[a-z0-9][a-z0-9-]*$/u;
|
|
13516
13604
|
function table(path, where, value) {
|
|
@@ -13666,7 +13754,8 @@ function nativeOptions(path, where, value) {
|
|
|
13666
13754
|
codex: ["startup_timeout_sec", "tool_timeout_sec"],
|
|
13667
13755
|
gemini: ["timeout"],
|
|
13668
13756
|
grok: ["startup_timeout_sec", "tool_timeout_sec"],
|
|
13669
|
-
antigravity: []
|
|
13757
|
+
antigravity: [],
|
|
13758
|
+
opencode: ["timeout"]
|
|
13670
13759
|
};
|
|
13671
13760
|
const result = {};
|
|
13672
13761
|
for (const [tool, raw] of Object.entries(
|
|
@@ -13860,6 +13949,11 @@ function sourceServers(source2, root) {
|
|
|
13860
13949
|
);
|
|
13861
13950
|
}
|
|
13862
13951
|
const path = resolve13(root, source2.slice(split + 1));
|
|
13952
|
+
if (tool === "opencode") {
|
|
13953
|
+
throw new ProjectError(
|
|
13954
|
+
"OpenCode MCP import is not qualified; define the server in pmcp.toml"
|
|
13955
|
+
);
|
|
13956
|
+
}
|
|
13863
13957
|
if (statSync11(path).size > 2 * 1024 * 1024) {
|
|
13864
13958
|
throw new ProjectError("MCP source exceeds the 2 MiB limit");
|
|
13865
13959
|
}
|
|
@@ -14223,12 +14317,125 @@ async function execMcp(spec, alias, env) {
|
|
|
14223
14317
|
}
|
|
14224
14318
|
|
|
14225
14319
|
// src/project/doctor.ts
|
|
14226
|
-
import { spawnSync as
|
|
14227
|
-
import { existsSync as
|
|
14228
|
-
import { join as
|
|
14320
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
14321
|
+
import { existsSync as existsSync22, readFileSync as readFileSync29 } from "fs";
|
|
14322
|
+
import { join as join28 } from "path";
|
|
14229
14323
|
import { parse as parse8 } from "smol-toml";
|
|
14324
|
+
|
|
14325
|
+
// src/project/adapters/opencode-doctor.ts
|
|
14326
|
+
import { realpathSync as realpathSync6 } from "fs";
|
|
14327
|
+
import { join as join26 } from "path";
|
|
14328
|
+
function isOpenCodeInventory(command3, args) {
|
|
14329
|
+
return command3 === "opencode" && args.length === 2 && args[0] === "debug" && args[1] === "skill";
|
|
14330
|
+
}
|
|
14331
|
+
function connected(out, alias) {
|
|
14332
|
+
const ansi = new RegExp(
|
|
14333
|
+
`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`,
|
|
14334
|
+
"gu"
|
|
14335
|
+
);
|
|
14336
|
+
const pattern = new RegExp(
|
|
14337
|
+
`^[^a-zA-Z0-9]*${alias}\\s+connected(?:\\s|$)`,
|
|
14338
|
+
"u"
|
|
14339
|
+
);
|
|
14340
|
+
return out.replace(ansi, "").split("\n").some((line) => pattern.test(line.trim()));
|
|
14341
|
+
}
|
|
14342
|
+
function projectSkill(out, root, name) {
|
|
14343
|
+
try {
|
|
14344
|
+
const entries = JSON.parse(out);
|
|
14345
|
+
if (!Array.isArray(entries)) return false;
|
|
14346
|
+
const expected = realpathSync6(
|
|
14347
|
+
join26(root, ".agents/skills", name, "SKILL.md")
|
|
14348
|
+
);
|
|
14349
|
+
return entries.some(
|
|
14350
|
+
(entry2) => entry2?.name === name && typeof entry2.location === "string" && realpathSync6(entry2.location) === expected
|
|
14351
|
+
);
|
|
14352
|
+
} catch {
|
|
14353
|
+
return false;
|
|
14354
|
+
}
|
|
14355
|
+
}
|
|
14356
|
+
function opencodeDoctor(spec, options2, listed2) {
|
|
14357
|
+
const findings = [];
|
|
14358
|
+
const names = skillNames(spec.root);
|
|
14359
|
+
if (names.length > 0) {
|
|
14360
|
+
findings.push(
|
|
14361
|
+
listed2(
|
|
14362
|
+
"opencode",
|
|
14363
|
+
"project skills discovered",
|
|
14364
|
+
options2.run("opencode", ["debug", "skill"], spec.root, options2.env),
|
|
14365
|
+
names,
|
|
14366
|
+
(out, name) => projectSkill(out, spec.root, name)
|
|
14367
|
+
)
|
|
14368
|
+
);
|
|
14369
|
+
}
|
|
14370
|
+
const aliases = serversForTool(spec, "opencode").map(([alias]) => alias);
|
|
14371
|
+
if (aliases.length > 0) {
|
|
14372
|
+
findings.push(
|
|
14373
|
+
listed2(
|
|
14374
|
+
"opencode",
|
|
14375
|
+
"mcp servers connected",
|
|
14376
|
+
options2.run("opencode", ["mcp", "list"], spec.root, options2.env),
|
|
14377
|
+
aliases,
|
|
14378
|
+
connected
|
|
14379
|
+
)
|
|
14380
|
+
);
|
|
14381
|
+
}
|
|
14382
|
+
return findings;
|
|
14383
|
+
}
|
|
14384
|
+
|
|
14385
|
+
// src/project/diagnostics/native-json.ts
|
|
14386
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
14387
|
+
import {
|
|
14388
|
+
closeSync as closeSync4,
|
|
14389
|
+
mkdtempSync as mkdtempSync3,
|
|
14390
|
+
openSync as openSync4,
|
|
14391
|
+
readFileSync as readFileSync28,
|
|
14392
|
+
rmSync as rmSync6,
|
|
14393
|
+
statSync as statSync13
|
|
14394
|
+
} from "fs";
|
|
14395
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
14396
|
+
import { join as join27 } from "path";
|
|
14397
|
+
var MAX_OUTPUT = 1 << 26;
|
|
14398
|
+
var nativeJsonRunner = (command3, args, cwd, env) => {
|
|
14399
|
+
const directory = mkdtempSync3(join27(tmpdir2(), "pmcp-native-json-"));
|
|
14400
|
+
let descriptor;
|
|
14401
|
+
try {
|
|
14402
|
+
const file2 = join27(directory, "stdout.json");
|
|
14403
|
+
descriptor = openSync4(file2, "wx", 384);
|
|
14404
|
+
const result = spawnSync3(command3, [...args], {
|
|
14405
|
+
cwd,
|
|
14406
|
+
...env ? { env: { ...process.env, ...env } } : {},
|
|
14407
|
+
encoding: "utf8",
|
|
14408
|
+
timeout: 18e4,
|
|
14409
|
+
maxBuffer: MAX_OUTPUT,
|
|
14410
|
+
stdio: ["ignore", descriptor, "pipe"]
|
|
14411
|
+
});
|
|
14412
|
+
const code = result.error?.code;
|
|
14413
|
+
if (result.status !== 0 || result.error) {
|
|
14414
|
+
return {
|
|
14415
|
+
missing: code === "ENOENT",
|
|
14416
|
+
status: result.status,
|
|
14417
|
+
stdout: result.stderr || result.error?.message || "Native inventory failed"
|
|
14418
|
+
};
|
|
14419
|
+
}
|
|
14420
|
+
if (statSync13(file2).size > MAX_OUTPUT) {
|
|
14421
|
+
return {
|
|
14422
|
+
missing: false,
|
|
14423
|
+
status: null,
|
|
14424
|
+
stdout: "Native JSON output exceeds 64 MiB"
|
|
14425
|
+
};
|
|
14426
|
+
}
|
|
14427
|
+
return { missing: false, status: 0, stdout: readFileSync28(file2, "utf8") };
|
|
14428
|
+
} finally {
|
|
14429
|
+
if (descriptor !== void 0) closeSync4(descriptor);
|
|
14430
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
14431
|
+
}
|
|
14432
|
+
};
|
|
14433
|
+
|
|
14434
|
+
// src/project/doctor.ts
|
|
14230
14435
|
var spawnRunner = (command3, args, cwd, env) => {
|
|
14231
|
-
|
|
14436
|
+
if (isOpenCodeInventory(command3, args))
|
|
14437
|
+
return nativeJsonRunner(command3, args, cwd, env);
|
|
14438
|
+
const result = spawnSync4(command3, [...args], {
|
|
14232
14439
|
cwd,
|
|
14233
14440
|
...env ? { env: { ...process.env, ...env } } : {},
|
|
14234
14441
|
encoding: "utf8",
|
|
@@ -14302,10 +14509,10 @@ function claude(spec, o) {
|
|
|
14302
14509
|
}
|
|
14303
14510
|
function codex(spec, o) {
|
|
14304
14511
|
const findings = [];
|
|
14305
|
-
const config =
|
|
14512
|
+
const config = join28(o.home, ".codex/config.toml");
|
|
14306
14513
|
let trusted = false;
|
|
14307
|
-
if (
|
|
14308
|
-
const projects = parse8(
|
|
14514
|
+
if (existsSync22(config)) {
|
|
14515
|
+
const projects = parse8(readFileSync29(config, "utf8")).projects ?? {};
|
|
14309
14516
|
const root = spec.root.replace(/\/$/u, "");
|
|
14310
14517
|
trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
|
|
14311
14518
|
}
|
|
@@ -14431,8 +14638,8 @@ function grok(spec, o) {
|
|
|
14431
14638
|
}
|
|
14432
14639
|
function antigravity(spec, options2) {
|
|
14433
14640
|
const foreign = agentNames(spec.root).filter((name) => {
|
|
14434
|
-
const text3 =
|
|
14435
|
-
|
|
14641
|
+
const text3 = readFileSync29(
|
|
14642
|
+
join28(spec.root, ".agents/agents", name, "agent.md"),
|
|
14436
14643
|
"utf8"
|
|
14437
14644
|
);
|
|
14438
14645
|
return splitFrontmatter(text3).meta["tools"] !== void 0;
|
|
@@ -14452,7 +14659,7 @@ function antigravity(spec, options2) {
|
|
|
14452
14659
|
if (serversForTool(spec, "antigravity").length > 0) {
|
|
14453
14660
|
const result = options2.run(
|
|
14454
14661
|
"agy",
|
|
14455
|
-
["plugin", "validate",
|
|
14662
|
+
["plugin", "validate", join28(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
|
|
14456
14663
|
spec.root
|
|
14457
14664
|
);
|
|
14458
14665
|
findings.push(
|
|
@@ -14476,6 +14683,8 @@ function doctor(spec, options2) {
|
|
|
14476
14683
|
if (wanted("codex")) findings.push(...codex(spec, options2));
|
|
14477
14684
|
if (wanted("gemini")) findings.push(...gemini(spec, options2));
|
|
14478
14685
|
if (wanted("grok")) findings.push(...grok(spec, options2));
|
|
14686
|
+
if (wanted("opencode"))
|
|
14687
|
+
findings.push(...opencodeDoctor(spec, options2, listed));
|
|
14479
14688
|
if (wanted("antigravity")) {
|
|
14480
14689
|
findings.push(...antigravity(spec, options2));
|
|
14481
14690
|
}
|
|
@@ -14483,8 +14692,8 @@ function doctor(spec, options2) {
|
|
|
14483
14692
|
}
|
|
14484
14693
|
|
|
14485
14694
|
// src/project/memory.ts
|
|
14486
|
-
import { existsSync as
|
|
14487
|
-
import { isAbsolute as isAbsolute7, join as
|
|
14695
|
+
import { existsSync as existsSync23, readFileSync as readFileSync30 } from "fs";
|
|
14696
|
+
import { isAbsolute as isAbsolute7, join as join29, resolve as resolve15 } from "path";
|
|
14488
14697
|
import { parse as parse9 } from "smol-toml";
|
|
14489
14698
|
function object7(value) {
|
|
14490
14699
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -14493,10 +14702,10 @@ function object7(value) {
|
|
|
14493
14702
|
return value;
|
|
14494
14703
|
}
|
|
14495
14704
|
function settings(file2, toml = false) {
|
|
14496
|
-
if (!
|
|
14705
|
+
if (!existsSync23(file2)) {
|
|
14497
14706
|
return {};
|
|
14498
14707
|
}
|
|
14499
|
-
const text3 =
|
|
14708
|
+
const text3 = readFileSync30(file2, "utf8");
|
|
14500
14709
|
return object7(toml ? parse9(text3) : JSON.parse(text3));
|
|
14501
14710
|
}
|
|
14502
14711
|
function configuredState(value) {
|
|
@@ -14509,11 +14718,17 @@ function configuredState(value) {
|
|
|
14509
14718
|
return "unknown";
|
|
14510
14719
|
}
|
|
14511
14720
|
function located(metadata2, path, pathScope) {
|
|
14512
|
-
return { ...metadata2, path, pathScope, exists:
|
|
14721
|
+
return { ...metadata2, path, pathScope, exists: existsSync23(path) };
|
|
14513
14722
|
}
|
|
14514
14723
|
function metadata(tool, spec, options2) {
|
|
14515
14724
|
const env = options2.env ?? {};
|
|
14516
|
-
const directory = (name, fallback) => resolve15(spec.root, env[name] ??
|
|
14725
|
+
const directory = (name, fallback) => resolve15(spec.root, env[name] ?? join29(options2.home, fallback));
|
|
14726
|
+
if (tool === "opencode") {
|
|
14727
|
+
return {
|
|
14728
|
+
state: "unsupported",
|
|
14729
|
+
source: "OpenCode native memory metadata is not qualified"
|
|
14730
|
+
};
|
|
14731
|
+
}
|
|
14517
14732
|
if (tool === "antigravity") {
|
|
14518
14733
|
return {
|
|
14519
14734
|
state: "unsupported",
|
|
@@ -14537,7 +14752,7 @@ function metadata(tool, spec, options2) {
|
|
|
14537
14752
|
state,
|
|
14538
14753
|
source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
|
|
14539
14754
|
},
|
|
14540
|
-
|
|
14755
|
+
join29(home2, "memories"),
|
|
14541
14756
|
"store"
|
|
14542
14757
|
);
|
|
14543
14758
|
}
|
|
@@ -14550,12 +14765,12 @@ function metadata(tool, spec, options2) {
|
|
|
14550
14765
|
}
|
|
14551
14766
|
if (tool === "claude") {
|
|
14552
14767
|
const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
|
|
14553
|
-
const user2 = settings(
|
|
14768
|
+
const user2 = settings(join29(home2, "settings.json"));
|
|
14554
14769
|
const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
|
|
14555
14770
|
const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
|
|
14556
14771
|
const override = user2["autoMemoryDirectory"];
|
|
14557
14772
|
if (typeof override === "string") {
|
|
14558
|
-
const path = override.startsWith("~/") ?
|
|
14773
|
+
const path = override.startsWith("~/") ? join29(options2.home, override.slice(2)) : override;
|
|
14559
14774
|
if (!isAbsolute7(path)) {
|
|
14560
14775
|
return {
|
|
14561
14776
|
state: "unknown",
|
|
@@ -14568,7 +14783,7 @@ function metadata(tool, spec, options2) {
|
|
|
14568
14783
|
if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
|
|
14569
14784
|
return located(
|
|
14570
14785
|
{ state, source: source2 },
|
|
14571
|
-
|
|
14786
|
+
join29(home2, "projects", projectName, "memory"),
|
|
14572
14787
|
"store"
|
|
14573
14788
|
);
|
|
14574
14789
|
}
|
|
@@ -14577,14 +14792,14 @@ function metadata(tool, spec, options2) {
|
|
|
14577
14792
|
state,
|
|
14578
14793
|
source: `${source2}; native project directory encoding is not guessed`
|
|
14579
14794
|
},
|
|
14580
|
-
|
|
14795
|
+
join29(home2, "projects"),
|
|
14581
14796
|
"base"
|
|
14582
14797
|
);
|
|
14583
14798
|
}
|
|
14584
14799
|
if (tool === "grok") {
|
|
14585
|
-
const home2 =
|
|
14586
|
-
const user2 = settings(
|
|
14587
|
-
const project2 = settings(
|
|
14800
|
+
const home2 = join29(options2.home, ".grok");
|
|
14801
|
+
const user2 = settings(join29(home2, "config.toml"), true);
|
|
14802
|
+
const project2 = settings(join29(spec.root, ".grok/config.toml"), true);
|
|
14588
14803
|
const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
|
|
14589
14804
|
const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
|
|
14590
14805
|
const overridden = projectMemory["enabled"] !== void 0;
|
|
@@ -14594,12 +14809,12 @@ function metadata(tool, spec, options2) {
|
|
|
14594
14809
|
state,
|
|
14595
14810
|
source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
|
|
14596
14811
|
},
|
|
14597
|
-
|
|
14812
|
+
join29(home2, "memory-v2/workspaces"),
|
|
14598
14813
|
"base"
|
|
14599
14814
|
);
|
|
14600
14815
|
}
|
|
14601
14816
|
const home = directory("GEMINI_CLI_HOME", ".gemini");
|
|
14602
|
-
const user = settings(
|
|
14817
|
+
const user = settings(join29(home, "settings.json"));
|
|
14603
14818
|
const context = user["context"] === void 0 ? {} : object7(user["context"]);
|
|
14604
14819
|
const names = context["fileName"] ?? "GEMINI.md";
|
|
14605
14820
|
const contextFileNames = typeof names === "string" ? [names] : names;
|
|
@@ -14615,7 +14830,7 @@ function metadata(tool, spec, options2) {
|
|
|
14615
14830
|
source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
|
|
14616
14831
|
contextFileNames
|
|
14617
14832
|
},
|
|
14618
|
-
|
|
14833
|
+
join29(home, "GEMINI.md"),
|
|
14619
14834
|
"store"
|
|
14620
14835
|
);
|
|
14621
14836
|
}
|
|
@@ -14648,14 +14863,14 @@ function memoryFindings(spec, options2) {
|
|
|
14648
14863
|
// src/project/edit.ts
|
|
14649
14864
|
import {
|
|
14650
14865
|
cpSync,
|
|
14651
|
-
existsSync as
|
|
14866
|
+
existsSync as existsSync24,
|
|
14652
14867
|
mkdirSync as mkdirSync8,
|
|
14653
|
-
readFileSync as
|
|
14654
|
-
rmSync as
|
|
14655
|
-
statSync as
|
|
14868
|
+
readFileSync as readFileSync31,
|
|
14869
|
+
rmSync as rmSync7,
|
|
14870
|
+
statSync as statSync14,
|
|
14656
14871
|
writeFileSync as writeFileSync7
|
|
14657
14872
|
} from "fs";
|
|
14658
|
-
import { basename as basename7, dirname as dirname20, join as
|
|
14873
|
+
import { basename as basename7, dirname as dirname20, join as join30 } from "path";
|
|
14659
14874
|
var CROSS_TOOL = ["name", "description"];
|
|
14660
14875
|
var tomlValue2 = (value) => JSON.stringify(value);
|
|
14661
14876
|
function findEntry(entries, wanted) {
|
|
@@ -14679,7 +14894,7 @@ function addEntry(root, entry2) {
|
|
|
14679
14894
|
`${entry2.name} is a ${kind2}; only skills and agents are added to .agents/`
|
|
14680
14895
|
);
|
|
14681
14896
|
}
|
|
14682
|
-
const { meta, body } = splitFrontmatter(
|
|
14897
|
+
const { meta, body } = splitFrontmatter(readFileSync31(entry2.path, "utf8"));
|
|
14683
14898
|
const name = basename7(entry2.slug);
|
|
14684
14899
|
if (!/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
|
|
14685
14900
|
throw new ProjectError(
|
|
@@ -14687,12 +14902,12 @@ function addEntry(root, entry2) {
|
|
|
14687
14902
|
);
|
|
14688
14903
|
}
|
|
14689
14904
|
if (kind2 === "skill") {
|
|
14690
|
-
const dest2 =
|
|
14691
|
-
if (
|
|
14905
|
+
const dest2 = join30(root, ".agents/skills", name);
|
|
14906
|
+
if (existsSync24(dest2))
|
|
14692
14907
|
throw new ProjectError(`.agents/skills/${name} already exists`);
|
|
14693
14908
|
const source2 = entry2.root ?? dirname20(entry2.path);
|
|
14694
14909
|
mkdirSync8(dirname20(dest2), { recursive: true });
|
|
14695
|
-
if (
|
|
14910
|
+
if (statSync14(source2).isDirectory()) {
|
|
14696
14911
|
cpSync(source2, dest2, { recursive: true });
|
|
14697
14912
|
} else {
|
|
14698
14913
|
mkdirSync8(dest2);
|
|
@@ -14703,14 +14918,14 @@ function addEntry(root, entry2) {
|
|
|
14703
14918
|
description: meta["description"] ?? entry2.description
|
|
14704
14919
|
});
|
|
14705
14920
|
writeFileSync7(
|
|
14706
|
-
|
|
14921
|
+
join30(dest2, "SKILL.md"),
|
|
14707
14922
|
`${head}
|
|
14708
14923
|
${body.replace(/^\n/u, "")}`
|
|
14709
14924
|
);
|
|
14710
14925
|
return { kind: kind2, name, path: `.agents/skills/${name}` };
|
|
14711
14926
|
}
|
|
14712
|
-
const dest =
|
|
14713
|
-
if (
|
|
14927
|
+
const dest = join30(root, ".agents/agents", name);
|
|
14928
|
+
if (existsSync24(dest))
|
|
14714
14929
|
throw new ProjectError(`.agents/agents/${name} already exists`);
|
|
14715
14930
|
const kept = {};
|
|
14716
14931
|
const moved = {};
|
|
@@ -14720,17 +14935,17 @@ ${body.replace(/^\n/u, "")}`
|
|
|
14720
14935
|
}
|
|
14721
14936
|
mkdirSync8(dest, { recursive: true });
|
|
14722
14937
|
writeFileSync7(
|
|
14723
|
-
|
|
14938
|
+
join30(dest, "agent.md"),
|
|
14724
14939
|
`${renderFrontmatter(kept)}
|
|
14725
14940
|
${body.replace(/^\n/u, "")}`
|
|
14726
14941
|
);
|
|
14727
14942
|
if (Object.keys(moved).length > 0) {
|
|
14728
|
-
const config =
|
|
14943
|
+
const config = join30(root, "pmcp.toml");
|
|
14729
14944
|
const lines2 = [
|
|
14730
14945
|
`[agents.${name}.claude]`,
|
|
14731
14946
|
...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
|
|
14732
14947
|
];
|
|
14733
|
-
const before =
|
|
14948
|
+
const before = existsSync24(config) ? readFileSync31(config, "utf8") : "";
|
|
14734
14949
|
const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
|
|
14735
14950
|
writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
|
|
14736
14951
|
`);
|
|
@@ -14753,15 +14968,15 @@ function withoutTables(toml, name) {
|
|
|
14753
14968
|
function removeAsset(root, name) {
|
|
14754
14969
|
const removed = [];
|
|
14755
14970
|
for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
|
|
14756
|
-
if (!
|
|
14757
|
-
|
|
14971
|
+
if (!existsSync24(join30(root, rel))) continue;
|
|
14972
|
+
rmSync7(join30(root, rel), { recursive: true, force: true });
|
|
14758
14973
|
removed.push(rel);
|
|
14759
14974
|
}
|
|
14760
14975
|
if (removed.length === 0)
|
|
14761
14976
|
throw new ProjectError(`no skill or agent named ${name} in .agents/`);
|
|
14762
|
-
const config =
|
|
14763
|
-
if (
|
|
14764
|
-
const before =
|
|
14977
|
+
const config = join30(root, "pmcp.toml");
|
|
14978
|
+
if (existsSync24(config)) {
|
|
14979
|
+
const before = readFileSync31(config, "utf8");
|
|
14765
14980
|
const after = withoutTables(before, name);
|
|
14766
14981
|
if (after !== before) writeFileSync7(config, after);
|
|
14767
14982
|
}
|
|
@@ -15000,16 +15215,16 @@ var doctorCommand = {
|
|
|
15000
15215
|
};
|
|
15001
15216
|
|
|
15002
15217
|
// src/validate.ts
|
|
15003
|
-
import { existsSync as
|
|
15004
|
-
import { basename as basename8, join as
|
|
15218
|
+
import { existsSync as existsSync25, readFileSync as readFileSync32, statSync as statSync15 } from "fs";
|
|
15219
|
+
import { basename as basename8, join as join31, resolve as resolve16 } from "path";
|
|
15005
15220
|
function validateSkillDir(dir) {
|
|
15006
15221
|
const root = resolve16(dir);
|
|
15007
|
-
const skillMd = basename8(root) === "SKILL.md" ? root :
|
|
15008
|
-
if (!
|
|
15222
|
+
const skillMd = basename8(root) === "SKILL.md" ? root : join31(root, "SKILL.md");
|
|
15223
|
+
if (!existsSync25(skillMd) || !statSync15(skillMd).isFile()) {
|
|
15009
15224
|
return [{ path: root, reason: "no SKILL.md", level: "error" }];
|
|
15010
15225
|
}
|
|
15011
|
-
const front = frontmatterObject(
|
|
15012
|
-
const directory = basename8(skillMd === root ?
|
|
15226
|
+
const front = frontmatterObject(readFileSync32(skillMd, "utf8"));
|
|
15227
|
+
const directory = basename8(skillMd === root ? join31(root, "..") : root);
|
|
15013
15228
|
return specProblems(front, directory).map((reason) => ({
|
|
15014
15229
|
path: skillMd,
|
|
15015
15230
|
reason,
|
|
@@ -15054,7 +15269,7 @@ var validateCommand = {
|
|
|
15054
15269
|
};
|
|
15055
15270
|
|
|
15056
15271
|
// src/commands/mailbox.ts
|
|
15057
|
-
import { readFileSync as
|
|
15272
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync8 } from "fs";
|
|
15058
15273
|
import { resolve as resolve17 } from "path";
|
|
15059
15274
|
init_config2();
|
|
15060
15275
|
init_service();
|
|
@@ -15237,7 +15452,7 @@ var mailboxCommand = {
|
|
|
15237
15452
|
ownsBinding = true;
|
|
15238
15453
|
}
|
|
15239
15454
|
const inputFile = one(context.args, "input");
|
|
15240
|
-
const input = inputFile ? JSON.parse(
|
|
15455
|
+
const input = inputFile ? JSON.parse(readFileSync33(inputFile, "utf8")) : {};
|
|
15241
15456
|
const result = await new MailboxService(store, binding).call(
|
|
15242
15457
|
operation,
|
|
15243
15458
|
input
|
|
@@ -15321,7 +15536,7 @@ var harnessOptions = [
|
|
|
15321
15536
|
];
|
|
15322
15537
|
|
|
15323
15538
|
// src/commands/harness/invocation.ts
|
|
15324
|
-
import { readFileSync as
|
|
15539
|
+
import { readFileSync as readFileSync34, statSync as statSync16 } from "fs";
|
|
15325
15540
|
init_validation();
|
|
15326
15541
|
var common = ["group-file", "generation"];
|
|
15327
15542
|
var options = {
|
|
@@ -15358,10 +15573,10 @@ var options = {
|
|
|
15358
15573
|
"recovery:abort-start": [...common, "session"]
|
|
15359
15574
|
};
|
|
15360
15575
|
function file(path) {
|
|
15361
|
-
const stat =
|
|
15576
|
+
const stat = statSync16(path);
|
|
15362
15577
|
if (!stat.isFile() || stat.size > 65536)
|
|
15363
15578
|
throw new ArgumentError("Input file exceeds 65536 bytes");
|
|
15364
|
-
return
|
|
15579
|
+
return readFileSync34(path, "utf8");
|
|
15365
15580
|
}
|
|
15366
15581
|
function parseInvocation(context) {
|
|
15367
15582
|
const [family, action, ...extra] = context.args.positional;
|
|
@@ -15694,14 +15909,14 @@ import { resolve as resolve21 } from "path";
|
|
|
15694
15909
|
|
|
15695
15910
|
// src/marketplace/export.ts
|
|
15696
15911
|
import {
|
|
15697
|
-
existsSync as
|
|
15912
|
+
existsSync as existsSync38,
|
|
15698
15913
|
lstatSync as lstatSync14,
|
|
15699
15914
|
mkdirSync as mkdirSync13,
|
|
15700
|
-
readFileSync as
|
|
15915
|
+
readFileSync as readFileSync39,
|
|
15701
15916
|
readdirSync as readdirSync13,
|
|
15702
15917
|
writeFileSync as writeFileSync14
|
|
15703
15918
|
} from "fs";
|
|
15704
|
-
import { dirname as dirname27, join as
|
|
15919
|
+
import { dirname as dirname27, join as join58, resolve as resolve20 } from "path";
|
|
15705
15920
|
var NATIVE_PLUGIN_RUNTIMES = [
|
|
15706
15921
|
"claude",
|
|
15707
15922
|
"codex",
|
|
@@ -15726,7 +15941,7 @@ function readResources(directory, prefix, files) {
|
|
|
15726
15941
|
for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
|
|
15727
15942
|
if (entry2.isSymbolicLink())
|
|
15728
15943
|
throw new Error("Plugin exports cannot contain symlinks");
|
|
15729
|
-
const path =
|
|
15944
|
+
const path = join58(directory, entry2.name);
|
|
15730
15945
|
const relativePath = `${prefix}/${entry2.name}`;
|
|
15731
15946
|
if (entry2.isDirectory()) {
|
|
15732
15947
|
if (relativePath.split("/").length > 6)
|
|
@@ -15739,7 +15954,7 @@ function readResources(directory, prefix, files) {
|
|
|
15739
15954
|
const stats = lstatSync14(path);
|
|
15740
15955
|
if (stats.size > 1024 * 1024 || files.size >= 256)
|
|
15741
15956
|
throw new Error("Plugin resources exceed the export budget");
|
|
15742
|
-
files.set(relativePath,
|
|
15957
|
+
files.set(relativePath, readFileSync39(path));
|
|
15743
15958
|
}
|
|
15744
15959
|
}
|
|
15745
15960
|
function pluginExportFiles(root, name, runtime2) {
|
|
@@ -15748,7 +15963,7 @@ function pluginExportFiles(root, name, runtime2) {
|
|
|
15748
15963
|
const files = /* @__PURE__ */ new Map();
|
|
15749
15964
|
readResources(containedPath(plugin.directory, "skills"), "skills", files);
|
|
15750
15965
|
for (const name2 of ["LICENSE", "NOTICE"])
|
|
15751
|
-
files.set(name2,
|
|
15966
|
+
files.set(name2, readFileSync39(containedPath(root, name2)));
|
|
15752
15967
|
const manifests = {
|
|
15753
15968
|
claude: [".claude-plugin/plugin.json", ".mcp.json"],
|
|
15754
15969
|
codex: ["plugin.json", "mcp.json"],
|
|
@@ -15757,10 +15972,10 @@ function pluginExportFiles(root, name, runtime2) {
|
|
|
15757
15972
|
agy: []
|
|
15758
15973
|
};
|
|
15759
15974
|
for (const filename of manifests[runtime2]) {
|
|
15760
|
-
if (
|
|
15975
|
+
if (existsSync38(join58(plugin.directory, filename)))
|
|
15761
15976
|
files.set(
|
|
15762
15977
|
filename,
|
|
15763
|
-
|
|
15978
|
+
readFileSync39(containedPath(plugin.directory, filename))
|
|
15764
15979
|
);
|
|
15765
15980
|
}
|
|
15766
15981
|
if (runtime2 === "agy") {
|
|
@@ -15771,11 +15986,11 @@ function pluginExportFiles(root, name, runtime2) {
|
|
|
15771
15986
|
`
|
|
15772
15987
|
)
|
|
15773
15988
|
);
|
|
15774
|
-
const mcp =
|
|
15775
|
-
if (
|
|
15989
|
+
const mcp = join58(plugin.directory, ".mcp.json");
|
|
15990
|
+
if (existsSync38(mcp))
|
|
15776
15991
|
files.set(
|
|
15777
15992
|
"mcp_config.json",
|
|
15778
|
-
|
|
15993
|
+
readFileSync39(containedPath(plugin.directory, mcp))
|
|
15779
15994
|
);
|
|
15780
15995
|
}
|
|
15781
15996
|
return files;
|
|
@@ -15794,7 +16009,7 @@ function exportPublicPlugin(root, name, runtime2, output) {
|
|
|
15794
16009
|
throw error;
|
|
15795
16010
|
}
|
|
15796
16011
|
for (const [path, bytes] of files) {
|
|
15797
|
-
const target2 =
|
|
16012
|
+
const target2 = join58(destination, path);
|
|
15798
16013
|
mkdirSync13(dirname27(target2), { recursive: true });
|
|
15799
16014
|
writeFileSync14(target2, bytes, { flag: "wx" });
|
|
15800
16015
|
}
|
|
@@ -15946,7 +16161,7 @@ function dispatch(argv, options2 = {}) {
|
|
|
15946
16161
|
}
|
|
15947
16162
|
return run(command3, rest, { ui, env, cwd });
|
|
15948
16163
|
}
|
|
15949
|
-
var VERSION = "0.
|
|
16164
|
+
var VERSION = "0.17.1";
|
|
15950
16165
|
async function defaultEntry(options2, context) {
|
|
15951
16166
|
const terminal = options2.terminal ?? {
|
|
15952
16167
|
stdin: process.stdin.isTTY === true,
|