@pipod/cli 0.1.10 → 0.1.11

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
@@ -1908,6 +1908,14 @@ var AccountClient = class {
1908
1908
  putOrgSettings(body) {
1909
1909
  return this.request(`/orgs/${encodeURIComponent(this.orgId)}/settings`, { method: "PUT", body });
1910
1910
  }
1911
+ /** The organization's policy: its `config` holds the limits; the scripts and Pi files are always empty. */
1912
+ getOrgPolicy() {
1913
+ return this.request(`/orgs/${encodeURIComponent(this.orgId)}/policy`);
1914
+ }
1915
+ /** Replaces the whole policy, compare-and-swap on `version`. The server validates it. */
1916
+ putOrgPolicy(body) {
1917
+ return this.request(`/orgs/${encodeURIComponent(this.orgId)}/policy`, { method: "PUT", body });
1918
+ }
1911
1919
  launch(body) {
1912
1920
  return this.request("/pods", { method: "POST", body });
1913
1921
  }
@@ -2209,10 +2217,12 @@ async function responseError(res, method, apiPath) {
2209
2217
  hint: "an owner of your organization can grant it (`pipod org-admin` opens the console)"
2210
2218
  });
2211
2219
  }
2220
+ const issues = res.status === 400 && Array.isArray(body.detail) ? body.detail.filter((issue) => typeof issue === "string").slice(0, 10).map((issue) => issue.slice(0, 240)) : [];
2212
2221
  return new PiPodError(`the pi pod server refused ${method} ${apiPath}: ${message}`, {
2213
2222
  status: res.status,
2214
2223
  ...code ? { code } : {},
2215
- ...safeDetail !== void 0 ? { detail: safeDetail } : {}
2224
+ ...safeDetail !== void 0 ? { detail: safeDetail } : {},
2225
+ ...issues.length > 0 ? { hint: issues.join("\n") } : {}
2216
2226
  });
2217
2227
  }
