@pipod/cli 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -18958,7 +18958,7 @@ async function fetchRemoteLayer(client, layer, source) {
18958
18958
  const ref = layer.name ?? source.templateRef;
18959
18959
  if (!ref) {
18960
18960
  throw new PiPodError("this project does not pin a template", {
18961
- hint: "name one: `pipod push template <name>`; a first launch offers to bootstrap and pin one"
18961
+ hint: "name one after `template`, as in `template <name>`; a first launch offers to bootstrap and pin one"
18962
18962
  });
18963
18963
  }
18964
18964
  const template = await findTemplate(client, ref);
@@ -18968,6 +18968,7 @@ async function fetchRemoteLayer(client, layer, source) {
18968
18968
  bundle: templateBundle(template),
18969
18969
  version,
18970
18970
  secretNames: listed2.secrets.map((entry) => entry.name),
18971
+ name: `template ${template.name}`,
18971
18972
  label: `template ${template.name}${version !== void 0 ? ` v${version}` : ""}`,
18972
18973
  template,
18973
18974
  scope: "template",
@@ -18980,6 +18981,7 @@ async function fetchRemoteLayer(client, layer, source) {
18980
18981
  bundle: settingsBundle(current2),
18981
18982
  version: current2.version,
18982
18983
  secretNames: [],
18984
+ name: "org policy",
18983
18985
  label: `org policy v${current2.version}`,
18984
18986
  scope: "policy",
18985
18987
  scopeId: client.orgId
@@ -18992,11 +18994,25 @@ async function fetchRemoteLayer(client, layer, source) {
18992
18994
  bundle: settingsBundle(current),
18993
18995
  version: current.version,
18994
18996
  secretNames: listed.secrets.map((entry) => entry.name),
18997
+ name: `${layer.kind} settings`,
18995
18998
  label: `${layer.kind} settings v${current.version}`,
18996
18999
  scope: layer.kind,
18997
19000
  scopeId
18998
19001
  };
18999
19002
  }
19003
+ async function writeLayerConfig(client, remote, config) {
19004
+ if (remote.scope === "template") {
19005
+ const updated = await client.updateTemplate(remote.scopeId, {
19006
+ ...remote.version !== void 0 ? { expectedVersion: remote.version } : {},
19007
+ config
19008
+ });
19009
+ return updated.version;
19010
+ }
19011
+ if (remote.version === void 0) throw new PiPodError(`${remote.label} has no version to compare-and-swap against`);
19012
+ const body = { config, version: remote.version };
19013
+ const written = remote.scope === "policy" ? await client.putOrgPolicy(body) : remote.scope === "user" ? await client.putUserSettings(body) : await client.putOrgSettings(body);
19014
+ return written.version;
19015
+ }
19000
19016
  function localLabel(layer, flags) {
19001
19017
  if (layer.kind === "template") return "project";
19002
19018
  if (layer.kind === "user") return "~/.pi-pod + ~/.pi/agent";
@@ -19097,12 +19113,12 @@ async function runPush(args, flags) {
19097
19113
  await syncEnvSecrets(flags.client, updated, source.env, { withSecrets: parsed.withSecrets, reportStale: true });
19098
19114
  return 0;
19099
19115
  }
19100
- if (remote.version === void 0) throw new PiPodError(`${remote.label} has no version to compare-and-swap against`);
19101
19116
  if (remote.scope === "policy") {
19102
- const written2 = await flags.client.putOrgPolicy({ config: source.config, version: remote.version });
19103
- info(`pushed ${local} to org policy v${written2.version}`);
19117
+ const version = await writeLayerConfig(flags.client, remote, source.config);
19118
+ info(`pushed ${local} to org policy v${version}`);
19104
19119
  return 0;
19105
19120
  }
19121
+ if (remote.version === void 0) throw new PiPodError(`${remote.label} has no version to compare-and-swap against`);
19106
19122
  const body = {
19107
19123
  config: source.config,
19108
19124
  version: remote.version,
@@ -19147,53 +19163,93 @@ import * as fs32 from "fs";
19147
19163
  import * as os7 from "os";
19148
19164
  import * as path30 from "path";
19149
19165
  import { spawnSync as spawnSync2 } from "child_process";
19150
- var USAGE = "usage: pipod settings <user|org> [show | edit | set <key> <value> | unset <key>]";
19151
- function readBundle(client, scope) {
19152
- return scope === "user" ? client.getUserSettings() : client.getOrgSettings();
19166
+ var USAGE = "usage: pipod settings [user | org | policy | template [<name>]] [show | edit | set <key> <value> | unset <key>]";
19167
+ var ACTIONS = ["show", "edit", "set", "unset"];
19168
+ async function runSettings(args, flags) {
19169
+ const { layer, rest } = parseLayer(args);
19170
+ const command = parseCommand(layer, rest);
19171
+ const remote = await fetchRemoteLayer(flags.client, layer, {
19172
+ templateRef: layer.kind === "template" && layer.name === void 0 ? pinnedTemplateRef(flags) : null
19173
+ });
19174
+ if (command.action === "show") {
19175
+ out(JSON.stringify({ ...remote.bundle, version: remote.version }, null, 2));
19176
+ return 0;
19177
+ }
19178
+ const config = structuredClone(remote.bundle.config);
19179
+ if (command.action === "set") setDotPath(config, command.key, command.value);
19180
+ if (command.action === "unset") unsetDotPath(config, command.key);
19181
+ const next = command.action === "edit" ? editInEditor(config, flags) : config;
19182
+ assertConfigSendable(remote, next);
19183
+ const version = await writeLayerConfig(flags.client, remote, next);
19184
+ const done = command.action === "edit" ? "updated" : command.action;
19185
+ const key = command.action === "edit" ? "" : ` ${command.key}`;
19186
+ info(`${done} ${remote.name}${key}${version === void 0 ? "" : ` (v${version})`}`);
19187
+ return 0;
19188
+ }
19189
+ function parseLayer(args) {
19190
+ const [scope = "user", ...rest] = args;
19191
+ if (scope === "user" || scope === "org" || scope === "policy") return { layer: { kind: scope }, rest };
19192
+ if (scope === "template") {
19193
+ const [name, ...afterName] = rest;
19194
+ return name === void 0 || ACTIONS.includes(name) ? { layer: { kind: "template" }, rest } : { layer: { kind: "template", name }, rest: afterName };
19195
+ }
19196
+ throw new PiPodError(`unknown settings layer "${scope}"`, { hint: USAGE });
19197
+ }
19198
+ function parseCommand(layer, args) {
19199
+ const words = layer.kind === "template" && layer.name !== void 0 ? `template ${layer.name}` : layer.kind;
19200
+ const [action = "show", ...rest] = args;
19201
+ switch (action) {
19202
+ case "show":
19203
+ case "edit":
19204
+ if (rest.length > 0) throw new PiPodError(`usage: pipod settings ${words} ${action}`);
19205
+ return { action };
19206
+ case "set": {
19207
+ const [key, raw, ...extra] = rest;
19208
+ if (!key || raw === void 0 || extra.length > 0) throw new PiPodError(`usage: pipod settings ${words} set <key> <value>`);
19209
+ let value;
19210
+ try {
19211
+ value = JSON.parse(raw);
19212
+ } catch {
19213
+ value = raw;
19214
+ }
19215
+ return { action, key, value };
19216
+ }
19217
+ case "unset": {
19218
+ const [key, ...extra] = rest;
19219
+ if (!key || extra.length > 0) throw new PiPodError(`usage: pipod settings ${words} unset <key>`);
19220
+ return { action, key };
19221
+ }
19222
+ default:
19223
+ throw new PiPodError(`unknown settings action "${action}"`, { hint: "actions: show, edit, set, unset" });
19224
+ }
19153
19225
  }
19154
- function writeBundle(client, scope, body) {
19155
- return scope === "user" ? client.putUserSettings(body) : client.putOrgSettings(body);
19226
+ function pinnedTemplateRef(flags) {
19227
+ const configPath = findConfigPath(flags.cwd ?? process.cwd(), { home: flags.home });
19228
+ if (!configPath) return null;
19229
+ const raw = readObject(configPath);
19230
+ return takeClientOnlyKeys(raw).template || null;
19156
19231
  }
19157
- function assertBundleConfigSendable(config) {
19232
+ function assertConfigSendable(remote, config) {
19233
+ if (remote.scope === "policy") return;
19158
19234
  const dropped = nonBundleKeysFound(config);
19159
19235
  if (dropped.length > 0) {
19160
19236
  throw new PiPodError(`not sent: the server bundle no longer accepts ${dropped.join(", ")}`, {
19161
19237
  hint: "local-only keys stay in local files (`template`, `secretResolver`, `pi.chords`); pass --reuse at launch; retired keys must be deleted"
19162
19238
  });
19163
19239
  }
19164
- }
19165
- function assertBundleConfigValid(config, scope) {
19166
19240
  const report = validateConfig(config);
19167
- for (const warning of report.warnings) warn(`${scope} settings: ${warning}`);
19241
+ for (const warning of report.warnings) warn(`${remote.name}: ${warning}`);
19168
19242
  if (report.errors.length > 0) {
19169
- throw new PiPodError(`invalid ${scope} settings config: ${report.errors.join("; ")}`, {
19243
+ throw new PiPodError(`invalid ${remote.name} config: ${report.errors.join("; ")}`, {
19170
19244
  hint: "fix the config or edit it back \u2014 nothing was sent"
19171
19245
  });
19172
19246
  }
19173
19247
  }
19174
- async function runSettings(args, flags) {
19175
- const [scope = "user", action = "show", ...rest] = args;
19176
- if (scope !== "user" && scope !== "org") {
19177
- throw new PiPodError(`unknown settings scope "${scope}"`, {
19178
- hint: `${USAGE}; templates are managed with \`pipod templates\`, and org policy with \`pipod pull|diff|push policy\``
19179
- });
19180
- }
19181
- if (action === "show") {
19182
- if (rest.length > 0) throw new PiPodError(`usage: pipod settings ${scope}`);
19183
- out(JSON.stringify(await readBundle(flags.client, scope), null, 2));
19184
- return 0;
19185
- }
19186
- if (action === "edit") return editSettings(scope, flags);
19187
- if (action === "set") return setSetting(scope, rest, flags.client);
19188
- if (action === "unset") return unsetSetting(scope, rest, flags.client);
19189
- throw new PiPodError(`unknown settings action "${action}"`, { hint: "actions: show, edit, set, unset" });
19190
- }
19191
- async function editSettings(scope, flags) {
19192
- const current = await readBundle(flags.client, scope);
19248
+ function editInEditor(config, flags) {
19193
19249
  const directory = fs32.mkdtempSync(path30.join(os7.tmpdir(), "pi-pod-settings-"));
19194
19250
  const file = path30.join(directory, "config.json");
19195
19251
  try {
19196
- fs32.writeFileSync(file, `${JSON.stringify(current.config, null, 2)}
19252
+ fs32.writeFileSync(file, `${JSON.stringify(config, null, 2)}
19197
19253
  `, { mode: 384 });
19198
19254
  const editor = flags.editor ?? process.env["VISUAL"] ?? process.env["EDITOR"];
19199
19255
  if (!editor) throw new PiPodError("$EDITOR is not set");
@@ -19203,46 +19259,11 @@ async function editSettings(scope, flags) {
19203
19259
  });
19204
19260
  if (edited.error) throw new PiPodError(`could not run editor: ${edited.error.message}`);
19205
19261
  if (edited.status !== 0) throw new PiPodError(`editor exited with status ${edited.status ?? "unknown"}`);
19206
- const config = readObject(file);
19207
- assertBundleConfigSendable(config);
19208
- assertBundleConfigValid(config, scope);
19209
- const result = await writeBundle(flags.client, scope, { config, version: current.version });
19210
- info(`updated ${scope} settings v${result.version}`);
19211
- return 0;
19262
+ return readObject(file);
19212
19263
  } finally {
19213
19264
  fs32.rmSync(directory, { recursive: true, force: true });
19214
19265
  }
19215
19266
  }
19216
- async function setSetting(scope, args, client) {
19217
- const [dotPath, rawValue, ...extra] = args;
19218
- if (!dotPath || rawValue === void 0 || extra.length > 0) {
19219
- throw new PiPodError(`usage: pipod settings ${scope} set <key> <value>`);
19220
- }
19221
- let value;
19222
- try {
19223
- value = JSON.parse(rawValue);
19224
- } catch {
19225
- value = rawValue;
19226
- }
19227
- const version = await updateConfig(scope, client, (config) => setDotPath(config, dotPath, value));
19228
- info(`set ${scope} setting ${dotPath} (v${version})`);
19229
- return 0;
19230
- }
19231
- async function unsetSetting(scope, args, client) {
19232
- const [dotPath, ...extra] = args;
19233
- if (!dotPath || extra.length > 0) throw new PiPodError(`usage: pipod settings ${scope} unset <key>`);
19234
- const version = await updateConfig(scope, client, (config) => unsetDotPath(config, dotPath));
19235
- info(`unset ${scope} setting ${dotPath} (v${version})`);
19236
- return 0;
19237
- }
19238
- async function updateConfig(scope, client, change) {
19239
- const current = await readBundle(client, scope);
19240
- const config = structuredClone(current.config);
19241
- change(config);
19242
- assertBundleConfigSendable(config);
19243
- assertBundleConfigValid(config, scope);
19244
- return (await writeBundle(client, scope, { config, version: current.version })).version;
19245
- }
19246
19267
  function unsetDotPath(target, dotPath) {
19247
19268
  const [head, ...rest] = dotPath.split(".");
19248
19269
  const child = target[head];