patchcord 0.6.30 → 0.6.31
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/.claude-plugin/plugin.json +1 -1
- package/bin/patchcord.mjs +57 -5
- package/package.json +1 -1
package/bin/patchcord.mjs
CHANGED
|
@@ -196,11 +196,24 @@ team (run from the project root):
|
|
|
196
196
|
patchcord team blueprint push --namespace <ns> --file <blueprint.json> [--json]
|
|
197
197
|
create/update a team blueprint (first push
|
|
198
198
|
to a new namespace claims it — first-provision)
|
|
199
|
+
PROMOTES BY DEFAULT — the pushed document
|
|
200
|
+
goes live immediately
|
|
201
|
+
patchcord team blueprint push ... --no-promote (alias --dry-run)
|
|
202
|
+
validate + store WITHOUT going live. Use this
|
|
203
|
+
when any deployer may not yet understand a
|
|
204
|
+
value in the document.
|
|
199
205
|
patchcord team deployment request --namespace <ns> [--revision <uid>] [--json]
|
|
200
206
|
enqueue an apply of a saved revision
|
|
201
207
|
(exit 3 = one is already in flight)
|
|
202
208
|
patchcord team deployment pending [--json] jobs waiting for this host to claim
|
|
203
|
-
patchcord team deployment claim --job <uid> [--installation <name>]
|
|
209
|
+
patchcord team deployment claim --job <uid> [--installation <name>]
|
|
210
|
+
[--installation-uuid <uuid>] [--json]
|
|
211
|
+
--installation is the host LABEL
|
|
212
|
+
(becomes claimed_by).
|
|
213
|
+
--installation-uuid is the identity
|
|
214
|
+
selector; must match the enrolment
|
|
215
|
+
body or identity-key membership is
|
|
216
|
+
never recorded. Optional.
|
|
204
217
|
take ownership (exit 3 = lost the race)
|
|
205
218
|
patchcord team deployment complete --job <uid> --status <healthy|degraded|cancelled>
|
|
206
219
|
[--result <json> | --result-file <path>] [--json]
|
|
@@ -1701,13 +1714,27 @@ you design the team, provision its agents, launch them, and manage them.
|
|
|
1701
1714
|
const file = flagVal("file");
|
|
1702
1715
|
const idem = flagVal("idempotency-key");
|
|
1703
1716
|
const wantJson = process.argv.includes("--json");
|
|
1704
|
-
|
|
1717
|
+
// --no-promote / --dry-run: store the revision and run strict server
|
|
1718
|
+
// validation WITHOUT moving the live pointer.
|
|
1719
|
+
//
|
|
1720
|
+
// This exists because push promotes by default, so a hand-written
|
|
1721
|
+
// document goes live the instant it validates. That is fine when the
|
|
1722
|
+
// document is one every consumer understands, and it is how a Team gets
|
|
1723
|
+
// bricked when it is not: the server can accept a value that deployers
|
|
1724
|
+
// do not yet handle, and promoting it makes the Team undeployable AND
|
|
1725
|
+
// unrestorable from one command. The rule the team arrived at is
|
|
1726
|
+
// "consumers accept before producers emit", and until now the only way
|
|
1727
|
+
// to honour it here was to remember to. This is the safe path existing
|
|
1728
|
+
// rather than being agreed.
|
|
1729
|
+
const noPromote = process.argv.includes("--no-promote") || process.argv.includes("--dry-run");
|
|
1730
|
+
if (!ns || !file) { console.error("Usage: patchcord team blueprint push --namespace <ns> --file <blueprint.json> [--no-promote|--dry-run] [--idempotency-key k] [--json]"); process.exit(1); }
|
|
1705
1731
|
let doc;
|
|
1706
1732
|
try { doc = JSON.parse(readFileSync(file, "utf-8")); }
|
|
1707
1733
|
catch (e) { console.error(`cannot read blueprint file: ${e.message}`); process.exit(1); }
|
|
1708
1734
|
// First POST for an unowned namespace IS first-provision (server claims
|
|
1709
1735
|
// the namespace atomically when PATCHCORD_BLUEPRINT_ATOMIC_CREATE is on).
|
|
1710
1736
|
const payload = { canonical_json: doc };
|
|
1737
|
+
if (noPromote) payload.promote = false;
|
|
1711
1738
|
if (idem) payload.idempotency_key = idem;
|
|
1712
1739
|
const { status, json, body } = await accountCall("POST", `/api/team/${encodeURIComponent(ns)}/blueprint/revisions`, payload);
|
|
1713
1740
|
if (status !== "201" && status !== "200") {
|
|
@@ -1715,7 +1742,7 @@ you design the team, provision its agents, launch them, and manage them.
|
|
|
1715
1742
|
process.exit(1);
|
|
1716
1743
|
}
|
|
1717
1744
|
if (wantJson) {
|
|
1718
|
-
process.stdout.write(JSON.stringify({ ...json, created: status === "201" }) + "\n");
|
|
1745
|
+
process.stdout.write(JSON.stringify({ ...json, created: status === "201", promoted: json.promoted !== false && !noPromote }) + "\n");
|
|
1719
1746
|
process.exit(0);
|
|
1720
1747
|
}
|
|
1721
1748
|
console.log(`${status === "201" ? "created" : "unchanged (dedup)"} revision_uid: ${json.revision_uid}`);
|
|
@@ -1785,10 +1812,35 @@ you design the team, provision its agents, launch them, and manage them.
|
|
|
1785
1812
|
if (dsub === "claim") {
|
|
1786
1813
|
const job = flagVal("job");
|
|
1787
1814
|
const installation = flagVal("installation");
|
|
1788
|
-
|
|
1815
|
+
// SEPARATE FIELD, deliberately not a new meaning for --installation.
|
|
1816
|
+
//
|
|
1817
|
+
// --installation becomes claimed_by, which is the human-readable "which
|
|
1818
|
+
// host took this job" an operator reads in the deploy list. Putting a
|
|
1819
|
+
// UUID there would spend a label field on an identifier and leave
|
|
1820
|
+
// nothing legible in its place.
|
|
1821
|
+
//
|
|
1822
|
+
// They are also different things by construction: --installation is
|
|
1823
|
+
// <hostname>-<suffix>, live (macOS renames itself on a Bonjour
|
|
1824
|
+
// collision) and MUX_HOST_ID-overridable — the exact selector we
|
|
1825
|
+
// disqualified for the identity flow. --installation-uuid is minted
|
|
1826
|
+
// beside the identity key itself so the key and its selector share one
|
|
1827
|
+
// lifetime. It is what the server keys team membership on, so it must
|
|
1828
|
+
// match the enrolment body's installation_uuid EXACTLY or membership is
|
|
1829
|
+
// written against a string nothing else uses and the team-scoped read
|
|
1830
|
+
// returns an empty set forever.
|
|
1831
|
+
//
|
|
1832
|
+
// Omitting it is legal and stays legal: an installation that has not
|
|
1833
|
+
// upgraded keeps claiming exactly as before and simply records no
|
|
1834
|
+
// identity-key membership. The server has accepted this field since
|
|
1835
|
+
// bb553b2, so consumers accept before producers emit.
|
|
1836
|
+
const installationUuid = flagVal("installation-uuid");
|
|
1837
|
+
if (!job) { console.error("Usage: patchcord team deployment claim --job <uid> [--installation <name>] [--installation-uuid <uuid>] [--json]"); process.exit(1); }
|
|
1838
|
+
const claimBody = {};
|
|
1839
|
+
if (installation) claimBody.installation = installation;
|
|
1840
|
+
if (installationUuid) claimBody.installation_uuid = installationUuid;
|
|
1789
1841
|
const { status, json, body } = await accountCall(
|
|
1790
1842
|
"POST", `/api/deployments/${encodeURIComponent(job)}/claim`,
|
|
1791
|
-
|
|
1843
|
+
claimBody,
|
|
1792
1844
|
);
|
|
1793
1845
|
// Losing a claim race is normal in a multi-host account, not a failure:
|
|
1794
1846
|
// exit 3 so a poller can skip to the next job without parsing stderr.
|
package/package.json
CHANGED