2218
2228
  function isSafeCapacityDetail(value) {
@@ -3870,6 +3880,9 @@ function orgSourceDir(args) {
3870
3880
  if (!home) throw new PiPodError("cannot locate ~/.pi-pod/org: HOME is unset", { hint: "pass --dir <path>" });
3871
3881
  return path11.join(home, ORG_SOURCE_SUBDIR);
3872
3882
  }
3883
+ function policySourcePath(args) {
3884
+ return path11.join(orgSourceDir(args), ORG_POLICY_FILE);
3885
+ }
3873
3886
  function safePiDir(root, subpath) {
3874
3887
  const directory = path11.join(root, subpath);
3875
3888
  let stat;
@@ -3970,6 +3983,28 @@ async function readEnvSnapshot(envPath, mode, resolver, home) {
3970
3983
  async function compileBundleSource(args) {
3971
3984
  const cwd = args.cwd ?? process.cwd();
3972
3985
  const envMode = args.includeEnv === false ? false : args.includeEnv === "names" ? "names" : true;
3986
+ if (args.source === "policy-file") {
3987
+ const configPath2 = policySourcePath({ dir: args.dir, home: args.home });
3988
+ if (!fs12.existsSync(configPath2)) {
3989
+ throw new PiPodError(`no org policy file at ${configPath2}`, {
3990
+ hint: "run `pipod pull policy` to create it from the server's policy, or pass --dir <path>"
3991
+ });
3992
+ }
3993
+ return {
3994
+ kind: "policy-file",
3995
+ config: parseConfig(configPath2),
3996
+ initScript: "",
3997
+ bakeScript: "",
3998
+ piFiles: {},
3999
+ env: {},
4000
+ warnings: [],
4001
+ name: "org policy",
4002
+ templateRef: null,
4003
+ projectRoot: null,
4004
+ root: path11.dirname(configPath2),
4005
+ configPath: configPath2
4006
+ };
4007
+ }
3973
4008
  if (args.source === "project") {
3974
4009
  const configPath2 = findConfigPath(cwd, { home: args.home });
3975
4010
  if (!configPath2) {
@@ -4069,6 +4104,7 @@ async function compileBundleSource(args) {
4069
4104
  }
4070
4105
  var ORG_SCRIPTS = { init: "init.sh", bake: "bake.sh" };
4071
4106
  var ORG_ENV_FILE = "env";
4107
+ var ORG_POLICY_FILE = "policy.json";
4072
4108
  function readPiFiles(root, config, kind) {
4073
4109
  const files = {};
4074
4110
  const warnings = [];
@@ -4323,6 +4359,15 @@ function writePulledAgents(writer, piDir, agents) {
4323
4359
  }
4324
4360
  function writeBundleSource(args) {
4325
4361
  const { kind, bundle } = args;
4362
+ if (kind === "policy-file") {
4363
+ const policyPath = policySourcePath({ dir: args.dir, home: args.home });
4364
+ const writer2 = new LayerWriter(path11.dirname(policyPath));
4365
+ if (!fs12.existsSync(policyPath) || stable(parseConfig(policyPath)) !== stable(bundle.config)) {
4366
+ writer2.noteComments(policyPath);
4367
+ writer2.json(policyPath, bundle.config);
4368
+ }
4369
+ return { written: writer2.written, removed: writer2.removed, warnings: writer2.warnings, root: writer2.root };
4370
+ }
4326
4371
  let root;
4327
4372
  let configPath;
4328
4373
  if (kind === "project") {
@@ -18863,7 +18908,7 @@ async function rmCmd2(client, args, flags) {
18863
18908
  }
18864
18909
 
18865
18910
  // src/commands/layers.ts
18866
- var LAYER_USAGE = "template [<name>], user, or org";
18911
+ var LAYER_USAGE = "template [<name>], user, org, or policy";
18867
18912
  function parseLayerArgs(verb, args, opts) {
18868
18913
  const positionals = [];
18869
18914
  let dir;
@@ -18890,18 +18935,29 @@ function parseLayerArgs(verb, args, opts) {
18890
18935
  layer = { kind: "template" };
18891
18936
  } else if (first === "template") {
18892
18937
  layer = second === void 0 ? { kind: "template" } : { kind: "template", name: second };
18893
- } else if (first === "user" || first === "org") {
18938
+ } else if (first === "user" || first === "org" || first === "policy") {
18894
18939
  layer = { kind: first };
18895
18940
  if (second !== void 0) extra.unshift(second);
18896
18941
  } else {
18897
18942
  throw new PiPodError(`unknown layer "${first}"`, { hint: `layers: ${LAYER_USAGE}` });
18898
18943
  }
18899
18944
  if (extra.length > 0) throw new PiPodError(`unexpected \`pipod ${verb}\` argument(s): ${extra.join(" ")}`);
18900
- if (dir !== void 0 && layer.kind !== "org") throw new PiPodError("--dir only applies to the org layer");
18945
+ if (dir !== void 0 && layer.kind !== "org" && layer.kind !== "policy") {
18946
+ throw new PiPodError("--dir only applies to the org and policy layers");
18947
+ }
18901
18948
  return { layer, ...dir !== void 0 ? { dir } : {}, yes, withSecrets };
18902
18949
  }
18903
18950
  function sourceKindOf(layer) {
18904
- return layer.kind === "template" ? "project" : layer.kind === "user" ? "user-dir" : "org-dir";
18951
+ switch (layer.kind) {
18952
+ case "template":
18953
+ return "project";
18954
+ case "user":
18955
+ return "user-dir";
18956
+ case "org":
18957
+ return "org-dir";
18958
+ case "policy":
18959
+ return "policy-file";
18960
+ }
18905
18961
  }
18906
18962
  async function fetchRemoteLayer(client, layer, source) {
18907
18963
  if (layer.kind === "template") {
@@ -18924,6 +18980,17 @@ async function fetchRemoteLayer(client, layer, source) {
18924
18980
  scopeId: template.id
18925
18981
  };
18926
18982
  }
18983
+ if (layer.kind === "policy") {
18984
+ const current2 = await client.getOrgPolicy();
18985
+ return {
18986
+ bundle: settingsBundle(current2),
18987
+ version: current2.version,
18988
+ secretNames: [],
18989
+ label: `org policy v${current2.version}`,
18990
+ scope: "policy",
18991
+ scopeId: client.orgId
18992
+ };
18993
+ }
18927
18994
  const current = layer.kind === "user" ? await client.getUserSettings() : await client.getOrgSettings();
18928
18995
  const scopeId = layer.kind === "user" ? client.userId : client.orgId;
18929
18996
  const listed = await client.listSecrets(layer.kind, scopeId).catch(() => ({ secrets: [] }));
@@ -18939,6 +19006,7 @@ async function fetchRemoteLayer(client, layer, source) {
18939
19006
  function localLabel(layer, flags) {
18940
19007
  if (layer.kind === "template") return "project";
18941
19008
  if (layer.kind === "user") return "~/.pi-pod + ~/.pi/agent";
19009
+ if (layer.kind === "policy") return displayPath(policySourcePath({ dir: flags.dir, home: flags.home }), flags.home);
18942
19010
  return displayPath(orgSourceDir({ dir: flags.dir, home: flags.home }), flags.home);
18943
19011
  }
18944
19012
  function printDiff(heading, lines) {
@@ -18972,22 +19040,10 @@ async function runPull(args, flags) {
18972
19040
  const kind = sourceKindOf(parsed.layer);
18973
19041
  let source;
18974
19042
  let firstPull = false;
18975
- if (kind === "org-dir" && !fs31.existsSync(orgSourceDir({ dir: parsed.dir, home: flags.home }))) {
19043
+ const localPath = kind === "org-dir" ? orgSourceDir({ dir: parsed.dir, home: flags.home }) : kind === "policy-file" ? policySourcePath({ dir: parsed.dir, home: flags.home }) : null;
19044
+ if (localPath !== null && !fs31.existsSync(localPath)) {
18976
19045
  firstPull = true;
18977
- source = {
18978
- kind,
18979
- config: {},
18980
- initScript: "",
18981
- bakeScript: "",
18982
- piFiles: {},
18983
- env: {},
18984
- warnings: [],
18985
- name: "org settings",
18986
- templateRef: null,
18987
- projectRoot: null,
18988
- root: orgSourceDir({ dir: parsed.dir, home: flags.home }),
18989
- configPath: ""
18990
- };
19046
+ source = { config: {}, initScript: "", bakeScript: "", piFiles: {}, env: {}, warnings: [], templateRef: null };
18991
19047
  } else {
18992
19048
  source = await compileBundleSource({ source: kind, cwd, home: flags.home, includeEnv: false, dir: parsed.dir });
18993
19049
  }
@@ -19048,6 +19104,11 @@ async function runPush(args, flags) {
19048
19104
  return 0;
19049
19105
  }
19050
19106
  if (remote.version === void 0) throw new PiPodError(`${remote.label} has no version to compare-and-swap against`);
19107
+ if (remote.scope === "policy") {
19108
+ const written2 = await flags.client.putOrgPolicy({ config: source.config, version: remote.version });
19109
+ info(`pushed ${local} to org policy v${written2.version}`);
19110
+ return 0;
19111
+ }
19051
19112
  const body = {
19052
19113
  config: source.config,
19053
19114
  version: remote.version,
@@ -19120,7 +19181,7 @@ async function runSettings(args, flags) {
19120
19181
  const [scope = "user", action = "show", ...rest] = args;
19121
19182
  if (scope !== "user" && scope !== "org") {
19122
19183
  throw new PiPodError(`unknown settings scope "${scope}"`, {
19123
- hint: `${USAGE}; templates are managed with \`pipod templates\``
19184
+ hint: `${USAGE}; templates are managed with \`pipod templates\`, and org policy with \`pipod pull|diff|push policy\``
19124
19185
  });
19125
19186
  }
19126
19187
  if (action === "show") {