paseo-room 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -6
- package/dist/index.js +238 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,8 +85,11 @@ starts, its exit code is preserved; a signal is returned using the conventional
|
|
|
85
85
|
query Keychain state, and reports older-runtime Keychain isolation as unverifiable. See
|
|
86
86
|
Anthropic's [credential-management reference](https://code.claude.com/docs/en/authentication#credential-management).
|
|
87
87
|
- An initialised Pi home (`~/.pi/agent`) and an operator-installed global
|
|
88
|
-
`pi-mcp-adapter` package under its `npm/node_modules
|
|
89
|
-
not a compatibility constraint.
|
|
88
|
+
`pi-mcp-adapter` package under its `npm/node_modules` (`pi install npm:pi-mcp-adapter`). The
|
|
89
|
+
package version is diagnostic, not a compatibility constraint. Pi 1.0's built-in MCP extension
|
|
90
|
+
does not replace it: Paseo gives a Pi seat its MCP servers only through the adapter, which it
|
|
91
|
+
recognises by name and configures with the adapter's own `--mcp-config` flag. The room never
|
|
92
|
+
installs or upgrades Pi or the adapter; setup stops with that install command instead.
|
|
90
93
|
|
|
91
94
|
## What gets created
|
|
92
95
|
|
|
@@ -285,17 +288,35 @@ but what the model actually reads is not proven, and `CLAUDE.md` is what covers
|
|
|
285
288
|
Pi providers use a strict command tail:
|
|
286
289
|
|
|
287
290
|
```text
|
|
288
|
-
--no-extensions --extension <canonical pi-mcp-adapter entry>
|
|
291
|
+
--no-extensions --extension <canonical pi-mcp-adapter entry>
|
|
292
|
+
[--extension <canonical entry of each selected package>] --no-approve
|
|
289
293
|
--append-system-prompt <role APPEND_SYSTEM.md>
|
|
290
294
|
```
|
|
291
295
|
|
|
296
|
+
Each Pi provider also pins `PASEO_ROOM_ROLE=<role>`, so an extension can tell it runs as a
|
|
297
|
+
seat. To load more of your own installed Pi packages in the seats, select them at setup:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
paseo-room setup --agent pi --pi-extension pi-blackbytes --pi-extension pi-provider-kiro=supervisor,lead,peer --apply
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Roles default to `supervisor,lead`; Peer gets an extension only when named, with a warning.
|
|
304
|
+
Packages load only from your Pi home's `npm/node_modules`, with the same containment checks
|
|
305
|
+
as the adapter; package prompts, skills and themes are not loaded. A package that is not
|
|
306
|
+
installed is a warning with its `pi install npm:<package>` command and the seat starts without
|
|
307
|
+
it; the room never installs one. Selecting a package states that it opens no second
|
|
308
|
+
multi-agent path in a seat (`pi-blackbytes` 3.1.0 turns its sub-agents off when
|
|
309
|
+
`PASEO_ROOM_ROLE` is set). The choice is recorded in `room.json`; a later `setup` without the
|
|
310
|
+
flag removes the extensions and warns which ones. See
|
|
311
|
+
[docs/design/pi-seat-extensions.md](docs/design/pi-seat-extensions.md).
|
|
312
|
+
|
|
292
313
|
This is Paseo's core `pi` provider, not an OMP provider or compatibility layer.
|
|
293
314
|
Paseo appends and owns its own generated temporary integration extension; `paseo-room` does
|
|
294
315
|
not resolve, create or include it. Before planning any writes, the room validates the exact
|
|
295
316
|
global package identity and canonical entry containment, then runs one bounded offline Pi RPC
|
|
296
|
-
`get_commands` probe with
|
|
297
|
-
`PI_MCP_CONFIG_MODE=exclusive`,
|
|
298
|
-
removed afterwards. This prevents adapter startup from selecting operator-global or caller
|
|
317
|
+
`get_commands` probe that loads the adapter and every selected extension, with
|
|
318
|
+
`PI_MCP_CONFIG_MODE=exclusive`, `PASEO_ROOM_ROLE=lead`, `HOME` set to a fresh temporary working
|
|
319
|
+
directory that is removed afterwards, and `PI_CODING_AGENT_DIR` an empty directory inside it. This prevents adapter startup from selecting operator-global or caller
|
|
299
320
|
project MCP configs during the probe. The correlated response must
|
|
300
321
|
attribute `/mcp` to `source: "extension"` at that same canonical path. Missing, malformed,
|
|
301
322
|
path-escaped or wrongly attributed adapters fail closed. The probe and setup do not write the
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "paseo-room",
|
|
18
|
-
version: "0.
|
|
18
|
+
version: "0.18.0",
|
|
19
19
|
description: "One CLI that configures Codex, Claude Code, and Pi role homes in $HOME and registers them with your local Paseo daemon.",
|
|
20
20
|
keywords: [
|
|
21
21
|
"paseo",
|
|
@@ -473,7 +473,7 @@ var init_roles = __esm({
|
|
|
473
473
|
// src/room.ts
|
|
474
474
|
import { join as join3 } from "path";
|
|
475
475
|
import { z } from "zod";
|
|
476
|
-
function renderMarker(version, agents, roles, contract, claudeMemoryContract, runtime) {
|
|
476
|
+
function renderMarker(version, agents, roles, contract, claudeMemoryContract, runtime, piExtensions) {
|
|
477
477
|
return JSON.stringify({
|
|
478
478
|
version,
|
|
479
479
|
agents: [...agents],
|
|
@@ -481,7 +481,10 @@ function renderMarker(version, agents, roles, contract, claudeMemoryContract, ru
|
|
|
481
481
|
...contract === void 0 ? {} : { contract },
|
|
482
482
|
// Written only when it differs from the default, so an unchanged room's marker is unchanged.
|
|
483
483
|
...claudeMemoryContract === false ? { claudeMemoryContract: false } : {},
|
|
484
|
-
...runtime === void 0 ? {} : { runtime }
|
|
484
|
+
...runtime === void 0 ? {} : { runtime },
|
|
485
|
+
...piExtensions === void 0 || piExtensions.length === 0 ? {} : {
|
|
486
|
+
piExtensions: piExtensions.map((choice) => ({ package: choice.package, roles: [...choice.roles] }))
|
|
487
|
+
}
|
|
485
488
|
}, null, 2) + "\n";
|
|
486
489
|
}
|
|
487
490
|
async function readMarker(layout) {
|
|
@@ -520,7 +523,12 @@ var init_room = __esm({
|
|
|
520
523
|
* Present only when this room opted in to runtime coordination. Missing means disabled, so a
|
|
521
524
|
* marker written before runtime existed keeps parsing and keeps its behaviour.
|
|
522
525
|
*/
|
|
523
|
-
runtime: z.strictObject({ enabled: z.literal(true), generation: z.string().min(1), schema: z.literal(1) }).optional()
|
|
526
|
+
runtime: z.strictObject({ enabled: z.literal(true), generation: z.string().min(1), schema: z.literal(1) }).optional(),
|
|
527
|
+
/**
|
|
528
|
+
* Pi packages this room loads as explicit seat extensions (docs/design/pi-seat-extensions.md).
|
|
529
|
+
* Missing means none, so a marker written before the option existed keeps its behaviour.
|
|
530
|
+
*/
|
|
531
|
+
piExtensions: z.array(z.strictObject({ package: z.string().min(1), roles: z.array(z.enum(ROLES)).min(1) })).min(1).optional()
|
|
524
532
|
});
|
|
525
533
|
}
|
|
526
534
|
});
|
|
@@ -1608,7 +1616,7 @@ var init_codex = __esm({
|
|
|
1608
1616
|
|
|
1609
1617
|
// src/agents/pi.ts
|
|
1610
1618
|
import { spawn as spawn2 } from "child_process";
|
|
1611
|
-
import { mkdtemp, readFile as readFile3, realpath, rm as rm2, stat } from "fs/promises";
|
|
1619
|
+
import { mkdir as mkdir2, mkdtemp, readFile as readFile3, realpath, rm as rm2, stat } from "fs/promises";
|
|
1612
1620
|
import { tmpdir } from "os";
|
|
1613
1621
|
import { isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "path";
|
|
1614
1622
|
function asObject2(value) {
|
|
@@ -1630,16 +1638,116 @@ function renderPiAppend(source, role) {
|
|
|
1630
1638
|
renderInstructions(role)
|
|
1631
1639
|
].filter((part) => Boolean(part)).join("\n\n");
|
|
1632
1640
|
}
|
|
1641
|
+
function parsePiExtensionSpec(spec) {
|
|
1642
|
+
const [name = "", roleList, ...rest] = spec.trim().split("=");
|
|
1643
|
+
if (rest.length > 0 || !NPM_NAME.test(name)) {
|
|
1644
|
+
throw new Error(`Not a Pi package selection: ${spec}. Use <npm package>[=<role>,<role>].`);
|
|
1645
|
+
}
|
|
1646
|
+
if (name === PACKAGE_NAME) throw new Error(`${PACKAGE_NAME} is always loaded and cannot be selected.`);
|
|
1647
|
+
if (roleList === void 0) return { package: name, roles: DEFAULT_EXTENSION_ROLES };
|
|
1648
|
+
const roles = roleList.split(",").map((role) => role.trim());
|
|
1649
|
+
const unknown = roles.filter((role) => !ROLES.some((known) => known === role));
|
|
1650
|
+
if (unknown.length > 0 || roles.length === 0) {
|
|
1651
|
+
throw new Error(`Unknown role in ${spec}: ${unknown.join(", ") || "(none)"}. Roles are ${ROLES.join(", ")}.`);
|
|
1652
|
+
}
|
|
1653
|
+
return { package: name, roles: ROLES.filter((role) => roles.includes(role)) };
|
|
1654
|
+
}
|
|
1655
|
+
function normalizePiExtensions(choices) {
|
|
1656
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1657
|
+
for (const choice of choices) {
|
|
1658
|
+
const roles = merged.get(choice.package) ?? /* @__PURE__ */ new Set();
|
|
1659
|
+
for (const role of choice.roles) roles.add(role);
|
|
1660
|
+
merged.set(choice.package, roles);
|
|
1661
|
+
}
|
|
1662
|
+
return [...merged.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([name, roles]) => ({ package: name, roles: ROLES.filter((role) => roles.has(role)) }));
|
|
1663
|
+
}
|
|
1664
|
+
async function resolvePiPackage(home, name) {
|
|
1665
|
+
const packageRoot = join10(home, "npm", "node_modules", name);
|
|
1666
|
+
const manifestPath = join10(packageRoot, "package.json");
|
|
1667
|
+
try {
|
|
1668
|
+
await stat(packageRoot);
|
|
1669
|
+
} catch (error) {
|
|
1670
|
+
if (error.code === "ENOENT") return { kind: "missing" };
|
|
1671
|
+
return { kind: "unsafe", detail: `Could not inspect ${packageRoot}.` };
|
|
1672
|
+
}
|
|
1673
|
+
try {
|
|
1674
|
+
const [canonicalRoot, manifestStat, raw] = await Promise.all([
|
|
1675
|
+
realpath(packageRoot),
|
|
1676
|
+
stat(manifestPath),
|
|
1677
|
+
readFile3(manifestPath, "utf8")
|
|
1678
|
+
]);
|
|
1679
|
+
if (!manifestStat.isFile()) return { kind: "unsafe", detail: `${manifestPath} is not a regular file.` };
|
|
1680
|
+
const manifest = asObject2(JSON.parse(raw));
|
|
1681
|
+
if (!manifest || manifest.name !== name) return { kind: "unsafe", detail: `${manifestPath} does not identify ${name}.` };
|
|
1682
|
+
const declared = asObject2(manifest.pi)?.extensions;
|
|
1683
|
+
if (!Array.isArray(declared) || declared.length === 0 || !declared.every((item) => typeof item === "string" && item.trim() !== "")) {
|
|
1684
|
+
return { kind: "unsafe", detail: `${manifestPath} declares no Pi extension entry.` };
|
|
1685
|
+
}
|
|
1686
|
+
const entries = [];
|
|
1687
|
+
for (const declaration of declared) {
|
|
1688
|
+
if (isAbsolute2(declaration)) return { kind: "unsafe", detail: `${manifestPath} declares an unsafe absolute extension path.` };
|
|
1689
|
+
const candidate = resolve3(packageRoot, declaration);
|
|
1690
|
+
const [canonicalEntry, entryStat] = await Promise.all([realpath(candidate), stat(candidate)]);
|
|
1691
|
+
if (!contains(canonicalRoot, canonicalEntry) || canonicalEntry === canonicalRoot || !entryStat.isFile()) {
|
|
1692
|
+
return { kind: "unsafe", detail: `A declared ${name} extension escapes its package or is not a regular file.` };
|
|
1693
|
+
}
|
|
1694
|
+
entries.push(canonicalEntry);
|
|
1695
|
+
}
|
|
1696
|
+
const version = typeof manifest.version === "string" && manifest.version.trim() ? manifest.version.trim() : "unknown";
|
|
1697
|
+
return { kind: "resolved", entries, version };
|
|
1698
|
+
} catch {
|
|
1699
|
+
return { kind: "unsafe", detail: `Could not safely resolve ${name} from ${manifestPath}.` };
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
async function resolvePiExtensions(home, choices, roles) {
|
|
1703
|
+
const byRole = {};
|
|
1704
|
+
const checks = [];
|
|
1705
|
+
for (const choice of normalizePiExtensions(choices)) {
|
|
1706
|
+
const seated = choice.roles.filter((role) => roles.includes(role));
|
|
1707
|
+
if (seated.length === 0) continue;
|
|
1708
|
+
const id2 = `pi.extension.${choice.package}`;
|
|
1709
|
+
const resolution = await resolvePiPackage(home, choice.package);
|
|
1710
|
+
if (resolution.kind === "missing") {
|
|
1711
|
+
checks.push(warn(
|
|
1712
|
+
`${id2}.missing`,
|
|
1713
|
+
`Pi package ${choice.package} is selected for ${seated.join(", ")} but is not installed in ${join10(home, "npm")}; those seats start without it.`,
|
|
1714
|
+
`Install it yourself with: pi install npm:${choice.package}, then run setup again. The room never installs Pi packages.`
|
|
1715
|
+
));
|
|
1716
|
+
continue;
|
|
1717
|
+
}
|
|
1718
|
+
if (resolution.kind === "unsafe") {
|
|
1719
|
+
checks.push(fail(id2, resolution.detail, `Reinstall ${choice.package} with Pi, or drop --pi-extension ${choice.package}, then run setup again.`));
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
for (const role of seated) byRole[role] = [...byRole[role] ?? [], ...resolution.entries];
|
|
1723
|
+
checks.push(pass(id2, `${choice.package} ${resolution.version} loads for ${seated.join(", ")} from ${resolution.entries.join(", ")}.`));
|
|
1724
|
+
if (seated.includes("peer")) {
|
|
1725
|
+
checks.push(warn(
|
|
1726
|
+
`${id2}.peer`,
|
|
1727
|
+
`Peer runs ${choice.package}'s extension code. The room cannot prove it opens no second multi-agent path; selecting it states that it does not, or that it closes that path when ${SEAT_ROLE_ENV} is set.`,
|
|
1728
|
+
`Drop peer from --pi-extension ${choice.package}=<roles> if that is not true.`
|
|
1729
|
+
));
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return { byRole, checks };
|
|
1733
|
+
}
|
|
1633
1734
|
function adapterFailure(home, detail) {
|
|
1634
1735
|
return fail(
|
|
1635
1736
|
"pi.adapter",
|
|
1636
1737
|
detail,
|
|
1637
|
-
`Install ${PACKAGE_NAME}
|
|
1738
|
+
`Install ${PACKAGE_NAME} into ${join10(home, "npm")} yourself with: pi install npm:${PACKAGE_NAME}. Pi's built-in MCP extension is not a substitute: Paseo hands a Pi seat its tools only through ${PACKAGE_NAME}. The room never installs Pi packages. Then run setup again.`
|
|
1638
1739
|
);
|
|
1639
1740
|
}
|
|
1640
1741
|
async function resolvePiAdapter(home) {
|
|
1641
1742
|
const packageRoot = join10(home, "npm", "node_modules", PACKAGE_NAME);
|
|
1642
1743
|
const manifestPath = join10(packageRoot, "package.json");
|
|
1744
|
+
try {
|
|
1745
|
+
await stat(packageRoot);
|
|
1746
|
+
} catch (error) {
|
|
1747
|
+
if (error.code === "ENOENT") {
|
|
1748
|
+
return { check: adapterFailure(home, `${PACKAGE_NAME} is not installed in ${join10(home, "npm")}.`) };
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1643
1751
|
try {
|
|
1644
1752
|
const [canonicalRoot, manifestStat, raw] = await Promise.all([
|
|
1645
1753
|
realpath(packageRoot),
|
|
@@ -1692,7 +1800,16 @@ function terminateProbe(child) {
|
|
|
1692
1800
|
function runProbeProcess(executable, adapterEntry, env, options, cwd) {
|
|
1693
1801
|
const timeoutMs = options.timeoutMs ?? PROBE_TIMEOUT_MS;
|
|
1694
1802
|
const outputLimit = options.outputLimit ?? PROBE_OUTPUT_LIMIT;
|
|
1695
|
-
const args = [
|
|
1803
|
+
const args = [
|
|
1804
|
+
"--mode",
|
|
1805
|
+
"rpc",
|
|
1806
|
+
"--no-session",
|
|
1807
|
+
"--no-extensions",
|
|
1808
|
+
"--extension",
|
|
1809
|
+
adapterEntry,
|
|
1810
|
+
...(options.extensions ?? []).flatMap((entry) => ["--extension", entry]),
|
|
1811
|
+
"--no-approve"
|
|
1812
|
+
];
|
|
1696
1813
|
return new Promise((resolveResult) => {
|
|
1697
1814
|
const child = spawn2(executable, args, { cwd, detached: true, env, stdio: ["pipe", "pipe", "pipe"] });
|
|
1698
1815
|
const stdout = [];
|
|
@@ -1750,12 +1867,15 @@ async function runProbe(executable, adapterEntry, env, options) {
|
|
|
1750
1867
|
const createdCwd = await mkdtemp(join10(tmpdir(), "paseo-room-pi-probe-"));
|
|
1751
1868
|
const cwd = await realpath(createdCwd);
|
|
1752
1869
|
try {
|
|
1870
|
+
const agentDir = join10(cwd, "agent");
|
|
1871
|
+
await mkdir2(agentDir);
|
|
1753
1872
|
return await runProbeProcess(executable, adapterEntry, {
|
|
1754
1873
|
...env,
|
|
1755
1874
|
HOME: cwd,
|
|
1756
|
-
PI_CODING_AGENT_DIR:
|
|
1875
|
+
PI_CODING_AGENT_DIR: agentDir,
|
|
1757
1876
|
PI_MCP_CONFIG_MODE: "exclusive",
|
|
1758
|
-
PI_OFFLINE: "1"
|
|
1877
|
+
PI_OFFLINE: "1",
|
|
1878
|
+
[SEAT_ROLE_ENV]: "lead"
|
|
1759
1879
|
}, options, cwd);
|
|
1760
1880
|
} finally {
|
|
1761
1881
|
await rm2(createdCwd, { force: true, recursive: true });
|
|
@@ -1850,7 +1970,7 @@ async function piCredentialDiagnostic(layout, role, binary = "pi") {
|
|
|
1850
1970
|
fix: `Start a minimal, non-room-equivalent interactive login session with: ${launch}. Run /login, then exit Pi.`
|
|
1851
1971
|
}] };
|
|
1852
1972
|
}
|
|
1853
|
-
var PACKAGE_NAME, PROBE_ID, PROBE_TIMEOUT_MS, PROBE_OUTPUT_LIMIT, SHARED3, EXECUTABLE3, MCP_FILE, MCP_KEYS, AUTH_ENV2, piAgent;
|
|
1973
|
+
var PACKAGE_NAME, PROBE_ID, PROBE_TIMEOUT_MS, PROBE_OUTPUT_LIMIT, SHARED3, EXECUTABLE3, MCP_FILE, MCP_KEYS, AUTH_ENV2, DEFAULT_EXTENSION_ROLES, NPM_NAME, SEAT_ROLE_ENV, piAgent;
|
|
1854
1974
|
var init_pi = __esm({
|
|
1855
1975
|
"src/agents/pi.ts"() {
|
|
1856
1976
|
"use strict";
|
|
@@ -1859,6 +1979,7 @@ var init_pi = __esm({
|
|
|
1859
1979
|
init_fsops();
|
|
1860
1980
|
init_layout();
|
|
1861
1981
|
init_result();
|
|
1982
|
+
init_roles();
|
|
1862
1983
|
init_instructions();
|
|
1863
1984
|
init_prompts();
|
|
1864
1985
|
init_which();
|
|
@@ -1895,13 +2016,16 @@ var init_pi = __esm({
|
|
|
1895
2016
|
"KIMI_API_KEY",
|
|
1896
2017
|
"COPILOT_GITHUB_TOKEN"
|
|
1897
2018
|
];
|
|
2019
|
+
DEFAULT_EXTENSION_ROLES = ["supervisor", "lead"];
|
|
2020
|
+
NPM_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
|
|
2021
|
+
SEAT_ROLE_ENV = "PASEO_ROOM_ROLE";
|
|
1898
2022
|
piAgent = {
|
|
1899
2023
|
id: "pi",
|
|
1900
2024
|
label: "Pi",
|
|
1901
2025
|
homeEnv: "PI_CODING_AGENT_DIR",
|
|
1902
2026
|
providerEnv: { PI_MCP_CONFIG_MODE: "exclusive" },
|
|
1903
2027
|
pins: {},
|
|
1904
|
-
async build(layout, roles) {
|
|
2028
|
+
async build(layout, roles, options) {
|
|
1905
2029
|
const home = layout.agentHome.pi;
|
|
1906
2030
|
const binary = await which(layout.bin.pi, layout.searchPath);
|
|
1907
2031
|
if (!binary) {
|
|
@@ -1914,13 +2038,22 @@ var init_pi = __esm({
|
|
|
1914
2038
|
}
|
|
1915
2039
|
const resolved = await resolvePiAdapter(home);
|
|
1916
2040
|
if (!resolved.adapter) return { entries: [], checks: [resolved.check] };
|
|
2041
|
+
const extensions = await resolvePiExtensions(home, options?.piExtensions ?? [], roles);
|
|
2042
|
+
if (extensions.checks.some((check) => check.status === "fail")) {
|
|
2043
|
+
return { entries: [], checks: [resolved.check, ...extensions.checks] };
|
|
2044
|
+
}
|
|
2045
|
+
const selected = [...new Set(Object.values(extensions.byRole).flat())];
|
|
1917
2046
|
const capability = await probePiMcp(binary, resolved.adapter.entry, {
|
|
1918
2047
|
HOME: layout.home,
|
|
1919
2048
|
PATH: layout.searchPath,
|
|
1920
|
-
PI_CODING_AGENT_DIR: "/dev/null",
|
|
1921
2049
|
PI_OFFLINE: "1"
|
|
1922
|
-
});
|
|
1923
|
-
if (capability.status === "fail")
|
|
2050
|
+
}, { extensions: selected });
|
|
2051
|
+
if (capability.status === "fail") {
|
|
2052
|
+
return { entries: [], checks: [resolved.check, ...extensions.checks, selected.length === 0 ? capability : {
|
|
2053
|
+
...capability,
|
|
2054
|
+
fix: `${capability.fix ?? ""} A selected Pi extension may also be what stops Pi from starting; retry without --pi-extension to tell.`.trim()
|
|
2055
|
+
}] };
|
|
2056
|
+
}
|
|
1924
2057
|
const settingsPath = join10(home, "settings.json");
|
|
1925
2058
|
let settings;
|
|
1926
2059
|
try {
|
|
@@ -1981,6 +2114,7 @@ var init_pi = __esm({
|
|
|
1981
2114
|
const entries = [];
|
|
1982
2115
|
const credentials = [];
|
|
1983
2116
|
const argv = {};
|
|
2117
|
+
const providerEnv = {};
|
|
1984
2118
|
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1985
2119
|
for (const role of roles) {
|
|
1986
2120
|
const target = roleHome(layout, "pi", role);
|
|
@@ -1999,14 +2133,24 @@ var init_pi = __esm({
|
|
|
1999
2133
|
leadSkillProjection: leadSkillProjection(layout, "pi")
|
|
2000
2134
|
}));
|
|
2001
2135
|
credentials.push(await piCredentialDiagnostic(layout, role, binary));
|
|
2002
|
-
argv[role] = [
|
|
2136
|
+
argv[role] = [
|
|
2137
|
+
"--no-extensions",
|
|
2138
|
+
"--extension",
|
|
2139
|
+
resolved.adapter.entry,
|
|
2140
|
+
...(extensions.byRole[role] ?? []).flatMap((entry) => ["--extension", entry]),
|
|
2141
|
+
"--no-approve",
|
|
2142
|
+
"--append-system-prompt",
|
|
2143
|
+
appendPath
|
|
2144
|
+
];
|
|
2145
|
+
providerEnv[role] = { [SEAT_ROLE_ENV]: role };
|
|
2003
2146
|
}
|
|
2004
2147
|
return {
|
|
2005
2148
|
entries,
|
|
2006
2149
|
credentials,
|
|
2007
|
-
checks: [pass("pi.home", `Pi found at ${binary} using ${home}.`), resolved.check, capability],
|
|
2150
|
+
checks: [pass("pi.home", `Pi found at ${binary} using ${home}.`), resolved.check, ...extensions.checks, capability],
|
|
2008
2151
|
binary,
|
|
2009
|
-
argv
|
|
2152
|
+
argv,
|
|
2153
|
+
providerEnv
|
|
2010
2154
|
};
|
|
2011
2155
|
}
|
|
2012
2156
|
};
|
|
@@ -3785,13 +3929,13 @@ var init_schema = __esm({
|
|
|
3785
3929
|
|
|
3786
3930
|
// src/runtime-plugin/server/store/publish.ts
|
|
3787
3931
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3788
|
-
import { chmod as chmod2, link, lstat as lstat4, mkdir as
|
|
3932
|
+
import { chmod as chmod2, link, lstat as lstat4, mkdir as mkdir3, open, unlink as unlink2 } from "fs/promises";
|
|
3789
3933
|
import { join as join13 } from "path";
|
|
3790
3934
|
function code(error) {
|
|
3791
3935
|
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
3792
3936
|
}
|
|
3793
3937
|
async function ensurePrivateDirectory(path) {
|
|
3794
|
-
await
|
|
3938
|
+
await mkdir3(path, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
3795
3939
|
const stat2 = await lstat4(path);
|
|
3796
3940
|
if (!stat2.isDirectory()) throw new Error(`${path} is not a directory.`);
|
|
3797
3941
|
if ((stat2.mode & 511) !== PRIVATE_DIR_MODE) await chmod2(path, PRIVATE_DIR_MODE);
|
|
@@ -3880,7 +4024,7 @@ var init_publish = __esm({
|
|
|
3880
4024
|
|
|
3881
4025
|
// src/runtime-plugin/server/store/project.ts
|
|
3882
4026
|
import { randomBytes as randomBytes3, randomUUID } from "crypto";
|
|
3883
|
-
import { mkdir as
|
|
4027
|
+
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile4, rename as rename2, rm as rm3, writeFile as writeFile2 } from "fs/promises";
|
|
3884
4028
|
import { basename as basename2, join as join14 } from "path";
|
|
3885
4029
|
import { z as z12 } from "zod";
|
|
3886
4030
|
function runtimeRoot(roomHome) {
|
|
@@ -4097,7 +4241,7 @@ var init_project = __esm({
|
|
|
4097
4241
|
}
|
|
4098
4242
|
/** Writes one disposable derived view. Cache files may be replaced; they are never authority. */
|
|
4099
4243
|
async writeCache(name, content) {
|
|
4100
|
-
await
|
|
4244
|
+
await mkdir4(this.cacheDirectory, { recursive: true, mode: 448 });
|
|
4101
4245
|
const temporary = join14(this.cacheDirectory, `.tmp-${String(process.pid)}-${randomBytes3(6).toString("hex")}`);
|
|
4102
4246
|
await writeFile2(temporary, content, { mode: PRIVATE_FILE_MODE });
|
|
4103
4247
|
await rename2(temporary, join14(this.cacheDirectory, name));
|
|
@@ -4280,7 +4424,7 @@ async function buildDesired(layout, agents, roles, build = {}) {
|
|
|
4280
4424
|
entries.push(...runtimePluginEntries(layout, agents, roles));
|
|
4281
4425
|
runtime = { enabled: true, generation: renderRuntimeManifest(agents, roles).roomGeneration, schema: 1 };
|
|
4282
4426
|
}
|
|
4283
|
-
entries.push({ kind: "file", path: join16(layout.roomHome, MARKER), content: renderMarker(package_default.version, agents, roles, contractDigest(), markerMemoryContract(agents, build.memoryContract ?? true), runtime) });
|
|
4427
|
+
entries.push({ kind: "file", path: join16(layout.roomHome, MARKER), content: renderMarker(package_default.version, agents, roles, contractDigest(), markerMemoryContract(agents, build.memoryContract ?? true), runtime, markerPiExtensions(agents, build.piExtensions)) });
|
|
4284
4428
|
return { entries, providers, profiles, checks, plugins, ...runtime === void 0 ? {} : { runtime } };
|
|
4285
4429
|
}
|
|
4286
4430
|
function contractProvenance(previous) {
|
|
@@ -4536,6 +4680,25 @@ async function runtimeRemovalWarning(layout) {
|
|
|
4536
4680
|
"Export it first with: paseo-room export --out <dir>"
|
|
4537
4681
|
)];
|
|
4538
4682
|
}
|
|
4683
|
+
function markerPiExtensions(agents, choices) {
|
|
4684
|
+
if (!agents.includes("pi") || choices === void 0 || choices.length === 0) return void 0;
|
|
4685
|
+
return normalizePiExtensions(choices);
|
|
4686
|
+
}
|
|
4687
|
+
function piExtensionChanges(previous, agents, choices) {
|
|
4688
|
+
const before = previous?.agents.includes("pi") === true ? previous.piExtensions ?? [] : [];
|
|
4689
|
+
const now = markerPiExtensions(agents, choices) ?? [];
|
|
4690
|
+
const removed = before.flatMap((choice) => {
|
|
4691
|
+
const kept = now.find((item) => item.package === choice.package)?.roles ?? [];
|
|
4692
|
+
const dropped = choice.roles.filter((role) => !kept.includes(role));
|
|
4693
|
+
return dropped.length === 0 ? [] : [`${choice.package} (${dropped.join(", ")})`];
|
|
4694
|
+
});
|
|
4695
|
+
const keep = before.map((choice) => `--pi-extension ${choice.package}=${choice.roles.join(",")}`).join(" ");
|
|
4696
|
+
return removed.length === 0 ? [] : [warn(
|
|
4697
|
+
"pi.extensions-removed",
|
|
4698
|
+
`This setup removes Pi extensions the room loaded before: ${removed.join("; ")}.`,
|
|
4699
|
+
`To keep them, run setup again with: ${keep}`
|
|
4700
|
+
)];
|
|
4701
|
+
}
|
|
4539
4702
|
function markerMemoryContract(agents, memoryContract) {
|
|
4540
4703
|
return agents.includes("claude") ? memoryContract : void 0;
|
|
4541
4704
|
}
|
|
@@ -4551,8 +4714,12 @@ async function setup(options = {}) {
|
|
|
4551
4714
|
const layout = resolveLayout(options, options.env);
|
|
4552
4715
|
const agents = options.agents ?? ["codex"];
|
|
4553
4716
|
const memoryContract = options.claudeMemoryContract ?? true;
|
|
4717
|
+
const piExtensions = options.piExtensions ?? [];
|
|
4554
4718
|
const invalid = layoutChecks(layout, agents);
|
|
4555
4719
|
if (invalid.length > 0) return failed("setup", invalid);
|
|
4720
|
+
if (piExtensions.length > 0 && !agents.includes("pi")) {
|
|
4721
|
+
return failed("setup", [fail("pi.extension-without-pi", "--pi-extension selects Pi packages, but this setup does not seat Pi.", "Add --agent pi, or drop --pi-extension.")]);
|
|
4722
|
+
}
|
|
4556
4723
|
const rootSafety = await roomPathSafety(layout, [], []);
|
|
4557
4724
|
if (hasFailure(rootSafety)) return failed("setup", rootSafety);
|
|
4558
4725
|
const previous = await readMarker(layout);
|
|
@@ -4563,7 +4730,7 @@ async function setup(options = {}) {
|
|
|
4563
4730
|
const runtime = options.runtime === true;
|
|
4564
4731
|
const daemon = await checkDaemon(layout, options.env, minimumPaseoVersion(compatibilityAgents, runtime || previous?.runtime?.enabled === true));
|
|
4565
4732
|
if (!daemon.daemon) return failed("setup", daemon.checks);
|
|
4566
|
-
const desired = await buildDesired(layout, agents, ROLES, { memoryContract, runtime });
|
|
4733
|
+
const desired = await buildDesired(layout, agents, ROLES, { memoryContract, runtime, piExtensions });
|
|
4567
4734
|
const stale = await staleFrom(layout, previous, agents, ROLES, runtime);
|
|
4568
4735
|
const runtimeChecks = stale.removePlugins.some((spec) => spec.id === RUNTIME_PLUGIN_ID) ? await runtimeDeselectionCheck(layout) : [];
|
|
4569
4736
|
const retained = stale.retainedDirectories.length === 0 ? [] : [warn(
|
|
@@ -4571,7 +4738,7 @@ async function setup(options = {}) {
|
|
|
4571
4738
|
`Preserved ${String(stale.retainedDirectories.length)} deselected role homes because they may contain role-owned credentials or runtime state.`,
|
|
4572
4739
|
`Use paseo-room remove --apply to delete the entire room after reviewing its credential warning, or remove deselected homes manually: ${stale.retainedDirectories.join(", ")}`
|
|
4573
4740
|
)];
|
|
4574
|
-
const checks = [...daemon.checks, ...runtimeChecks, ...desired.checks, ...retained];
|
|
4741
|
+
const checks = [...daemon.checks, ...runtimeChecks, ...desired.checks, ...retained, ...piExtensionChanges(previous, agents, piExtensions)];
|
|
4575
4742
|
if (hasFailure(checks)) return failed("setup", checks);
|
|
4576
4743
|
return withSession(daemon.daemon, async (session) => {
|
|
4577
4744
|
const plan = await planRoom(session, desired, stale);
|
|
@@ -4603,13 +4770,15 @@ async function setup(options = {}) {
|
|
|
4603
4770
|
await session.refresh(ids);
|
|
4604
4771
|
const pluginChecks = await reconcilePlugins(session, desired, stale, plan, pending);
|
|
4605
4772
|
await applyEntries(markerEntries);
|
|
4773
|
+
const appliedPiExtensions = markerPiExtensions(agents, piExtensions);
|
|
4606
4774
|
const appliedMarker = {
|
|
4607
4775
|
version: package_default.version,
|
|
4608
4776
|
agents: [...agents],
|
|
4609
4777
|
roles: [...ROLES],
|
|
4610
4778
|
contract: contractDigest(),
|
|
4611
4779
|
...markerMemoryContract(agents, memoryContract) === false ? { claudeMemoryContract: false } : {},
|
|
4612
|
-
...desired.runtime === void 0 ? {} : { runtime: desired.runtime }
|
|
4780
|
+
...desired.runtime === void 0 ? {} : { runtime: desired.runtime },
|
|
4781
|
+
...appliedPiExtensions === void 0 ? {} : { piExtensions: appliedPiExtensions.map((choice) => ({ package: choice.package, roles: [...choice.roles] })) }
|
|
4613
4782
|
};
|
|
4614
4783
|
const appliedChecks = [
|
|
4615
4784
|
...checks,
|
|
@@ -4647,7 +4816,8 @@ async function verify(options = {}) {
|
|
|
4647
4816
|
const desired = await buildDesired(layout, marker.agents, marker.roles, {
|
|
4648
4817
|
// The room's own recorded choices, so verify compares against what setup wrote.
|
|
4649
4818
|
memoryContract: marker.claudeMemoryContract ?? true,
|
|
4650
|
-
runtime
|
|
4819
|
+
runtime,
|
|
4820
|
+
...marker.piExtensions === void 0 ? {} : { piExtensions: marker.piExtensions }
|
|
4651
4821
|
});
|
|
4652
4822
|
const runtimeChecks = runtime ? [marker.runtime?.generation === desired.runtime?.generation ? pass("runtime.generation", `Runtime manifest generation ${String(desired.runtime?.generation)} matches this package.`) : fail("runtime.generation", `This room was set up with runtime generation ${String(marker.runtime?.generation)}; this package generates ${String(desired.runtime?.generation)}.`, "Run: paseo-room setup --runtime --apply")] : [];
|
|
4653
4823
|
const checks = [...daemon.checks, ...runtimeChecks, ...desired.checks];
|
|
@@ -4845,6 +5015,20 @@ async function runWizard(prompts, emit, write, options = {}, onAction) {
|
|
|
4845
5015
|
if (typeof carrier === "symbol") return cancelled();
|
|
4846
5016
|
claudeMemoryContract = carrier === "both";
|
|
4847
5017
|
}
|
|
5018
|
+
let piExtensions = options.piExtensions;
|
|
5019
|
+
const recorded = agents.includes("pi") && piExtensions === void 0 ? (await readMarker(resolveLayout(options, options.env)))?.piExtensions ?? [] : [];
|
|
5020
|
+
if (recorded.length > 0) {
|
|
5021
|
+
const listed = recorded.map((choice) => `${choice.package} (${choice.roles.join(", ")})`).join("; ");
|
|
5022
|
+
const keep = await prompts.select({
|
|
5023
|
+
message: `Pi seats load these extensions: ${listed}. Keep them?`,
|
|
5024
|
+
options: [
|
|
5025
|
+
{ value: "keep", label: "Yes \u2014 keep loading them (recommended)" },
|
|
5026
|
+
{ value: "drop", label: "No \u2014 start Pi seats with the MCP adapter only" }
|
|
5027
|
+
]
|
|
5028
|
+
});
|
|
5029
|
+
if (typeof keep === "symbol") return cancelled();
|
|
5030
|
+
if (keep === "keep") piExtensions = recorded;
|
|
5031
|
+
}
|
|
4848
5032
|
const coordination = await prompts.select({
|
|
4849
5033
|
message: "Runtime coordination (preview): track assignments, Peer reports and writer ownership through a trusted Paseo plugin?",
|
|
4850
5034
|
options: [
|
|
@@ -4853,7 +5037,13 @@ async function runWizard(prompts, emit, write, options = {}, onAction) {
|
|
|
4853
5037
|
]
|
|
4854
5038
|
});
|
|
4855
5039
|
if (typeof coordination === "symbol") return cancelled();
|
|
4856
|
-
const setupOptions = {
|
|
5040
|
+
const setupOptions = {
|
|
5041
|
+
...options,
|
|
5042
|
+
agents,
|
|
5043
|
+
claudeMemoryContract,
|
|
5044
|
+
...coordination === "runtime" ? { runtime: true } : {},
|
|
5045
|
+
...piExtensions === void 0 ? {} : { piExtensions }
|
|
5046
|
+
};
|
|
4857
5047
|
const preview = await setup(setupOptions);
|
|
4858
5048
|
const status = emit(preview);
|
|
4859
5049
|
if (preview.outcome !== "changes-planned") return status;
|
|
@@ -4866,6 +5056,8 @@ var init_wizard = __esm({
|
|
|
4866
5056
|
"src/wizard.ts"() {
|
|
4867
5057
|
"use strict";
|
|
4868
5058
|
init_commands();
|
|
5059
|
+
init_layout();
|
|
5060
|
+
init_room();
|
|
4869
5061
|
init_roles();
|
|
4870
5062
|
terminalPrompts = {
|
|
4871
5063
|
select: (options) => clack.select({ message: options.message, options: [...options.options] }),
|
|
@@ -4887,7 +5079,7 @@ init_layout();
|
|
|
4887
5079
|
init_result();
|
|
4888
5080
|
init_schema();
|
|
4889
5081
|
init_project();
|
|
4890
|
-
import { copyFile, mkdir as
|
|
5082
|
+
import { copyFile, mkdir as mkdir5, readdir as readdir5, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
|
|
4891
5083
|
import { join as join17, resolve as resolve4 } from "path";
|
|
4892
5084
|
var LIMITATION = "Briefs, answers, reports and gate commands are stored as written and cannot be proven secret-free; review the export before sharing it.";
|
|
4893
5085
|
async function isEmptyOrAbsent(path) {
|
|
@@ -4958,9 +5150,9 @@ async function exportRuntime(options = {}) {
|
|
|
4958
5150
|
...summary.filter((entry) => entry.omitted.length > 0).map((entry) => warn("export.omitted", `Project ${entry.projectId}: ${String(entry.omitted.length)} unreadable event file(s) were left out by name: ${entry.omitted.join(", ")}.`))
|
|
4959
5151
|
];
|
|
4960
5152
|
if (!options.apply) return { command: "export", outcome: "changes-planned", changed: false, checks, operations };
|
|
4961
|
-
await
|
|
5153
|
+
await mkdir5(destination, { recursive: true, mode: 448 });
|
|
4962
5154
|
for (const [from, to] of copies) {
|
|
4963
|
-
await
|
|
5155
|
+
await mkdir5(join17(to, ".."), { recursive: true, mode: 448 });
|
|
4964
5156
|
await copyFile(from, to);
|
|
4965
5157
|
}
|
|
4966
5158
|
await writeFile3(join17(destination, "summary.json"), `${JSON.stringify({
|
|
@@ -5006,6 +5198,7 @@ function renderHuman(result) {
|
|
|
5006
5198
|
|
|
5007
5199
|
// src/cli.ts
|
|
5008
5200
|
init_result();
|
|
5201
|
+
init_pi();
|
|
5009
5202
|
init_roles();
|
|
5010
5203
|
init_prompts();
|
|
5011
5204
|
init_skills();
|
|
@@ -5015,6 +5208,13 @@ function collectAgent(value, previous) {
|
|
|
5015
5208
|
const seated = previous ?? [];
|
|
5016
5209
|
return seated.includes(agent) ? seated : [...seated, agent];
|
|
5017
5210
|
}
|
|
5211
|
+
function collectPiExtension(value, previous) {
|
|
5212
|
+
try {
|
|
5213
|
+
return [...previous ?? [], parsePiExtensionSpec(value)];
|
|
5214
|
+
} catch (error) {
|
|
5215
|
+
throw new InvalidArgumentError(error instanceof Error ? error.message : String(error));
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5018
5218
|
var PATH_FLAGS = ["roomHome", "codexHome", "claudeHome", "piHome", "codexBin", "claudeBin", "piBin", "paseoBin"];
|
|
5019
5219
|
function optionsFrom(raw, base) {
|
|
5020
5220
|
const paths = {};
|
|
@@ -5030,7 +5230,8 @@ function optionsFrom(raw, base) {
|
|
|
5030
5230
|
...raw.apply === true ? { apply: true } : {},
|
|
5031
5231
|
// Commander defaults a --no- flag to true, so only an explicit opt-out is carried.
|
|
5032
5232
|
...raw.claudeMemoryContract === false ? { claudeMemoryContract: false } : {},
|
|
5033
|
-
...raw.runtime === true ? { runtime: true } : {}
|
|
5233
|
+
...raw.runtime === true ? { runtime: true } : {},
|
|
5234
|
+
...Array.isArray(raw.piExtension) && raw.piExtension.length > 0 ? { piExtensions: raw.piExtension } : {}
|
|
5034
5235
|
};
|
|
5035
5236
|
}
|
|
5036
5237
|
function failedFromThrown(command, error) {
|
|
@@ -5091,7 +5292,7 @@ async function runCli(argv, output, context = {}) {
|
|
|
5091
5292
|
return emit(failedFromThrown(wizardCommand, error));
|
|
5092
5293
|
}
|
|
5093
5294
|
}
|
|
5094
|
-
const program = new Command().name("paseo-room").description("Configure Codex/Claude/Pi role homes in $HOME and register them with your local Paseo daemon.").version(package_default.version).usage("<setup|verify|remove|export> [options]\n auth login <codex|claude|pi> <supervisor|lead|peer> [options]").argument("<command>", "setup | verify | remove | export | auth").argument("[command-arguments...]", "auth login <agent> <role>").option("--agent <agent>", "codex, claude, or pi; repeat to combine (default: codex)", collectAgent).option("--apply", "actually make the changes (default: dry run)").option("--json", "machine-readable output").option("--no-claude-memory-contract", "omit the role contract from Claude role CLAUDE.md files, leaving the room plugin as the only Claude carrier").option("--runtime", "opt in to runtime coordination (preview): installs the trusted paseo-room-runtime plugin; omit it to deselect").option("--out <dir>", "export: destination directory (default: a new directory under the room's runtime exports)").option("--include-gate-output", "export: also copy bounded gate output tails (best-effort masked)").option("--room-home <path>", "where role homes are written (default: ~/.paseo-room)").option("--codex-home <path>", "source Codex config (default: ~/.codex)").option("--claude-home <path>", "source Claude Code config (default: ~/.claude)").option("--pi-home <path>", "source Pi config (default: ~/.pi/agent)").option("--codex-bin <path>", "Codex executable (default: found on PATH)").option("--claude-bin <path>", "Claude Code executable (default: found on PATH)").option("--pi-bin <path>", "Pi executable (default: found on PATH)").option("--paseo-bin <path>", "Paseo executable (default: found on PATH)").configureOutput({ writeOut: (text3) => {
|
|
5295
|
+
const program = new Command().name("paseo-room").description("Configure Codex/Claude/Pi role homes in $HOME and register them with your local Paseo daemon.").version(package_default.version).usage("<setup|verify|remove|export> [options]\n auth login <codex|claude|pi> <supervisor|lead|peer> [options]").argument("<command>", "setup | verify | remove | export | auth").argument("[command-arguments...]", "auth login <agent> <role>").option("--agent <agent>", "codex, claude, or pi; repeat to combine (default: codex)", collectAgent).option("--apply", "actually make the changes (default: dry run)").option("--json", "machine-readable output").option("--no-claude-memory-contract", "omit the role contract from Claude role CLAUDE.md files, leaving the room plugin as the only Claude carrier").option("--runtime", "opt in to runtime coordination (preview): installs the trusted paseo-room-runtime plugin; omit it to deselect").option("--pi-extension <package[=roles]>", "load an operator-installed Pi package in Pi seats; roles default to supervisor,lead (peer only when named); repeat for more", collectPiExtension).option("--out <dir>", "export: destination directory (default: a new directory under the room's runtime exports)").option("--include-gate-output", "export: also copy bounded gate output tails (best-effort masked)").option("--room-home <path>", "where role homes are written (default: ~/.paseo-room)").option("--codex-home <path>", "source Codex config (default: ~/.codex)").option("--claude-home <path>", "source Claude Code config (default: ~/.claude)").option("--pi-home <path>", "source Pi config (default: ~/.pi/agent)").option("--codex-bin <path>", "Codex executable (default: found on PATH)").option("--claude-bin <path>", "Claude Code executable (default: found on PATH)").option("--pi-bin <path>", "Pi executable (default: found on PATH)").option("--paseo-bin <path>", "Paseo executable (default: found on PATH)").configureOutput({ writeOut: (text3) => {
|
|
5095
5296
|
output.stdout(text3);
|
|
5096
5297
|
}, writeErr: (text3) => {
|
|
5097
5298
|
output.stderr(text3);
|
|
@@ -5124,6 +5325,11 @@ async function runCli(argv, output, context = {}) {
|
|
|
5124
5325
|
}
|
|
5125
5326
|
if (raw.runtime === true && (command === "verify" || command === "remove" || command === "export")) {
|
|
5126
5327
|
output.stderr(`paseo-room ${command}: --runtime is a setup choice recorded in the room marker; ${command} reads it from there.
|
|
5328
|
+
`);
|
|
5329
|
+
return 2;
|
|
5330
|
+
}
|
|
5331
|
+
if (raw.piExtension !== void 0 && command !== "setup") {
|
|
5332
|
+
output.stderr(`paseo-room ${command}: --pi-extension is a setup choice recorded in the room marker; ${command} reads it from there.
|
|
5127
5333
|
`);
|
|
5128
5334
|
return 2;
|
|
5129
5335
|
}
|