@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 +82 -21
- package/dist/cli.js.map +1 -1
- package/docs/config-simplification.md +8 -6
- package/man/pipod-config.5 +1 -1
- package/man/pipod.1 +34 -14
- package/man/pipod.1.md +32 -13
- package/package.json +1 -1
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
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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") {
|