@pipod/cli 0.1.6 → 0.1.7
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 +6 -0
- package/dist/cli.js +183 -536
- package/dist/cli.js.map +1 -1
- package/man/pipod-config.5 +150 -0
- package/man/pipod-config.5.md +148 -0
- package/man/pipod.1 +861 -0
- package/man/pipod.1.md +852 -0
- package/package.json +9 -2
package/dist/cli.js
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from "./chunk-GNKF75X7.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
|
-
import * as
|
|
10
|
-
import * as
|
|
9
|
+
import * as fs36 from "fs";
|
|
10
|
+
import * as path34 from "path";
|
|
11
11
|
|
|
12
12
|
// src/errors.ts
|
|
13
13
|
var EXIT = {
|
|
@@ -4436,7 +4436,7 @@ function parseResourcePath(flag, value) {
|
|
|
4436
4436
|
function sameResourceList(requested, echoed) {
|
|
4437
4437
|
const want = requested ?? [];
|
|
4438
4438
|
const got = echoed ?? [];
|
|
4439
|
-
return want.length === got.length && want.every((
|
|
4439
|
+
return want.length === got.length && want.every((path35, index) => got[index] === path35);
|
|
4440
4440
|
}
|
|
4441
4441
|
|
|
4442
4442
|
// src/account/launch-seed.ts
|
|
@@ -6275,7 +6275,7 @@ function splitPiArgs(piArgs) {
|
|
|
6275
6275
|
if (key === "model" || key === "thinking") continue;
|
|
6276
6276
|
const flag = PI_RESOURCE_FLAGS[key];
|
|
6277
6277
|
if (!flag || !Array.isArray(paths)) continue;
|
|
6278
|
-
for (const
|
|
6278
|
+
for (const path35 of paths) managedArgs.push(flag, path35);
|
|
6279
6279
|
}
|
|
6280
6280
|
}
|
|
6281
6281
|
return {
|
|
@@ -7002,19 +7002,19 @@ function branchSessionStats(entries, state) {
|
|
|
7002
7002
|
function branchUsageTotals(entries) {
|
|
7003
7003
|
const totals = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
7004
7004
|
for (const entry of entries) {
|
|
7005
|
-
let
|
|
7005
|
+
let usage2;
|
|
7006
7006
|
if (entry.type === "message") {
|
|
7007
7007
|
const message = entry.message;
|
|
7008
|
-
if (message.role === "assistant" || message.role === "toolResult")
|
|
7008
|
+
if (message.role === "assistant" || message.role === "toolResult") usage2 = message.usage;
|
|
7009
7009
|
} else if (entry.type === "compaction" || entry.type === "branch_summary") {
|
|
7010
|
-
|
|
7010
|
+
usage2 = entry.usage;
|
|
7011
7011
|
}
|
|
7012
|
-
if (!
|
|
7013
|
-
totals.input +=
|
|
7014
|
-
totals.output +=
|
|
7015
|
-
totals.cacheRead +=
|
|
7016
|
-
totals.cacheWrite +=
|
|
7017
|
-
totals.cost +=
|
|
7012
|
+
if (!usage2) continue;
|
|
7013
|
+
totals.input += usage2.input ?? 0;
|
|
7014
|
+
totals.output += usage2.output ?? 0;
|
|
7015
|
+
totals.cacheRead += usage2.cacheRead ?? 0;
|
|
7016
|
+
totals.cacheWrite += usage2.cacheWrite ?? 0;
|
|
7017
|
+
totals.cost += usage2.cost?.total ?? 0;
|
|
7018
7018
|
}
|
|
7019
7019
|
return totals;
|
|
7020
7020
|
}
|
|
@@ -8632,11 +8632,11 @@ function extractAtPrefix(text) {
|
|
|
8632
8632
|
const tokenStart = lastDelimiterIndex === -1 ? 0 : lastDelimiterIndex + 1;
|
|
8633
8633
|
return text[tokenStart] === "@" ? text.slice(tokenStart) : null;
|
|
8634
8634
|
}
|
|
8635
|
-
function buildCompletionValue(
|
|
8636
|
-
return isQuotedPrefix ||
|
|
8635
|
+
function buildCompletionValue(path35, isQuotedPrefix) {
|
|
8636
|
+
return isQuotedPrefix || path35.includes(" ") ? `@"${path35}"` : `@${path35}`;
|
|
8637
8637
|
}
|
|
8638
|
-
function basename7(
|
|
8639
|
-
const trimmed =
|
|
8638
|
+
function basename7(path35) {
|
|
8639
|
+
const trimmed = path35.endsWith("/") ? path35.slice(0, -1) : path35;
|
|
8640
8640
|
const cut = trimmed.lastIndexOf("/");
|
|
8641
8641
|
return cut === -1 ? trimmed : trimmed.slice(cut + 1);
|
|
8642
8642
|
}
|
|
@@ -16560,8 +16560,8 @@ async function allJobs(client) {
|
|
|
16560
16560
|
beforeId = last.id;
|
|
16561
16561
|
}
|
|
16562
16562
|
}
|
|
16563
|
-
function assertArgCount(args, expected,
|
|
16564
|
-
if (args.length !== expected) throw new PiPodError(`usage: ${
|
|
16563
|
+
function assertArgCount(args, expected, usage2) {
|
|
16564
|
+
if (args.length !== expected) throw new PiPodError(`usage: ${usage2}`);
|
|
16565
16565
|
}
|
|
16566
16566
|
function shortSchedule(trigger) {
|
|
16567
16567
|
if (trigger.type === "cron") return trigger.cron;
|
|
@@ -16747,8 +16747,8 @@ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
16747
16747
|
var CHANGE_USAGE = "pipod billing change --plan standard|pro [--yes]";
|
|
16748
16748
|
var CHANGE_PREVIEW_USAGE = "pipod billing change preview --plan standard|pro";
|
|
16749
16749
|
var CHANGE_CONFIRM_USAGE = "pipod billing change confirm --quote <quoteId> [--yes]";
|
|
16750
|
-
function usageError(message,
|
|
16751
|
-
return new PiPodError(message, { hint: `usage: ${
|
|
16750
|
+
function usageError(message, usage2) {
|
|
16751
|
+
return new PiPodError(message, { hint: `usage: ${usage2}`, exitCode: EXIT.USAGE });
|
|
16752
16752
|
}
|
|
16753
16753
|
function parseChangeArgs(args) {
|
|
16754
16754
|
let form = "flow";
|
|
@@ -17945,11 +17945,11 @@ function formatStatus(status) {
|
|
|
17945
17945
|
const expires = status.state === "ready" && status.expiresAt ? ` (expires ${status.expiresAt})` : "";
|
|
17946
17946
|
return `${status.providerId} ${status.state.replaceAll("_", " ")}${expires}`;
|
|
17947
17947
|
}
|
|
17948
|
-
function rejectExtra(args,
|
|
17949
|
-
if (args.length > 0) throw new PiPodError(
|
|
17948
|
+
function rejectExtra(args, usage2) {
|
|
17949
|
+
if (args.length > 0) throw new PiPodError(usage2);
|
|
17950
17950
|
}
|
|
17951
|
-
function rejectProviderArgs(args,
|
|
17952
|
-
if (args.length !== 1 || args[0].startsWith("-")) throw new PiPodError(
|
|
17951
|
+
function rejectProviderArgs(args, usage2) {
|
|
17952
|
+
if (args.length !== 1 || args[0].startsWith("-")) throw new PiPodError(usage2);
|
|
17953
17953
|
}
|
|
17954
17954
|
function terminalInteraction() {
|
|
17955
17955
|
const controller = new AbortController();
|
|
@@ -19188,9 +19188,111 @@ function setDotPath(target, dotPath, value) {
|
|
|
19188
19188
|
cursor[parts.at(-1)] = value;
|
|
19189
19189
|
}
|
|
19190
19190
|
|
|
19191
|
-
// src/
|
|
19191
|
+
// src/manual.ts
|
|
19192
19192
|
import * as fs33 from "fs";
|
|
19193
19193
|
import * as path31 from "path";
|
|
19194
|
+
var MAIN_PAGE = "pipod.1.md";
|
|
19195
|
+
var FENCE = "```";
|
|
19196
|
+
function usage(command) {
|
|
19197
|
+
const entry = command === null ? section(mainPage(), 2, "SYNOPSIS") : commandEntry(command);
|
|
19198
|
+
if (entry === null) throw new PiPodError(`the pipod manual has no entry for ${command}`);
|
|
19199
|
+
const block = openingBlock(entry);
|
|
19200
|
+
const more = command !== null && hasMoreThanBlock(entry) ? `
|
|
19201
|
+
More: pipod help ${command}
|
|
19202
|
+
` : "";
|
|
19203
|
+
return `${block}${more}`;
|
|
19204
|
+
}
|
|
19205
|
+
function manualEntry(name) {
|
|
19206
|
+
const entry = commandEntry(name) ?? topicSection(name) ?? topicPage(name);
|
|
19207
|
+
if (entry === null) {
|
|
19208
|
+
throw new PiPodError(`no manual entry for "${name}"`, {
|
|
19209
|
+
hint: "`pipod help` lists the commands and topics",
|
|
19210
|
+
exitCode: EXIT.USAGE
|
|
19211
|
+
});
|
|
19212
|
+
}
|
|
19213
|
+
return plainText(entry);
|
|
19214
|
+
}
|
|
19215
|
+
function commandEntry(command) {
|
|
19216
|
+
const commands = section(mainPage(), 2, "COMMANDS");
|
|
19217
|
+
return commands === null ? null : section(commands, 3, `pipod ${command}`);
|
|
19218
|
+
}
|
|
19219
|
+
function topicSection(topic) {
|
|
19220
|
+
return /^[a-z]+$/.test(topic) ? section(mainPage(), 2, topic.toUpperCase()) : null;
|
|
19221
|
+
}
|
|
19222
|
+
function topicPage(topic) {
|
|
19223
|
+
const prefix = `pipod-${topic}.`;
|
|
19224
|
+
const file = fs33.readdirSync(manDir()).find(
|
|
19225
|
+
(name) => name.startsWith(prefix) && /^\d+\.md$/.test(name.slice(prefix.length))
|
|
19226
|
+
);
|
|
19227
|
+
return file === void 0 ? null : read(path31.join(manDir(), file));
|
|
19228
|
+
}
|
|
19229
|
+
function section(markdown, level, title) {
|
|
19230
|
+
const lines = markdown.split("\n");
|
|
19231
|
+
const heading = `${"#".repeat(level)} ${title}`;
|
|
19232
|
+
let start = -1;
|
|
19233
|
+
let fenced = false;
|
|
19234
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19235
|
+
const line = lines[i];
|
|
19236
|
+
if (line.startsWith(FENCE)) fenced = !fenced;
|
|
19237
|
+
if (fenced) continue;
|
|
19238
|
+
if (start < 0) {
|
|
19239
|
+
if (line === heading) start = i;
|
|
19240
|
+
continue;
|
|
19241
|
+
}
|
|
19242
|
+
const depth2 = /^(#+) /.exec(line)?.[1]?.length;
|
|
19243
|
+
if (depth2 !== void 0 && depth2 <= level) return `${lines.slice(start, i).join("\n").trimEnd()}
|
|
19244
|
+
`;
|
|
19245
|
+
}
|
|
19246
|
+
return start < 0 ? null : `${lines.slice(start).join("\n").trimEnd()}
|
|
19247
|
+
`;
|
|
19248
|
+
}
|
|
19249
|
+
function openingBlock(entry) {
|
|
19250
|
+
const lines = entry.split("\n");
|
|
19251
|
+
const open = lines.findIndex((line) => line.startsWith(FENCE));
|
|
19252
|
+
const close = lines.findIndex((line, i) => i > open && line.startsWith(FENCE));
|
|
19253
|
+
if (open < 0 || close < 0) throw new PiPodError("a pipod manual entry does not open with a usage block");
|
|
19254
|
+
return `${lines.slice(open + 1, close).join("\n")}
|
|
19255
|
+
`;
|
|
19256
|
+
}
|
|
19257
|
+
function hasMoreThanBlock(entry) {
|
|
19258
|
+
const lines = entry.split("\n");
|
|
19259
|
+
const fences = lines.flatMap((line, i) => line.startsWith(FENCE) ? [i] : []);
|
|
19260
|
+
return lines.slice((fences[1] ?? lines.length) + 1).some((line) => line.trim() !== "");
|
|
19261
|
+
}
|
|
19262
|
+
function plainText(markdown) {
|
|
19263
|
+
const out2 = [];
|
|
19264
|
+
let fenced = false;
|
|
19265
|
+
for (const line of markdown.split("\n")) {
|
|
19266
|
+
if (line.startsWith(FENCE)) {
|
|
19267
|
+
fenced = !fenced;
|
|
19268
|
+
continue;
|
|
19269
|
+
}
|
|
19270
|
+
if (fenced) out2.push(line === "" ? "" : ` ${line}`);
|
|
19271
|
+
else out2.push(line.replace(/^#+ /, ""));
|
|
19272
|
+
}
|
|
19273
|
+
return `${out2.join("\n").trimEnd()}
|
|
19274
|
+
`;
|
|
19275
|
+
}
|
|
19276
|
+
function mainPage() {
|
|
19277
|
+
return read(path31.join(manDir(), MAIN_PAGE));
|
|
19278
|
+
}
|
|
19279
|
+
function manDir() {
|
|
19280
|
+
return path31.join(packageRoot(), "man");
|
|
19281
|
+
}
|
|
19282
|
+
function read(file) {
|
|
19283
|
+
try {
|
|
19284
|
+
return fs33.readFileSync(file, "utf8");
|
|
19285
|
+
} catch (cause) {
|
|
19286
|
+
throw new PiPodError(`the pipod manual is missing from this install (${file})`, {
|
|
19287
|
+
hint: "reinstall pipod: `npm install -g @pipod/cli`, or `npm run build` in a checkout",
|
|
19288
|
+
cause
|
|
19289
|
+
});
|
|
19290
|
+
}
|
|
19291
|
+
}
|
|
19292
|
+
|
|
19293
|
+
// src/scaffold.ts
|
|
19294
|
+
import * as fs34 from "fs";
|
|
19295
|
+
import * as path32 from "path";
|
|
19194
19296
|
function configTemplate() {
|
|
19195
19297
|
return `{
|
|
19196
19298
|
// .pi-pod/config.json \u2014 optional server-side launch overrides for this project.
|
|
@@ -19232,8 +19334,8 @@ set -euo pipefail
|
|
|
19232
19334
|
echo "init complete"
|
|
19233
19335
|
`;
|
|
19234
19336
|
function scaffold(opts) {
|
|
19235
|
-
const dir =
|
|
19236
|
-
|
|
19337
|
+
const dir = path32.join(opts.projectRoot, CONFIG_DIR);
|
|
19338
|
+
fs34.mkdirSync(dir, { recursive: true });
|
|
19237
19339
|
const files = [
|
|
19238
19340
|
{ name: "config.json", contents: configTemplate() },
|
|
19239
19341
|
{ name: "env.example", contents: ENV_EXAMPLE_TEMPLATE },
|
|
@@ -19242,32 +19344,32 @@ function scaffold(opts) {
|
|
|
19242
19344
|
const created = [];
|
|
19243
19345
|
const skipped = [];
|
|
19244
19346
|
for (const file of files) {
|
|
19245
|
-
const target =
|
|
19246
|
-
if (
|
|
19247
|
-
skipped.push(
|
|
19347
|
+
const target = path32.join(dir, file.name);
|
|
19348
|
+
if (fs34.existsSync(target) && !opts.force) {
|
|
19349
|
+
skipped.push(path32.join(CONFIG_DIR, file.name));
|
|
19248
19350
|
continue;
|
|
19249
19351
|
}
|
|
19250
|
-
|
|
19251
|
-
if (file.mode)
|
|
19252
|
-
created.push(
|
|
19352
|
+
fs34.writeFileSync(target, file.contents, file.mode ? { mode: file.mode } : void 0);
|
|
19353
|
+
if (file.mode) fs34.chmodSync(target, file.mode);
|
|
19354
|
+
created.push(path32.join(CONFIG_DIR, file.name));
|
|
19253
19355
|
}
|
|
19254
19356
|
const gitignoreUpdated = ensureGitignoreEntry(opts.projectRoot, `${CONFIG_DIR}/env`);
|
|
19255
|
-
const envPath =
|
|
19256
|
-
const envCreated = !
|
|
19357
|
+
const envPath = path32.join(dir, "env");
|
|
19358
|
+
const envCreated = !fs34.existsSync(envPath);
|
|
19257
19359
|
if (envCreated) {
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
created.push(
|
|
19360
|
+
fs34.writeFileSync(envPath, ENV_TEMPLATE, { mode: 384 });
|
|
19361
|
+
fs34.chmodSync(envPath, 384);
|
|
19362
|
+
created.push(path32.join(CONFIG_DIR, "env"));
|
|
19261
19363
|
}
|
|
19262
19364
|
return { created, skipped, gitignoreUpdated, envCreated };
|
|
19263
19365
|
}
|
|
19264
19366
|
function ensureGitignoreEntry(projectRoot, entry) {
|
|
19265
|
-
const gitignorePath =
|
|
19266
|
-
const existing =
|
|
19367
|
+
const gitignorePath = path32.join(projectRoot, ".gitignore");
|
|
19368
|
+
const existing = fs34.existsSync(gitignorePath) ? fs34.readFileSync(gitignorePath, "utf8") : "";
|
|
19267
19369
|
const alreadyPresent = existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === entry || line === `/${entry}`);
|
|
19268
19370
|
if (alreadyPresent) return false;
|
|
19269
19371
|
const prefix = existing === "" || existing.endsWith("\n") ? "" : "\n";
|
|
19270
|
-
|
|
19372
|
+
fs34.appendFileSync(gitignorePath, `${prefix}
|
|
19271
19373
|
# pi pod secrets \u2014 never commit
|
|
19272
19374
|
${entry}
|
|
19273
19375
|
`);
|
|
@@ -19281,7 +19383,7 @@ function printInitResult(result, projectRoot) {
|
|
|
19281
19383
|
const steps = [
|
|
19282
19384
|
[`edit ${CONFIG_DIR}/config.json and init.sh`, "what this project's pods get"],
|
|
19283
19385
|
[`edit ${CONFIG_DIR}/env`, "this project's own keys"],
|
|
19284
|
-
["pipod", `launch a pod for ${
|
|
19386
|
+
["pipod", `launch a pod for ${path32.basename(projectRoot)}; the first launch offers to keep these in a template`]
|
|
19285
19387
|
].map(([command, why]) => `${command.padEnd(40)} ${color.dim(`# ${why}`)}`);
|
|
19286
19388
|
plain("");
|
|
19287
19389
|
plain("Next steps:");
|
|
@@ -19291,8 +19393,8 @@ function printInitResult(result, projectRoot) {
|
|
|
19291
19393
|
|
|
19292
19394
|
// src/update.ts
|
|
19293
19395
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
19294
|
-
import * as
|
|
19295
|
-
import * as
|
|
19396
|
+
import * as fs35 from "fs";
|
|
19397
|
+
import * as path33 from "path";
|
|
19296
19398
|
function run(cmd, args, cwd) {
|
|
19297
19399
|
const r = spawnSync3(cmd, args, { cwd, encoding: "utf8" });
|
|
19298
19400
|
return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}`.trim() };
|
|
@@ -19301,13 +19403,13 @@ function runVisible(cmd, args, cwd) {
|
|
|
19301
19403
|
return spawnSync3(cmd, args, { cwd, stdio: "inherit" }).status === 0;
|
|
19302
19404
|
}
|
|
19303
19405
|
function detectInstall(root = packageRoot()) {
|
|
19304
|
-
if (
|
|
19305
|
-
if (!root.split(
|
|
19406
|
+
if (fs35.existsSync(path33.join(root, ".git"))) return { kind: "git", root };
|
|
19407
|
+
if (!root.split(path33.sep).includes("node_modules") && run("git", ["rev-parse", "--is-inside-work-tree"], root).out === "true") {
|
|
19306
19408
|
return { kind: "git", root };
|
|
19307
19409
|
}
|
|
19308
|
-
if (root.split(
|
|
19410
|
+
if (root.split(path33.sep).includes("node_modules")) {
|
|
19309
19411
|
try {
|
|
19310
|
-
const pkg = JSON.parse(
|
|
19412
|
+
const pkg = JSON.parse(fs35.readFileSync(path33.join(root, "package.json"), "utf8"));
|
|
19311
19413
|
if (pkg.name) return { kind: "npm", root, name: pkg.name };
|
|
19312
19414
|
} catch {
|
|
19313
19415
|
}
|
|
@@ -19316,7 +19418,7 @@ function detectInstall(root = packageRoot()) {
|
|
|
19316
19418
|
}
|
|
19317
19419
|
function versionAt(root) {
|
|
19318
19420
|
try {
|
|
19319
|
-
const pkg = JSON.parse(
|
|
19421
|
+
const pkg = JSON.parse(fs35.readFileSync(path33.join(root, "package.json"), "utf8"));
|
|
19320
19422
|
return pkg.version ?? "0.0.0";
|
|
19321
19423
|
} catch {
|
|
19322
19424
|
return "0.0.0";
|
|
@@ -19391,16 +19493,16 @@ async function updateCheckout(root, opts) {
|
|
|
19391
19493
|
return rebuild(root, before);
|
|
19392
19494
|
}
|
|
19393
19495
|
function distIsStale(root) {
|
|
19394
|
-
const srcDir =
|
|
19395
|
-
if (!
|
|
19396
|
-
const dist =
|
|
19397
|
-
if (!
|
|
19398
|
-
const distTime =
|
|
19496
|
+
const srcDir = path33.join(root, "src");
|
|
19497
|
+
if (!fs35.existsSync(srcDir)) return false;
|
|
19498
|
+
const dist = path33.join(root, "dist", "cli.js");
|
|
19499
|
+
if (!fs35.existsSync(dist)) return true;
|
|
19500
|
+
const distTime = fs35.statSync(dist).mtimeMs;
|
|
19399
19501
|
const newest = (dir) => {
|
|
19400
19502
|
let max = 0;
|
|
19401
|
-
for (const entry of
|
|
19402
|
-
const p =
|
|
19403
|
-
max = Math.max(max, entry.isDirectory() ? newest(p) :
|
|
19503
|
+
for (const entry of fs35.readdirSync(dir, { withFileTypes: true })) {
|
|
19504
|
+
const p = path33.join(dir, entry.name);
|
|
19505
|
+
max = Math.max(max, entry.isDirectory() ? newest(p) : fs35.statSync(p).mtimeMs);
|
|
19404
19506
|
}
|
|
19405
19507
|
return max;
|
|
19406
19508
|
};
|
|
@@ -19421,8 +19523,8 @@ function rebuild(root, before) {
|
|
|
19421
19523
|
const head = run("git", ["rev-parse", "--short", "HEAD"], root).out;
|
|
19422
19524
|
plain("");
|
|
19423
19525
|
info(after === before ? `now at ${color.bold(after)} (${head})` : `updated ${before} \u2192 ${color.bold(after)} (${head})`);
|
|
19424
|
-
const upgrade =
|
|
19425
|
-
if (
|
|
19526
|
+
const upgrade = path33.join(run("git", ["rev-parse", "--show-toplevel"], root).out, "selfhost", "upgrade");
|
|
19527
|
+
if (fs35.existsSync(upgrade)) out(`if this checkout runs your server, upgrade it to match: ${upgrade}`);
|
|
19426
19528
|
out("run `pipod doctor` to check the new version against this repo");
|
|
19427
19529
|
}
|
|
19428
19530
|
async function updateFromRegistry(root, name, opts) {
|
|
@@ -19490,472 +19592,6 @@ var ALIASES = {
|
|
|
19490
19592
|
a: "attach",
|
|
19491
19593
|
ls: "list"
|
|
19492
19594
|
};
|
|
19493
|
-
var HELP = `pipod ${VERSION} \u2014 run pi sessions in server-managed pods
|
|
19494
|
-
|
|
19495
|
-
Usage:
|
|
19496
|
-
pipod [options] launch a pod for this project and start pi
|
|
19497
|
-
pipod [options] -- <pi args\u2026> launch and pass arguments to pi: prompt words,
|
|
19498
|
-
--model <id>, --thinking <level>, --tui-mode <mode>,
|
|
19499
|
-
-e/--extension <path>, --skill <path>,
|
|
19500
|
-
--prompt-template <path> (each repeatable)
|
|
19501
|
-
pipod <command> [options] run a command
|
|
19502
|
-
pipod help [command] show help (same as --help)
|
|
19503
|
-
|
|
19504
|
-
Sign in first with \`pipod login\`. A pod-scoped PI_POD_SERVER_TOKEN also counts as
|
|
19505
|
-
signed in. While signed out, only login, whoami, init, doctor, update, help, and
|
|
19506
|
-
version are available.
|
|
19507
|
-
|
|
19508
|
-
Pod commands:
|
|
19509
|
-
list [pod] List pods; \`list <pod>\` shows one machine's group (ls)
|
|
19510
|
-
attach [pod] Reconnect to a pod session (a)
|
|
19511
|
-
stop [pod\u2026] Stop pods now (files kept; attach restarts them)
|
|
19512
|
-
fork <pod> Launch a new pod forked from a pod's session
|
|
19513
|
-
rename <pod> <name> Rename a pod
|
|
19514
|
-
send [pod] <path> Copy host files into a pod
|
|
19515
|
-
receive [pod] <path> Copy pod files into the current directory
|
|
19516
|
-
archive [pod\u2026] Hide pods from the list (files kept; restore brings them back)
|
|
19517
|
-
restore [pod\u2026] Bring archived pods back
|
|
19518
|
-
gc Report retention state; --delete removes reclaimable pods
|
|
19519
|
-
|
|
19520
|
-
Account commands:
|
|
19521
|
-
login Sign in to a pi pod server via Zitadel
|
|
19522
|
-
logout Revoke this session and delete the stored credentials
|
|
19523
|
-
whoami Show the signed-in account
|
|
19524
|
-
account Open the Zitadel self-service console
|
|
19525
|
-
org-admin Open the Zitadel Console for this organization
|
|
19526
|
-
billing Open Stripe Checkout or the billing portal (authenticated)
|
|
19527
|
-
credentials Connect account model-provider sign-ins
|
|
19528
|
-
|
|
19529
|
-
Configuration commands:
|
|
19530
|
-
init Scaffold .pi-pod/ config, env, env.example, and init.sh
|
|
19531
|
-
templates Manage account templates
|
|
19532
|
-
push Upload a local config layer to its server bundle
|
|
19533
|
-
pull Write a server bundle into its local config layer
|
|
19534
|
-
diff Show local vs server drift for a config layer
|
|
19535
|
-
secrets Manage server and project secret scopes
|
|
19536
|
-
settings Show or edit one server settings config directly
|
|
19537
|
-
jobs Manage server-scheduled jobs
|
|
19538
|
-
|
|
19539
|
-
Maintenance commands:
|
|
19540
|
-
doctor Check auth, server, org, and launch-resolution layers
|
|
19541
|
-
update Upgrade pi pod itself
|
|
19542
|
-
|
|
19543
|
-
Launch options:
|
|
19544
|
-
--template <name|id> Launch from an account template
|
|
19545
|
-
--on <pod|self> Co-locate the new pod on an existing pod's machine
|
|
19546
|
-
--reuse Restart this project's newest stopped pod instead of creating one
|
|
19547
|
-
--no-seed Start the pod with an empty workspace (skip workspace seeding)
|
|
19548
|
-
--dry-run Resolve and print the launch plan; create nothing
|
|
19549
|
-
|
|
19550
|
-
General options:
|
|
19551
|
-
-y, --yes Skip supported confirmations
|
|
19552
|
-
-v, --verbose Debug logging (secrets redacted)
|
|
19553
|
-
--version Print CLI, Node, and bundled Pi versions
|
|
19554
|
-
-h, --help Show help
|
|
19555
|
-
|
|
19556
|
-
Settings resolve on the server: built-in \u2192 org \u2192 org-template \u2192 user \u2192 user-template
|
|
19557
|
-
\u2192 org policy. Each bundle has a local directory as its source of truth (see
|
|
19558
|
-
\`pipod push --help\`); local project settings apply after \`pipod push\`, and the
|
|
19559
|
-
first launch in a project offers to bootstrap a template.
|
|
19560
|
-
|
|
19561
|
-
Per-launch Pi resources (--extension, --skill, --prompt-template) name files or
|
|
19562
|
-
directories inside the pod as absolute paths and add to the pod's configured Pi
|
|
19563
|
-
resources for that launch; anything else Pi-related belongs in the pi.args config.
|
|
19564
|
-
|
|
19565
|
-
Workspace seeding: launching from a directory with no .pi-pod/config.json seeds the
|
|
19566
|
-
new pod from that directory (its git repository root when inside one). A clean,
|
|
19567
|
-
pushed checkout whose remote tip matches HEAD is cloned at that exact commit;
|
|
19568
|
-
anything else \u2014 dirty trees, unpushed commits, private remotes without usable
|
|
19569
|
-
credentials, plain directories \u2014 is streamed in as a tar archive (git-ignored
|
|
19570
|
-
paths, node_modules and .pi-pod/env excluded). Private-remote credentials are
|
|
19571
|
-
forwarded only after a prompt (\`--yes\` approves it) and are never stored.
|
|
19572
|
-
\`--dry-run\` prints the decision without prompting or creating a pod. Forks,
|
|
19573
|
-
reused pods, \`--on\` co-location and \`--no-seed\` never seed.
|
|
19574
|
-
|
|
19575
|
-
Run \`pipod <command> --help\` for command-specific usage.
|
|
19576
|
-
`;
|
|
19577
|
-
var SUBCOMMAND_HELP = {
|
|
19578
|
-
login: `Usage: pipod login [--server <url>] [--issuer <url>] [--org <alias>] [--device] [--token <jwt>]
|
|
19579
|
-
|
|
19580
|
-
Sign this machine in to a pi pod server via Zitadel (authorization code + PKCE,
|
|
19581
|
-
in your browser). Where no browser can open here (over SSH, on a headless
|
|
19582
|
-
server), it prints a page and a code to confirm on any other device instead.
|
|
19583
|
-
The session is stored in ~/.pi-pod/auth.json.
|
|
19584
|
-
|
|
19585
|
-
Options:
|
|
19586
|
-
--server <url> Server to sign in to (default: the previous session's server,
|
|
19587
|
-
$PI_POD_ACCOUNT_URL, or the production server)
|
|
19588
|
-
--issuer <url> OIDC issuer (default: $PI_POD_ISSUER, else the one the
|
|
19589
|
-
server publishes); needed only for a server too old to
|
|
19590
|
-
publish its own
|
|
19591
|
-
--org <alias> Only sign in to the organization with this primary domain;
|
|
19592
|
-
omit it to sign in to your own organization
|
|
19593
|
-
--device Sign in with a code on another device even if a browser
|
|
19594
|
-
could open here
|
|
19595
|
-
--token <jwt> Use an existing access token instead of the browser flow (dev/CI)
|
|
19596
|
-
`,
|
|
19597
|
-
account: `Usage: pipod account
|
|
19598
|
-
|
|
19599
|
-
Open the Zitadel self-service console in your browser to manage profile,
|
|
19600
|
-
password, MFA, and active sessions.
|
|
19601
|
-
`,
|
|
19602
|
-
"org-admin": `Usage: pipod org-admin
|
|
19603
|
-
|
|
19604
|
-
Open the Zitadel Console in your browser. Organization owners manage members
|
|
19605
|
-
and role grants there.
|
|
19606
|
-
`,
|
|
19607
|
-
billing: `Usage: pipod billing checkout [--plan standard|pro] [--trial]
|
|
19608
|
-
pipod billing portal
|
|
19609
|
-
pipod billing change --plan standard|pro [--yes]
|
|
19610
|
-
pipod billing change preview --plan standard|pro
|
|
19611
|
-
pipod billing change confirm --quote <quoteId> [--yes]
|
|
19612
|
-
|
|
19613
|
-
Authenticated Stripe Checkout and billing portal. Checkout sends trial:false
|
|
19614
|
-
(paid) unless --trial. Closed public trial refuses --trial with 402. Change
|
|
19615
|
-
previews a Standard<->Pro plan change and confirms it only after an explicit
|
|
19616
|
-
yes; stale quotes are never auto-confirmed. Return
|
|
19617
|
-
pages do not grant a plan. Public npm is not a supported install path; use a
|
|
19618
|
-
GitHub release tarball or git clone + npm run build.
|
|
19619
|
-
`,
|
|
19620
|
-
logout: `Usage: pipod logout
|
|
19621
|
-
|
|
19622
|
-
Revoke the current account session and delete ~/.pi-pod/auth.json. Pod
|
|
19623
|
-
operations are unavailable until the next \`pipod login\`.
|
|
19624
|
-
`,
|
|
19625
|
-
whoami: `Usage: pipod whoami
|
|
19626
|
-
|
|
19627
|
-
Show the signed-in server, user, permissions, active organization, and console
|
|
19628
|
-
links. Exits 1 when signed out.
|
|
19629
|
-
`,
|
|
19630
|
-
init: `Usage: pipod init [--force]
|
|
19631
|
-
|
|
19632
|
-
Scaffold project configuration under .pi-pod/ in the current directory:
|
|
19633
|
-
config.json, env.example, env (mode 600), and init.sh.
|
|
19634
|
-
|
|
19635
|
-
Options:
|
|
19636
|
-
--force Overwrite scaffold files that already exist
|
|
19637
|
-
`,
|
|
19638
|
-
doctor: `Usage: pipod doctor [--template <name|id>]
|
|
19639
|
-
|
|
19640
|
-
Check each layer a launch depends on: account auth, server reachability and
|
|
19641
|
-
version, organization membership and roles, secret references, and the
|
|
19642
|
-
server-resolved settings bundle chain for this directory. Works while signed
|
|
19643
|
-
out to diagnose auth problems. Exits non-zero when a layer fails.
|
|
19644
|
-
|
|
19645
|
-
Options:
|
|
19646
|
-
--template <name|id> Plan the checked launch with this template
|
|
19647
|
-
`,
|
|
19648
|
-
list: `Usage: pipod list [pod] [--all] [--archived] [--template <name|id>] [--quiet]
|
|
19649
|
-
|
|
19650
|
-
Alias: ls
|
|
19651
|
-
|
|
19652
|
-
List pods on the signed-in server with stable short refs (p-\u2026). The project is
|
|
19653
|
-
the directory's git repository (or the directory, or its .pi-pod/ config): a
|
|
19654
|
-
listing shows that project's pods when it has any \u2014 its pinned template's when
|
|
19655
|
-
it pins one \u2014 and every pod otherwise. Co-located pods are grouped under the pod
|
|
19656
|
-
whose machine they share, with an ON column naming it.
|
|
19657
|
-
With a pod argument, show just that machine's group: the pod and everything
|
|
19658
|
-
co-located on it.
|
|
19659
|
-
|
|
19660
|
-
Status says where a pod's files are: a stopped pod keeps them on disk and restarts on
|
|
19661
|
-
attach; an archived workspace is restored on next use, which takes longer the bigger it is.
|
|
19662
|
-
Pods you archive stay hidden unless --archived is passed.
|
|
19663
|
-
|
|
19664
|
-
Options:
|
|
19665
|
-
-a, --all Every pod; ignore the current project and its template pin
|
|
19666
|
-
--archived Include logically archived pods
|
|
19667
|
-
--template <name|id> Only pods launched from that template (whole account)
|
|
19668
|
-
-q, --quiet Print pod ids only, one per line
|
|
19669
|
-
`,
|
|
19670
|
-
attach: `Usage: pipod attach [pod] [--yes] [-- <prompt\u2026> [--tui-mode <mode>]]
|
|
19671
|
-
|
|
19672
|
-
Alias: a
|
|
19673
|
-
|
|
19674
|
-
Reconnect to a pod session through the server gateway. Name the pod by id,
|
|
19675
|
-
short ref (p-\u2026), or name; omit it when this project has exactly one pod.
|
|
19676
|
-
Quitting detaches: the pod keeps running until the server's idle policy reaps
|
|
19677
|
-
it or \`pipod stop\` stops it. Raw pod shells are not available.
|
|
19678
|
-
Attaching to an archived pod offers to restore it first.
|
|
19679
|
-
|
|
19680
|
-
After --:
|
|
19681
|
-
<prompt\u2026> Words are sent to the session as a startup prompt
|
|
19682
|
-
--tui-mode <mode> Local TUI layout for this attach: regular or fullscreen
|
|
19683
|
-
|
|
19684
|
-
Options:
|
|
19685
|
-
-y, --yes Restore an archived pod without asking
|
|
19686
|
-
`,
|
|
19687
|
-
stop: `Usage: pipod stop [pod\u2026]
|
|
19688
|
-
|
|
19689
|
-
Stop pods now by id, short ref (p-\u2026), or name; omit the argument when this
|
|
19690
|
-
project has exactly one pod. Stopped pods keep their files and \`pipod attach\`
|
|
19691
|
-
restarts them. Stopping a pod that hosts co-located pods stops those too.
|
|
19692
|
-
`,
|
|
19693
|
-
fork: `Usage: pipod fork <pod> [--session <path>] [launch options] [-- <pi args\u2026>]
|
|
19694
|
-
|
|
19695
|
-
Launch a new pod whose session starts as a fork of <pod>'s current session.
|
|
19696
|
-
<pod> is an id, short ref (p-\u2026), or name. The workspace is not copied \u2014
|
|
19697
|
-
commit and push first if the new pod needs those changes. --on is not
|
|
19698
|
-
supported: a fork launches a new sandbox, not a co-located pod.
|
|
19699
|
-
|
|
19700
|
-
Options:
|
|
19701
|
-
--session <path> Fork a specific session file instead of the newest
|
|
19702
|
-
--template <name|id> Launch the new pod from an account template
|
|
19703
|
-
--dry-run Resolve and print the launch plan; create nothing
|
|
19704
|
-
-y, --yes Skip supported confirmations
|
|
19705
|
-
`,
|
|
19706
|
-
rename: `Usage: pipod rename <pod> <name>
|
|
19707
|
-
|
|
19708
|
-
Rename a server-managed pod. <pod> is an id, short ref (p-\u2026), or the current
|
|
19709
|
-
name.
|
|
19710
|
-
`,
|
|
19711
|
-
send: `Usage: pipod send [pod] <path> [--yes]
|
|
19712
|
-
|
|
19713
|
-
Copy a host file or directory into a pod through the server. Omit the pod when
|
|
19714
|
-
this project has exactly one. Sending to an archived pod offers to restore it
|
|
19715
|
-
first.
|
|
19716
|
-
|
|
19717
|
-
Options:
|
|
19718
|
-
-y, --yes Restore an archived pod without asking
|
|
19719
|
-
`,
|
|
19720
|
-
receive: `Usage: pipod receive [pod] <path> [--yes]
|
|
19721
|
-
|
|
19722
|
-
Copy a file or directory from a pod into the current local directory. <path>
|
|
19723
|
-
may be a workdir-relative path or an absolute path inside that workdir, such
|
|
19724
|
-
as foo or /workspace/foo. The destination must not already exist. Omit the
|
|
19725
|
-
pod when this project has exactly one. Receiving from an archived pod offers
|
|
19726
|
-
to restore it first.
|
|
19727
|
-
|
|
19728
|
-
Options:
|
|
19729
|
-
-y, --yes Restore an archived pod without asking
|
|
19730
|
-
`,
|
|
19731
|
-
archive: `Usage: pipod archive [pod\u2026] [--all] [--template <name|id>] [--idle <duration>] [--dry-run] [--yes]
|
|
19732
|
-
|
|
19733
|
-
Archive pods by id, short ref (p-\u2026), or name. Archiving hides pods from
|
|
19734
|
-
\`pipod list\` (\`pipod list --archived\` shows them); it never deletes work, and
|
|
19735
|
-
\`pipod restore\` brings them back. Archiving a pod that hosts co-located pods
|
|
19736
|
-
archives those too.
|
|
19737
|
-
|
|
19738
|
-
Options:
|
|
19739
|
-
-a, --all Archive every pod (cannot be combined with pod refs)
|
|
19740
|
-
--template <name|id> Only pods launched from that template (whole account)
|
|
19741
|
-
--idle <duration> Only pods whose last activity is older than this (4h, 2d, 30m)
|
|
19742
|
-
--dry-run Print the matching pods; archive nothing
|
|
19743
|
-
-y, --yes Skip the bulk confirmation
|
|
19744
|
-
`,
|
|
19745
|
-
restore: `Usage: pipod restore [pod\u2026] [--all] [--yes]
|
|
19746
|
-
|
|
19747
|
-
Bring archived pods back by id, short ref (p-\u2026), or name. Restoring a host
|
|
19748
|
-
restores the co-located pods archived with it. \`pipod attach\` then starts the
|
|
19749
|
-
pod; a workspace in cold storage takes longer the bigger it is.
|
|
19750
|
-
|
|
19751
|
-
Options:
|
|
19752
|
-
-a, --all Restore every archived pod (cannot be combined with pod refs)
|
|
19753
|
-
-y, --yes Skip the --all confirmation
|
|
19754
|
-
`,
|
|
19755
|
-
gc: `Usage: pipod gc [--delete] [--yes]
|
|
19756
|
-
|
|
19757
|
-
List the archived and failed pods that can be deleted for good; --delete deletes
|
|
19758
|
-
them. (The server stops idle pods and moves stopped workspaces to cold storage on
|
|
19759
|
-
its own schedule; that never deletes anything.)
|
|
19760
|
-
|
|
19761
|
-
Options:
|
|
19762
|
-
--delete Permanently delete the reclaimable pods
|
|
19763
|
-
-y, --yes Delete without a confirmation prompt
|
|
19764
|
-
`,
|
|
19765
|
-
jobs: `Usage: pipod jobs [action] [args] [--quiet] [--yes] [--project] [--org]
|
|
19766
|
-
|
|
19767
|
-
Manage scheduled jobs the pi pod server runs for you: at each trigger it
|
|
19768
|
-
launches a pod from the job's template and sends the job's prompt. Local job
|
|
19769
|
-
files in .pi-pod/jobs/ (project) or ~/.pi-pod/jobs/ (user) are the reviewable
|
|
19770
|
-
source; \`jobs push\` promotes them to the server. <job> is an id, short ref
|
|
19771
|
-
(j-\u2026), or name.
|
|
19772
|
-
|
|
19773
|
-
Actions:
|
|
19774
|
-
list List server jobs (default)
|
|
19775
|
-
show <job> Show a job's schedule, template, model, and prompt
|
|
19776
|
-
runs <job> List a job's recent runs
|
|
19777
|
-
run <job> Run an active job once, now; its schedule is unchanged
|
|
19778
|
-
activate <job> Activate a job
|
|
19779
|
-
pause <job> Pause a job's schedule
|
|
19780
|
-
resume <job> Resume a paused job
|
|
19781
|
-
rm <job> Remove a job; its schedule stops firing
|
|
19782
|
-
push [job\u2026] Create or update server jobs from local job files
|
|
19783
|
-
pull <job> Write a server job to a local job file
|
|
19784
|
-
diff [job\u2026] Show local vs server drift; exits 1 when drifted
|
|
19785
|
-
|
|
19786
|
-
Options:
|
|
19787
|
-
--org push: create org-scoped jobs or promote matching user jobs
|
|
19788
|
-
(promotion is one-way)
|
|
19789
|
-
--project pull: write into this project's .pi-pod/jobs/ instead of
|
|
19790
|
-
~/.pi-pod/jobs/
|
|
19791
|
-
-q, --quiet list: print job ids only, one per line
|
|
19792
|
-
-y, --yes Skip confirmations; pull: overwrite an existing local file
|
|
19793
|
-
`,
|
|
19794
|
-
credentials: `Usage: pipod credentials [action]
|
|
19795
|
-
|
|
19796
|
-
Manage account-scoped model-provider sign-ins. Connected credentials apply to
|
|
19797
|
-
every pod you launch; OAuth refresh tokens stay in server custody and never
|
|
19798
|
-
reach pods or this machine.
|
|
19799
|
-
|
|
19800
|
-
Actions:
|
|
19801
|
-
list Saved credentials and connectable providers (default)
|
|
19802
|
-
connect Connect every provider your local pi is signed in to:
|
|
19803
|
-
API keys are imported, everything else is a fresh
|
|
19804
|
-
account login; ready credentials are left alone
|
|
19805
|
-
connect <provider> Sign in to a provider (OAuth by default)
|
|
19806
|
-
reconnect <provider> Redo the sign-in for a saved credential
|
|
19807
|
-
test <provider> Verify a saved credential against the provider
|
|
19808
|
-
remove <provider> [--yes] Delete a saved credential (alias: rm)
|
|
19809
|
-
|
|
19810
|
-
connect options:
|
|
19811
|
-
--api-key Enter an API key instead of the OAuth flow
|
|
19812
|
-
--api-key --from-pi Import the API key your local pi resolves for that
|
|
19813
|
-
provider (auth.json or env) instead of typing it.
|
|
19814
|
-
OAuth grants are never imported.
|
|
19815
|
-
`,
|
|
19816
|
-
secrets: `Usage: pipod secrets [action]
|
|
19817
|
-
|
|
19818
|
-
Manage the secret layers pods see. Requires account or pod-token auth. Server
|
|
19819
|
-
scopes are org, user, and template/<name>; project is this project's dotenv
|
|
19820
|
-
file, the highest-precedence layer for launches from the project.
|
|
19821
|
-
|
|
19822
|
-
Actions:
|
|
19823
|
-
list Every secret a pod launched here would see,
|
|
19824
|
-
which layer wins, and who set it (default)
|
|
19825
|
-
set <scope> <NAME> Store a secret, its value read from a hidden
|
|
19826
|
-
prompt or stdin (never the command line)
|
|
19827
|
-
rm <scope> <NAME> Remove a secret (aliases: remove, unset)
|
|
19828
|
-
sync <scope> Upload a dotenv file to a server scope
|
|
19829
|
-
|
|
19830
|
-
sync options (scope must be org, user, or template/<name>):
|
|
19831
|
-
--file <path> Dotenv file to upload (default: ~/.pi-pod/secrets/<scope>.env)
|
|
19832
|
-
--prune Also delete server secrets that are not in the file
|
|
19833
|
-
--dry-run Show what would change without writing
|
|
19834
|
-
|
|
19835
|
-
Values may be op:// references; they resolve just-in-time (at upload for
|
|
19836
|
-
server scopes, at launch for the project file) and the references themselves
|
|
19837
|
-
are never stored on the server.
|
|
19838
|
-
`,
|
|
19839
|
-
templates: `Usage: pipod templates [action]
|
|
19840
|
-
|
|
19841
|
-
Manage pod templates on the pi pod server. A template stores config, init and
|
|
19842
|
-
bake scripts, Pi settings files, named secrets, and agent instructions.
|
|
19843
|
-
Templates are personal by default: only their owner sees and launches them.
|
|
19844
|
-
--org at creation or \`templates share\` makes one org-wide.
|
|
19845
|
-
|
|
19846
|
-
Agent instructions are added to the agent's system prompt in every pod launched
|
|
19847
|
-
from the template, with the hosts and secrets the pod actually has: say what
|
|
19848
|
-
access the template is meant to have. They guide the agent and enforce nothing;
|
|
19849
|
-
the template's secrets and egress decide what a pod can reach. A pod's child
|
|
19850
|
-
pods and the jobs it schedules launch from the pod's own template.
|
|
19851
|
-
|
|
19852
|
-
Actions:
|
|
19853
|
-
list List templates with scope and key counts (default)
|
|
19854
|
-
show <name|id> Show config, scripts, agent instructions, Pi settings, and
|
|
19855
|
-
secret names
|
|
19856
|
-
create <name> Create a template
|
|
19857
|
-
edit <name|id> Update a template's name, description, config, scripts, or
|
|
19858
|
-
agent instructions
|
|
19859
|
-
share <name|id> Make a personal template org-wide (cannot be undone)
|
|
19860
|
-
rm <name|id> Delete a template (alias: delete)
|
|
19861
|
-
|
|
19862
|
-
create/edit options:
|
|
19863
|
-
--name <name> New name (edit only)
|
|
19864
|
-
--description <text> Set the description
|
|
19865
|
-
--config <file> Store this config file (same shape as .pi-pod/config.json)
|
|
19866
|
-
--from-here create only: store this project's authored config,
|
|
19867
|
-
scripts, and sanitized Pi files (contradicts --config)
|
|
19868
|
-
--init-script <file> Store this init script
|
|
19869
|
-
--bake-script <file> Store this bake script
|
|
19870
|
-
--agent-instructions <file>
|
|
19871
|
-
Store this file as the agent instructions (an empty
|
|
19872
|
-
file removes them); \`pipod push\` leaves them alone
|
|
19873
|
-
--org create only: make the template org-wide from the start
|
|
19874
|
-
--with-secrets With --from-here: also upload project env values as
|
|
19875
|
-
template secrets
|
|
19876
|
-
-y, --yes Skip confirmations (rm)
|
|
19877
|
-
`,
|
|
19878
|
-
push: `Usage: pipod push [template [<name>] | user | org] [--dir <path>]
|
|
19879
|
-
[--with-secrets] [--yes]
|
|
19880
|
-
|
|
19881
|
-
Replace a server config bundle with its local source, whole. The layer names
|
|
19882
|
-
both sides; edit the local files with any editor, then push. A diff preview
|
|
19883
|
-
precedes the confirmation.
|
|
19884
|
-
|
|
19885
|
-
Layers and their local source:
|
|
19886
|
-
template [<name>] This project: .pi-pod/config.json, its init and bake
|
|
19887
|
-
scripts, Pi files under .pi/, and the project env file
|
|
19888
|
-
(secrets, with --with-secrets or a prompt). Without
|
|
19889
|
-
<name> the project's pinned template is used.
|
|
19890
|
-
user ~/.pi-pod/config.json and ~/.pi/agent/ (settings.json,
|
|
19891
|
-
models.json, mcporter.json, subagents.json, agents/)
|
|
19892
|
-
org ~/.pi-pod/org/: config.json, init.sh, bake.sh, pi/\u2026, and
|
|
19893
|
-
an optional env file whose values become org secrets
|
|
19894
|
-
|
|
19895
|
-
Inside a project the layer defaults to template; elsewhere it is required.
|
|
19896
|
-
Provider credentials, hooks, mcpServers, and model credential values never
|
|
19897
|
-
leave this machine.
|
|
19898
|
-
|
|
19899
|
-
Options:
|
|
19900
|
-
--dir <path> org only: read the org source from <path> instead
|
|
19901
|
-
--with-secrets Also upload the layer's env values as secrets
|
|
19902
|
-
-y, --yes Apply the replacement without the confirmation prompt
|
|
19903
|
-
`,
|
|
19904
|
-
pull: `Usage: pipod pull [template [<name>] | user | org] [--dir <path>] [--yes]
|
|
19905
|
-
|
|
19906
|
-
Write a server config bundle into its local source files (see
|
|
19907
|
-
\`pipod push --help\` for the layer \u2192 directory table). A diff preview
|
|
19908
|
-
precedes the confirmation; nothing is written when the two already match.
|
|
19909
|
-
|
|
19910
|
-
What pull keeps from the local files:
|
|
19911
|
-
config.json client-only keys: $schema, template, secretResolver,
|
|
19912
|
-
pi.chords, pi.sessionNaming
|
|
19913
|
-
settings.json hooks and mcpServers (and packages when hostConfig.packages
|
|
19914
|
-
is off)
|
|
19915
|
-
models.json credential values the push had dropped
|
|
19916
|
-
agents/ files the bundle no longer contains are removed
|
|
19917
|
-
|
|
19918
|
-
Secrets stay on the server; their names are listed. The user layer has no
|
|
19919
|
-
local script files, so user bundle scripts are shown but not written.
|
|
19920
|
-
|
|
19921
|
-
Options:
|
|
19922
|
-
--dir <path> org only: write the org source to <path> instead
|
|
19923
|
-
-y, --yes Apply the local changes without the confirmation prompt
|
|
19924
|
-
`,
|
|
19925
|
-
diff: `Usage: pipod diff [template [<name>] | user | org] [--dir <path>]
|
|
19926
|
-
|
|
19927
|
-
Compare a config layer's local source with its server bundle (see
|
|
19928
|
-
\`pipod push --help\` for the layer \u2192 directory table). Secrets compare by
|
|
19929
|
-
name only. Exits 1 when they differ, 0 when they match.
|
|
19930
|
-
|
|
19931
|
-
Options:
|
|
19932
|
-
--dir <path> org only: read the org source from <path> instead
|
|
19933
|
-
`,
|
|
19934
|
-
settings: `Usage: pipod settings <user|org> [show | edit | set <key> <value> | unset <key>]
|
|
19935
|
-
|
|
19936
|
-
Show or edit a persistent settings bundle on the server: your own user bundle,
|
|
19937
|
-
or the org-wide defaults (org scope needs the org:manage permission). Edits
|
|
19938
|
-
change only the config; scripts and Pi files stay as they are \u2014 replace the
|
|
19939
|
-
whole bundle from local files with \`pipod push user\` or \`pipod push org\`.
|
|
19940
|
-
Edits use compare-and-swap on the bundle version, so concurrent changes fail
|
|
19941
|
-
cleanly instead of overwriting each other.
|
|
19942
|
-
|
|
19943
|
-
Actions:
|
|
19944
|
-
show Print the settings bundle as JSON (default)
|
|
19945
|
-
edit Edit the config in $VISUAL/$EDITOR, then save
|
|
19946
|
-
set <key> <value> Set one dotted key to a JSON value, or to the text as
|
|
19947
|
-
a string: pipod settings user set pi.model provider/model
|
|
19948
|
-
unset <key> Remove one dotted key, so the layers below decide it
|
|
19949
|
-
`,
|
|
19950
|
-
update: `Usage: pipod update [--dry-run]
|
|
19951
|
-
|
|
19952
|
-
Upgrade pi pod itself: a registry install updates to the latest published
|
|
19953
|
-
version; a git checkout pulls its upstream and rebuilds.
|
|
19954
|
-
|
|
19955
|
-
Options:
|
|
19956
|
-
--dry-run Report what an update would do without changing anything
|
|
19957
|
-
`
|
|
19958
|
-
};
|
|
19959
19595
|
var SELF_PARSING_SUBCOMMANDS = /* @__PURE__ */ new Set(["credentials", "secrets", "templates", "push", "pull", "diff", "settings", "billing"]);
|
|
19960
19596
|
var UNIVERSAL_FLAGS = /* @__PURE__ */ new Set(["--verbose", "--version", "--help"]);
|
|
19961
19597
|
var FLAG_SCOPE = {
|
|
@@ -20131,15 +19767,22 @@ function parseArgs(argv) {
|
|
|
20131
19767
|
});
|
|
20132
19768
|
}
|
|
20133
19769
|
}
|
|
20134
|
-
let commandPositionals = positionals;
|
|
20135
19770
|
if (positionals[0] === "help") {
|
|
20136
19771
|
flags.help = true;
|
|
20137
19772
|
seenFlags.add("--help");
|
|
20138
|
-
|
|
19773
|
+
const name = positionals[1];
|
|
19774
|
+
return {
|
|
19775
|
+
subcommand: null,
|
|
19776
|
+
subcommandArgs: [],
|
|
19777
|
+
flags,
|
|
19778
|
+
piArgs,
|
|
19779
|
+
seenFlags,
|
|
19780
|
+
...name !== void 0 ? { helpEntry: resolveSubcommand(name) ?? name } : {}
|
|
19781
|
+
};
|
|
20139
19782
|
}
|
|
20140
|
-
const subcommand = resolveSubcommand(
|
|
20141
|
-
if (!subcommand &&
|
|
20142
|
-
throw new PiPodError(`unknown pi pod command "${
|
|
19783
|
+
const subcommand = resolveSubcommand(positionals[0]);
|
|
19784
|
+
if (!subcommand && positionals.length > 0) {
|
|
19785
|
+
throw new PiPodError(`unknown pi pod command "${positionals[0]}"`, {
|
|
20143
19786
|
hint: 'pass all Pi arguments after `--`, for example: pipod -- "review this change"',
|
|
20144
19787
|
exitCode: EXIT.USAGE
|
|
20145
19788
|
});
|
|
@@ -20152,7 +19795,7 @@ function parseArgs(argv) {
|
|
|
20152
19795
|
}
|
|
20153
19796
|
return {
|
|
20154
19797
|
subcommand,
|
|
20155
|
-
subcommandArgs: subcommand ?
|
|
19798
|
+
subcommandArgs: subcommand ? positionals.slice(1) : [],
|
|
20156
19799
|
flags,
|
|
20157
19800
|
piArgs,
|
|
20158
19801
|
seenFlags
|
|
@@ -20175,10 +19818,14 @@ function canonicalFlagName(name) {
|
|
|
20175
19818
|
}
|
|
20176
19819
|
}
|
|
20177
19820
|
async function main(argv) {
|
|
20178
|
-
const { subcommand, subcommandArgs, flags, piArgs, seenFlags } = parseArgs(argv);
|
|
19821
|
+
const { subcommand, subcommandArgs, flags, piArgs, seenFlags, helpEntry } = parseArgs(argv);
|
|
20179
19822
|
setVerbose(flags.verbose);
|
|
20180
19823
|
if (flags.help) {
|
|
20181
|
-
process.stdout.write(
|
|
19824
|
+
if (helpEntry !== void 0) process.stdout.write(manualEntry(helpEntry));
|
|
19825
|
+
else if (subcommand) process.stdout.write(usage(subcommand));
|
|
19826
|
+
else process.stdout.write(`pipod ${VERSION} \u2014 run pi sessions in server-managed pods
|
|
19827
|
+
|
|
19828
|
+
${usage(null)}`);
|
|
20182
19829
|
return EXIT.OK;
|
|
20183
19830
|
}
|
|
20184
19831
|
for (const flag of checkFlagScope(subcommand, seenFlags)) {
|
|
@@ -20291,7 +19938,7 @@ function announceUserConfig() {
|
|
|
20291
19938
|
function runInit(flags) {
|
|
20292
19939
|
const cwd = process.cwd();
|
|
20293
19940
|
const existing = findConfigPath(cwd);
|
|
20294
|
-
if (existing !== null &&
|
|
19941
|
+
if (existing !== null && path34.dirname(path34.dirname(existing)) !== fs36.realpathSync(cwd)) {
|
|
20295
19942
|
info(`note: ${color.bold(displayPath(existing))} defines an enclosing project; this scaffold shadows it here`);
|
|
20296
19943
|
}
|
|
20297
19944
|
const result = scaffold({ projectRoot: cwd, force: flags.force });
|
|
@@ -20319,7 +19966,7 @@ function reportError(errorValue) {
|
|
|
20319
19966
|
if (errorValue instanceof Error && errorValue.stack) plain(color.dim(errorValue.stack));
|
|
20320
19967
|
return EXIT.FAILURE;
|
|
20321
19968
|
}
|
|
20322
|
-
var invokedDirectly = process.argv[1] !== void 0 && ["cli.js", "cli.ts", "pipod", "pi-pod", "pp"].includes(
|
|
19969
|
+
var invokedDirectly = process.argv[1] !== void 0 && ["cli.js", "cli.ts", "pipod", "pi-pod", "pp"].includes(path34.basename(process.argv[1]));
|
|
20323
19970
|
if (invokedDirectly) {
|
|
20324
19971
|
for (const stream of [process.stdout, process.stderr]) {
|
|
20325
19972
|
stream.on("error", (failure) => {
|