@calimero-network/mero-js 27.0.1 → 27.1.0
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 +5 -2
- package/dist/cloud-v2/approvals.d.ts +344 -0
- package/dist/cloud-v2/approvals.js +170 -0
- package/dist/cloud-v2/capabilities.d.ts +8 -1
- package/dist/cloud-v2/capabilities.js +8 -1
- package/dist/cloud-v2/client.d.ts +114 -7
- package/dist/cloud-v2/client.js +149 -6
- package/dist/cloud-v2/errors.d.ts +23 -0
- package/dist/cloud-v2/errors.js +31 -0
- package/dist/cloud-v2/index.d.ts +4 -2
- package/dist/cloud-v2/index.js +2 -1
- package/dist/index.browser.mjs +7 -3
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +294 -8
- package/dist/index.cjs.map +4 -4
- package/dist/index.mjs +294 -8
- package/dist/index.mjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
CalimeroError: () => CalimeroError,
|
|
39
39
|
ConfirmCancelled: () => ConfirmCancelled,
|
|
40
40
|
ConfirmRequired: () => ConfirmRequired,
|
|
41
|
+
ConsentStale: () => ConsentStale,
|
|
41
42
|
DEFAULT_ORG_CAPABILITIES: () => DEFAULT_ORG_CAPABILITIES,
|
|
42
43
|
DEFAULT_RELEASE_MIRROR: () => DEFAULT_RELEASE_MIRROR,
|
|
43
44
|
GITHUB_RELEASES: () => GITHUB_RELEASES,
|
|
@@ -45,11 +46,14 @@ __export(index_exports, {
|
|
|
45
46
|
IdempotencyMismatch: () => IdempotencyMismatch,
|
|
46
47
|
KMS_RELEASE_SIGNER: () => KMS_RELEASE_SIGNER,
|
|
47
48
|
LoginRequired: () => LoginRequired,
|
|
49
|
+
MAX_REPORT_PAGE: () => MAX_REPORT_PAGE,
|
|
48
50
|
MOCK_QUOTE_HEADER: () => MOCK_QUOTE_HEADER,
|
|
49
51
|
NODE_RELEASE_SIGNER: () => NODE_RELEASE_SIGNER,
|
|
50
52
|
NotFound: () => NotFound,
|
|
51
53
|
ORG_CAPABILITIES: () => ORG_CAPABILITIES,
|
|
52
54
|
PRODUCTION_PROFILE: () => PRODUCTION_PROFILE,
|
|
55
|
+
RELEASE_VERIFIER: () => RELEASE_VERIFIER,
|
|
56
|
+
ReleaseDeadlineError: () => ReleaseDeadlineError,
|
|
53
57
|
SEALED_CONTENT_TYPE: () => SEALED_CONTENT_TYPE,
|
|
54
58
|
SEALED_PATH: () => SEALED_PATH,
|
|
55
59
|
SealedTransportError: () => SealedTransportError,
|
|
@@ -64,6 +68,8 @@ __export(index_exports, {
|
|
|
64
68
|
checkOwner: () => checkOwner,
|
|
65
69
|
claimedRelease: () => claimedRelease,
|
|
66
70
|
cloudNodeReleaseUrl: () => cloudNodeReleaseUrl,
|
|
71
|
+
compareVersions: () => compareVersions,
|
|
72
|
+
consentReturn: () => consentReturn,
|
|
67
73
|
createQuoteVerifier: () => createQuoteVerifier,
|
|
68
74
|
createSealedFetch: () => createSealedFetch,
|
|
69
75
|
createSignedReleaseVerifier: () => createSignedReleaseVerifier,
|
|
@@ -75,15 +81,25 @@ __export(index_exports, {
|
|
|
75
81
|
generateOwnerKey: () => generateOwnerKey,
|
|
76
82
|
orgCapabilities: () => orgCapabilities,
|
|
77
83
|
ownerPublicKey: () => ownerPublicKey,
|
|
84
|
+
pageQuery: () => pageQuery,
|
|
85
|
+
pendingApprovalsFrom: () => pendingApprovalsFrom,
|
|
78
86
|
provisionAgent: () => provisionAgent,
|
|
79
87
|
provisionInfo: () => provisionInfo,
|
|
88
|
+
publisherBindingBytes: () => publisherBindingBytes,
|
|
89
|
+
publisherBindingMessage: () => publisherBindingMessage,
|
|
80
90
|
releaseAgent: () => releaseAgent,
|
|
91
|
+
releaseConsent: () => releaseConsent,
|
|
92
|
+
releaseNotesUrl: () => releaseNotesUrl,
|
|
93
|
+
stalledBy: () => stalledBy,
|
|
81
94
|
transportKeyBinding: () => transportKeyBinding,
|
|
82
95
|
trustSignedRelease: () => trustSignedRelease,
|
|
83
96
|
trustedMeasurementsFromReleases: () => trustedMeasurementsFromReleases,
|
|
97
|
+
upgradeConsent: () => upgradeConsent,
|
|
84
98
|
validateAgentId: () => validateAgentId,
|
|
99
|
+
verifyReleaseUrl: () => verifyReleaseUrl,
|
|
85
100
|
verifySignedAgentPolicy: () => verifySignedAgentPolicy,
|
|
86
|
-
verifySignedNodeRelease: () => verifySignedNodeRelease
|
|
101
|
+
verifySignedNodeRelease: () => verifySignedNodeRelease,
|
|
102
|
+
withdrawConsent: () => withdrawConsent
|
|
87
103
|
});
|
|
88
104
|
module.exports = __toCommonJS(index_exports);
|
|
89
105
|
|
|
@@ -1355,6 +1371,20 @@ var IdempotencyMismatch = class extends CalimeroError {
|
|
|
1355
1371
|
this.name = "IdempotencyMismatch";
|
|
1356
1372
|
}
|
|
1357
1373
|
};
|
|
1374
|
+
var ReleaseDeadlineError = class extends CalimeroError {
|
|
1375
|
+
constructor(message, details = {}) {
|
|
1376
|
+
super(409, "RELEASE_DEADLINE", message, details);
|
|
1377
|
+
this.name = "ReleaseDeadlineError";
|
|
1378
|
+
this.release = typeof details.release === "string" ? details.release : "";
|
|
1379
|
+
this.after = typeof details.after === "string" ? details.after : "";
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
var ConsentStale = class extends CalimeroError {
|
|
1383
|
+
constructor(message, details = {}) {
|
|
1384
|
+
super(409, "CONSENT_STALE", message, details);
|
|
1385
|
+
this.name = "ConsentStale";
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1358
1388
|
var AppError = class extends CalimeroError {
|
|
1359
1389
|
constructor(message, details = {}) {
|
|
1360
1390
|
super(422, "APP_ERROR", message, details);
|
|
@@ -1408,6 +1438,10 @@ function errorFrom(status, body, retryAfterHeader) {
|
|
|
1408
1438
|
return new NotFound(message, details);
|
|
1409
1439
|
case "409 IDEMPOTENCY_MISMATCH":
|
|
1410
1440
|
return new IdempotencyMismatch(message, details);
|
|
1441
|
+
case "409 RELEASE_DEADLINE":
|
|
1442
|
+
return new ReleaseDeadlineError(message, details);
|
|
1443
|
+
case "409 CONSENT_STALE":
|
|
1444
|
+
return new ConsentStale(message, details);
|
|
1411
1445
|
case "412 CONFIRM_REQUIRED":
|
|
1412
1446
|
return new ConfirmRequired(message, details);
|
|
1413
1447
|
case "413 ARGS_TOO_LARGE":
|
|
@@ -1484,6 +1518,113 @@ function browserPasskey() {
|
|
|
1484
1518
|
};
|
|
1485
1519
|
}
|
|
1486
1520
|
|
|
1521
|
+
// src/cloud-v2/approvals.ts
|
|
1522
|
+
var MAX_REPORT_PAGE = 100;
|
|
1523
|
+
function pageQuery(page = {}) {
|
|
1524
|
+
const q = new URLSearchParams();
|
|
1525
|
+
if (page.after !== void 0) q.set("after", page.after);
|
|
1526
|
+
if (page.limit !== void 0) {
|
|
1527
|
+
if (!Number.isInteger(page.limit) || page.limit < 1 || page.limit > MAX_REPORT_PAGE) {
|
|
1528
|
+
throw new RangeError(`limit is 1 to ${MAX_REPORT_PAGE}`);
|
|
1529
|
+
}
|
|
1530
|
+
q.set("limit", String(page.limit));
|
|
1531
|
+
}
|
|
1532
|
+
const s = q.toString();
|
|
1533
|
+
return s ? `?${s}` : "";
|
|
1534
|
+
}
|
|
1535
|
+
function compareVersions(a, b) {
|
|
1536
|
+
try {
|
|
1537
|
+
return compareReleaseVersions(a, b);
|
|
1538
|
+
} catch {
|
|
1539
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function stalledBy(approved, deadlines, now = Date.now() / 1e3) {
|
|
1543
|
+
if (approved === null || approved.length === 0) return null;
|
|
1544
|
+
const newest = approved.reduce((a, b) => compareVersions(a, b) >= 0 ? a : b);
|
|
1545
|
+
let stall = null;
|
|
1546
|
+
for (const d of deadlines) {
|
|
1547
|
+
if (d.afterSecs <= now && compareVersions(newest, d.version) < 0) {
|
|
1548
|
+
if (stall === null || compareVersions(d.version, stall.version) > 0) stall = d;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
return stall;
|
|
1552
|
+
}
|
|
1553
|
+
function publisherBindingMessage(cluster, org, signerId, generation) {
|
|
1554
|
+
return `calimero-publisher-binding/v1
|
|
1555
|
+
${cluster}
|
|
1556
|
+
${org}
|
|
1557
|
+
${signerId}
|
|
1558
|
+
${generation}`;
|
|
1559
|
+
}
|
|
1560
|
+
function publisherBindingBytes(cluster, org, signerId, generation) {
|
|
1561
|
+
return new TextEncoder().encode(publisherBindingMessage(cluster, org, signerId, generation));
|
|
1562
|
+
}
|
|
1563
|
+
function consentReturn(href) {
|
|
1564
|
+
const url2 = typeof href === "string" ? new URL(href) : href;
|
|
1565
|
+
const requestId = url2.searchParams.get("request_id");
|
|
1566
|
+
const result = url2.searchParams.get("result");
|
|
1567
|
+
if (!requestId || result !== "approved" && result !== "declined") return null;
|
|
1568
|
+
return { requestId, result };
|
|
1569
|
+
}
|
|
1570
|
+
var RELEASE_VERIFIER = "https://mero-tee.vercel.app";
|
|
1571
|
+
function releaseNotesUrl(version) {
|
|
1572
|
+
return `https://github.com/calimero-network/mero-tee/releases/tag/mero-tee-v${encodeURIComponent(version)}`;
|
|
1573
|
+
}
|
|
1574
|
+
function verifyReleaseUrl(version) {
|
|
1575
|
+
return `${RELEASE_VERIFIER}/mero-tee?release_tag=${encodeURIComponent(`mero-tee-v${version}`)}`;
|
|
1576
|
+
}
|
|
1577
|
+
function pendingApprovalsFrom(report, rules, now = Date.now() / 1e3) {
|
|
1578
|
+
const deadlineOf = (v) => rules.deadlines.find((d) => d.version === v) ?? null;
|
|
1579
|
+
const releasesActionable = report.governance.releases === "owner";
|
|
1580
|
+
const upgradesActionable = report.governance.appUpgrades === "owner";
|
|
1581
|
+
const releases = report.clusterReleases.filter((r) => !r.approved).sort((a, b) => compareVersions(b.release, a.release)).map((r) => ({
|
|
1582
|
+
version: r.release,
|
|
1583
|
+
nodes: r.nodes,
|
|
1584
|
+
actionable: releasesActionable,
|
|
1585
|
+
releaseNotesUrl: releaseNotesUrl(r.release),
|
|
1586
|
+
verifyUrl: verifyReleaseUrl(r.release),
|
|
1587
|
+
deadline: deadlineOf(r.release)
|
|
1588
|
+
}));
|
|
1589
|
+
const upgrades = [];
|
|
1590
|
+
for (const a of report.apps) {
|
|
1591
|
+
const available = a.availableVersions ?? [];
|
|
1592
|
+
if (a.package === null || available.length === 0) continue;
|
|
1593
|
+
upgrades.push({
|
|
1594
|
+
app: a.app,
|
|
1595
|
+
name: a.name ?? null,
|
|
1596
|
+
package: a.package.package,
|
|
1597
|
+
from: a.package.version,
|
|
1598
|
+
to: available[available.length - 1],
|
|
1599
|
+
available,
|
|
1600
|
+
actionable: upgradesActionable
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
return {
|
|
1604
|
+
scope: report.scope,
|
|
1605
|
+
governance: report.governance,
|
|
1606
|
+
approvedReleases: report.approvedReleases,
|
|
1607
|
+
releases,
|
|
1608
|
+
upgrades,
|
|
1609
|
+
stalledBy: stalledBy(report.approvedReleases, rules.deadlines, now),
|
|
1610
|
+
actionable: releases.some((r) => r.actionable) || upgrades.some((u) => u.actionable)
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
function releaseConsent(scope, version, returnTo, options = {}) {
|
|
1614
|
+
return {
|
|
1615
|
+
kind: "release",
|
|
1616
|
+
scope: scope.kind === "org" ? { org: scope.org } : "account",
|
|
1617
|
+
subject: options.withdraw ? { version, withdraw: true } : { version },
|
|
1618
|
+
returnTo
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function withdrawConsent(scope, version, returnTo) {
|
|
1622
|
+
return releaseConsent(scope, version, returnTo, { withdraw: true });
|
|
1623
|
+
}
|
|
1624
|
+
function upgradeConsent(app, toVersion, returnTo) {
|
|
1625
|
+
return { kind: "upgrade", subject: { app, toVersion }, returnTo };
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1487
1628
|
// src/cloud-v2/client.ts
|
|
1488
1629
|
function seg(value) {
|
|
1489
1630
|
return encodeURIComponent(value);
|
|
@@ -1829,8 +1970,11 @@ var CalimeroClient = class {
|
|
|
1829
1970
|
* Move an app to another release of its package (`{ version }`), another
|
|
1830
1971
|
* package, or a bundle hash, running its migration. Answers the upgrade's
|
|
1831
1972
|
* entry; a failed migration rejects with code `UPGRADE_FAILED` (422).
|
|
1832
|
-
* Where
|
|
1833
|
-
* (`412 CONFIRM_REQUIRED`), which
|
|
1973
|
+
* Where owners hold app upgrades, the node asks for the approval of
|
|
1974
|
+
* whoever the package's manifest names (`412 CONFIRM_REQUIRED`), which
|
|
1975
|
+
* `passkey` gives. Refused (`403`) to a session opened on an app's
|
|
1976
|
+
* frontend: an app asks its user with {@link requestConsent} instead. Past
|
|
1977
|
+
* a release deadline its approver has not met: {@link ReleaseDeadlineError}.
|
|
1834
1978
|
*/
|
|
1835
1979
|
upgradeApp(app, target, passkey) {
|
|
1836
1980
|
const path = `apps/${seg(app)}/upgrade`;
|
|
@@ -2003,8 +2147,11 @@ var CalimeroClient = class {
|
|
|
2003
2147
|
/**
|
|
2004
2148
|
* Approve a mero-tee release to hold the apps of `org`, or of this account
|
|
2005
2149
|
* outside any organization (`org` `null`); `approved: false` withdraws it.
|
|
2006
|
-
* Until then, nodes on that release get none of those apps' data.
|
|
2007
|
-
* approves with `passkey
|
|
2150
|
+
* Until then, nodes on that release get none of those apps' data. For an
|
|
2151
|
+
* organization, a member holding `APPROVE_RELEASES` approves with `passkey`;
|
|
2152
|
+
* the node binds the release's measurements itself. Refused (`403`) to a
|
|
2153
|
+
* session opened on an app's frontend: an app asks its user with
|
|
2154
|
+
* {@link requestConsent} instead.
|
|
2008
2155
|
*/
|
|
2009
2156
|
async approveRelease(org, version, passkey, approved = true) {
|
|
2010
2157
|
const path = org === null ? "auth/releases" : `orgs/${seg(org)}/releases`;
|
|
@@ -2015,7 +2162,8 @@ var CalimeroClient = class {
|
|
|
2015
2162
|
* `org`'s apps or this account's own (`org` `null`). `prefixes` `'manifest'`
|
|
2016
2163
|
* (the default) approves every prefix the package's latest release declares;
|
|
2017
2164
|
* `replace` sets exactly `prefixes`, which is how one is revoked. Approved
|
|
2018
|
-
* with `passkey`.
|
|
2165
|
+
* with `passkey`. Refused (`403`) to a session opened on an app's frontend:
|
|
2166
|
+
* an app asks with {@link requestConsent} (`kind: 'outbound'`) instead.
|
|
2019
2167
|
*/
|
|
2020
2168
|
async approveOutbound(org, pkg, passkey, options = {}) {
|
|
2021
2169
|
const path = org === null ? "auth/outbound" : `orgs/${seg(org)}/outbound`;
|
|
@@ -2025,6 +2173,137 @@ var CalimeroClient = class {
|
|
|
2025
2173
|
passkey
|
|
2026
2174
|
);
|
|
2027
2175
|
}
|
|
2176
|
+
// ---------------------------------------------------------------- approvals
|
|
2177
|
+
/**
|
|
2178
|
+
* One page of the report of a scope's apps: an organization's (any member;
|
|
2179
|
+
* `404` to anyone else) or, with `org` `null`, the account's own outside any
|
|
2180
|
+
* organization (a session bound to an app's frontend: that package's apps).
|
|
2181
|
+
* The governance, the scope's approved releases, the releases the cluster's
|
|
2182
|
+
* nodes run and whether each is approved, and per app its release, newer
|
|
2183
|
+
* releases it may move to, last upgrade and replicas.
|
|
2184
|
+
*/
|
|
2185
|
+
appsReport(org, page = {}) {
|
|
2186
|
+
const path = org === null ? "auth/apps" : `orgs/${seg(org)}/apps`;
|
|
2187
|
+
return this.get(`${path}${pageQuery(page)}`);
|
|
2188
|
+
}
|
|
2189
|
+
/** {@link appsReport}, every page, with the apps of all of them in `apps`. */
|
|
2190
|
+
async appsReportAll(org) {
|
|
2191
|
+
const first = await this.appsReport(org);
|
|
2192
|
+
const apps = [...first.apps];
|
|
2193
|
+
let next = first.next;
|
|
2194
|
+
while (next !== null) {
|
|
2195
|
+
const page = await this.appsReport(org, { after: next });
|
|
2196
|
+
apps.push(...page.apps);
|
|
2197
|
+
next = page.next;
|
|
2198
|
+
}
|
|
2199
|
+
return { ...first, apps, next: null };
|
|
2200
|
+
}
|
|
2201
|
+
/** `GET /releases`: the cluster's governance and every release deadline. Public. */
|
|
2202
|
+
releaseRules() {
|
|
2203
|
+
return this.get("releases");
|
|
2204
|
+
}
|
|
2205
|
+
/**
|
|
2206
|
+
* What waits for a decision in a scope (`org`, or the account's own apps
|
|
2207
|
+
* with `null`): mero-tee releases the cluster's nodes run that the scope has
|
|
2208
|
+
* not approved, apps with newer releases of their package, and whether a
|
|
2209
|
+
* passed deadline stops new work. Read over the sealed channel; act on an
|
|
2210
|
+
* item by sending the user to the consent page ({@link requestConsent},
|
|
2211
|
+
* `releaseConsent`, `upgradeConsent`).
|
|
2212
|
+
*/
|
|
2213
|
+
async pendingApprovals(org = null) {
|
|
2214
|
+
const [report, rules] = await Promise.all([this.appsReportAll(org), this.releaseRules()]);
|
|
2215
|
+
return pendingApprovalsFrom(report, rules);
|
|
2216
|
+
}
|
|
2217
|
+
/**
|
|
2218
|
+
* Ask the node to ask the user: a release, upgrade or outbound approval
|
|
2219
|
+
* that only the account holder may make, decided on the node's own consent
|
|
2220
|
+
* page, never in the app. Needs a passkey session (a session bound to the
|
|
2221
|
+
* app's frontend is fine). Send the person to the answer's `url`; the page
|
|
2222
|
+
* sends them back to `returnTo?request_id=…&result=approved|declined`
|
|
2223
|
+
* (`consentReturn`), and {@link consentResult} confirms the decision.
|
|
2224
|
+
*/
|
|
2225
|
+
requestConsent(request) {
|
|
2226
|
+
return this.post("consent/requests", request);
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* {@link requestConsent}, then send the browser to the consent page
|
|
2230
|
+
* (`navigate`, by default `window.location.assign`). Answers the request
|
|
2231
|
+
* once `navigate` returns: with a popup (`url => window.open(url)`), poll it
|
|
2232
|
+
* with {@link waitForConsent}.
|
|
2233
|
+
*/
|
|
2234
|
+
async openConsentPage(request, navigate = (url2) => {
|
|
2235
|
+
const w = globalThis.window;
|
|
2236
|
+
if (!w) throw new Error("no window to open the consent page in; pass `navigate`");
|
|
2237
|
+
w.location.assign(url2);
|
|
2238
|
+
}) {
|
|
2239
|
+
const started = await this.requestConsent(request);
|
|
2240
|
+
navigate(started.url);
|
|
2241
|
+
return started;
|
|
2242
|
+
}
|
|
2243
|
+
/** The decision on a consent request (any node answers, for the account that asked). */
|
|
2244
|
+
consentResult(requestId) {
|
|
2245
|
+
return this.get(`consent/${seg(requestId)}/result`);
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Poll {@link consentResult} until the request is decided, every
|
|
2249
|
+
* `intervalMs` (2 s), until `signal` aborts or `expiresAt` (Unix seconds)
|
|
2250
|
+
* passes, when it answers the last `pending` result.
|
|
2251
|
+
*/
|
|
2252
|
+
async waitForConsent(requestId, options = {}) {
|
|
2253
|
+
const interval = options.intervalMs ?? 2e3;
|
|
2254
|
+
for (; ; ) {
|
|
2255
|
+
const result = await this.consentResult(requestId);
|
|
2256
|
+
if (result.status !== "pending") return result;
|
|
2257
|
+
if (options.signal?.aborted) return result;
|
|
2258
|
+
if (options.expiresAt !== void 0 && Date.now() / 1e3 >= options.expiresAt) return result;
|
|
2259
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
/** `GET /publishers/{signerId}`: the cluster name and binding generation a key signs to bind it next. */
|
|
2263
|
+
publisherBinding(signerId) {
|
|
2264
|
+
return this.get(`publishers/${seg(signerId)}`);
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Bind a package signing key to `org`, which then approves the node images
|
|
2268
|
+
* every instance of the key's packages runs on, and upgrades those whose
|
|
2269
|
+
* package declares `upgrades: publisher`. `proof` is the key's signature
|
|
2270
|
+
* over `publisherBindingMessage(cluster, org, signerId, generation)`; a
|
|
2271
|
+
* member holding `APPROVE_RELEASES` sends it with `passkey`.
|
|
2272
|
+
*/
|
|
2273
|
+
bindPublisher(org, proof, passkey) {
|
|
2274
|
+
return this.postConfirmed(`orgs/${seg(org)}/publishers`, { ...proof }, passkey);
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Upgrade every instance of `pkg` whose release declares `upgrades:
|
|
2278
|
+
* publisher`, whoever owns it, as its publisher `org` (a
|
|
2279
|
+
* `MANAGE_APPLICATION` holder there), with one passkey approval. Answers
|
|
2280
|
+
* the run, kept on the publisher.
|
|
2281
|
+
*/
|
|
2282
|
+
upgradePublished(org, pkg, target, passkey) {
|
|
2283
|
+
return this.postConfirmed(`orgs/${seg(org)}/packages/${seg(pkg)}/upgrade`, { ...target }, passkey);
|
|
2284
|
+
}
|
|
2285
|
+
// ---------------------------------------------------------------- operator
|
|
2286
|
+
/** `GET /ops/apps`: what every app runs, without names or accounts. Operator token (`useApiKey`). */
|
|
2287
|
+
opsApps(page = {}) {
|
|
2288
|
+
return this.get(`ops/apps${pageQuery(page)}`);
|
|
2289
|
+
}
|
|
2290
|
+
/** `GET /ops/orgs`: each organization's approved releases. Operator token. */
|
|
2291
|
+
opsOrgs(page = {}) {
|
|
2292
|
+
return this.get(`ops/orgs${pageQuery(page)}`);
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* Announce when work on images older than `version` ends: from `after` on,
|
|
2296
|
+
* a scope that has not approved it (or newer) starts no new work. Operator
|
|
2297
|
+
* token.
|
|
2298
|
+
*/
|
|
2299
|
+
async setReleaseDeadline(version, after) {
|
|
2300
|
+
const at = typeof after === "string" ? after : after.toISOString();
|
|
2301
|
+
await this.post(`ops/releases/${seg(version)}/deadline`, { after: at });
|
|
2302
|
+
}
|
|
2303
|
+
/** Lift a release's deadline. Operator token. */
|
|
2304
|
+
async liftReleaseDeadline(version) {
|
|
2305
|
+
await this.delete(`ops/releases/${seg(version)}/deadline`);
|
|
2306
|
+
}
|
|
2028
2307
|
/** Add a member, or change their role. */
|
|
2029
2308
|
async setMember(org, account, role) {
|
|
2030
2309
|
await this.post(`orgs/${seg(org)}/members`, { account, role });
|
|
@@ -2084,7 +2363,7 @@ var ORG_CAPABILITIES = {
|
|
|
2084
2363
|
JOIN_OPEN_TEAMS: 1 << 2,
|
|
2085
2364
|
/** Add and remove a team's members. */
|
|
2086
2365
|
MANAGE_MEMBERS: 1 << 3,
|
|
2087
|
-
/** Upgrade the organization's apps, start org upgrade runs, approve outbound hosts (org-wide). */
|
|
2366
|
+
/** Upgrade the organization's apps (and, as a publisher, every instance of its `publisher` packages), start org upgrade runs, approve outbound hosts (org-wide). */
|
|
2088
2367
|
MANAGE_APPLICATION: 1 << 4,
|
|
2089
2368
|
/** Create a top-level team without being an admin. */
|
|
2090
2369
|
CREATE_TEAM: 1 << 5,
|
|
@@ -2093,7 +2372,14 @@ var ORG_CAPABILITIES = {
|
|
|
2093
2372
|
/** Change who may join a team (open or invite-only). */
|
|
2094
2373
|
MANAGE_VISIBILITY: 1 << 7,
|
|
2095
2374
|
/** Set the organization's and its teams' metadata. */
|
|
2096
|
-
MANAGE_METADATA: 1 << 8
|
|
2375
|
+
MANAGE_METADATA: 1 << 8,
|
|
2376
|
+
/**
|
|
2377
|
+
* Approve (or withdraw) the mero-tee releases that hold the apps the
|
|
2378
|
+
* organization governs (those of the packages it publishes, and its own of
|
|
2379
|
+
* unbound packages), and bind a package signing key to it
|
|
2380
|
+
* (`bindPublisher`). Organization-wide only. Bit 9 is left unused.
|
|
2381
|
+
*/
|
|
2382
|
+
APPROVE_RELEASES: 1 << 10
|
|
2097
2383
|
};
|
|
2098
2384
|
var DEFAULT_ORG_CAPABILITIES = ORG_CAPABILITIES.JOIN_OPEN_TEAMS;
|
|
2099
2385
|
function orgCapabilities(...names) {
|