@apex-actions/cli 0.95.2 → 0.97.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 +13 -13
- package/THIRD-PARTY-NOTICES.md +3 -3
- package/dist/cli/environment/main.js +1 -1
- package/dist/cli/grant/main.js +1 -1
- package/dist/cli/login/device.js +1 -1
- package/dist/cli/login/main.js +6 -6
- package/dist/cli/logout/main.js +2 -2
- package/dist/cli/migrate/main.js +1 -1
- package/dist/cli/notify/main.js +1 -1
- package/dist/cli/reference.js +6 -6
- package/dist/cli/secret/shared.js +1 -1
- package/dist/cli/tenant/main.js +4 -4
- package/dist/cli/token/main.js +3 -3
- package/dist/cli/whoami/main.js +1 -1
- package/dist/src/application/authenticate.js +3 -3
- package/dist/src/application/manage-departures.js +1 -1
- package/dist/src/application/manage-secrets.js +1 -1
- package/dist/src/application/manage-terms.js +1 -1
- package/dist/src/domain/accounts.js +1 -1
- package/dist/src/domain/credentials.js +7 -7
- package/dist/src/domain/fleet-history.js +1 -1
- package/dist/src/domain/grants.js +2 -2
- package/dist/src/domain/insight.js +1 -1
- package/dist/src/domain/logsearch.js +1 -1
- package/dist/src/domain/notify.js +4 -4
- package/dist/src/domain/provenance.js +1 -1
- package/dist/src/domain/runs.js +1 -1
- package/dist/src/domain/spend.js +1 -1
- package/dist/src/domain/supplychain.js +1 -1
- package/dist/src/infrastructure/binary-install.js +1 -1
- package/dist/src/infrastructure/control-plane-http.js +20 -20
- package/dist/src/infrastructure/credentials.js +4 -4
- package/dist/src/infrastructure/device-login.js +1 -1
- package/dist/src/infrastructure/loopback-login.js +1 -1
- package/package.json +3 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`apex` developer and operator CLI (TypeScript, commander).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A workspace of the [Apex Actions](../../README.md) repository. What it is inside: [`docs/architecture/components/cli.md`](../../docs/architecture/components/cli.md); the conventions: [`docs/conventions/`](../../docs/conventions/README.md).
|
|
6
6
|
|
|
7
7
|
## Commands
|
|
8
8
|
|
|
@@ -20,12 +20,12 @@ Part of the [Apex Actions](../) superproject. Conventions, architecture, and age
|
|
|
20
20
|
| `apex secret` · `apex variable` | the control-plane REST API | a customer token with `secrets:*` / `variables:*` (`APEX_TOKEN` or `apex login`) |
|
|
21
21
|
| `apex login --console` · `logout --console` | the Admin Console in the browser, and the control-plane REST API | an operator signed in to the Admin Console, who approves it |
|
|
22
22
|
| `apex grant list` · `apex tenant installations` | the control-plane admin API | the operator token (`apex login --console`), or the machine token |
|
|
23
|
-
| `apex grant create` · `update` · `revoke` | the control-plane admin API | the operator token (`apex login --console`) — never the machine token (
|
|
23
|
+
| `apex grant create` · `update` · `revoke` | the control-plane admin API | the operator token (`apex login --console`) — never the machine token (`api` ADR-0056, `api` ADR-0079) |
|
|
24
24
|
|
|
25
25
|
Every other customer command in `cli/reference.ts` names the permission it needs; the public reference shows it.
|
|
26
26
|
|
|
27
|
-
Generated reference: `docs/
|
|
28
|
-
the structured one the public site renders, `
|
|
27
|
+
Generated reference: `docs/features/cli-commands.md` (`pnpm docs:cli` from `packages/tooling`), and
|
|
28
|
+
the structured one the public site renders, `apps/cli/cli-reference.json` (`pnpm docs:reference`;
|
|
29
29
|
customer commands only, classified in `cli/reference.ts`).
|
|
30
30
|
|
|
31
31
|
```bash
|
|
@@ -38,7 +38,7 @@ apex logs '#42' --job test # one job, clean enough to pipe
|
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
`apex migrate scan` answers "will my workflows run" before anything has moved. Every verdict comes
|
|
41
|
-
out of the engine's conformance corpus with the fixture that proves it (ADR-0002, engine ADR-0013) —
|
|
41
|
+
out of the engine's conformance corpus with the fixture that proves it (`cli` ADR-0002, `engine` ADR-0013) —
|
|
42
42
|
nothing here holds a second opinion about compatibility. It imports secret and variable **names** and
|
|
43
43
|
never values, and `--path` scans a checkout on this machine with no token and no network:
|
|
44
44
|
|
|
@@ -49,8 +49,8 @@ apex migrate scan acme --repo acme/api --json # one repository of an organizat
|
|
|
49
49
|
|
|
50
50
|
### Credentials
|
|
51
51
|
|
|
52
|
-
The CLI resolves one credential, in this order (
|
|
53
|
-
[ADR-0005](docs/
|
|
52
|
+
The CLI resolves one credential, in this order (`api` ADR-0064, amended by
|
|
53
|
+
[ADR-0005](../../docs/adrs/cli/0005-an-exported-token-outranks-a-saved-login.md)) — **the environment outranks
|
|
54
54
|
the file**:
|
|
55
55
|
|
|
56
56
|
1. `APEX_TOKEN` — a token minted on the dashboard (`/settings/tokens`), for CI and scripts;
|
|
@@ -66,7 +66,7 @@ the file**:
|
|
|
66
66
|
**Operator commands** — `apex grant` and `apex tenant installations` — look first for the **operator
|
|
67
67
|
token** `apex login --console` saved in `$XDG_CONFIG_HOME/apex/console-credentials` (same mode rules), when
|
|
68
68
|
it is unexpired and was minted by the control plane `APEX_API_URL` names, and otherwise use the order above
|
|
69
|
-
(
|
|
69
|
+
(`api` ADR-0079). The operator token is approved on the Admin Console (`APEX_CONSOLE_URL`, or the
|
|
70
70
|
API's `app.` host read as `admin.`) by an operator, by loopback only; it lives a week at most and reaches
|
|
71
71
|
only the grant routes and the installation list, and every grant it writes names its operator. The machine
|
|
72
72
|
token can list grants and is refused every write. `apex logout --console` revokes and forgets it.
|
|
@@ -75,7 +75,7 @@ token can list grants and is refused every write. `apex logout --console` revoke
|
|
|
75
75
|
only ever sent to the control plane that issued it. `apex login` opens the consent page on the web
|
|
76
76
|
app — `APEX_WEB_URL`, or the API's origin — and listens on `127.0.0.1` for the loopback callback
|
|
77
77
|
(RFC 8252, PKCE); once the code is redeemed it hands the tab back to the web app's bare `/cli/done`
|
|
78
|
-
page (
|
|
78
|
+
page (`api` ADR-0075). Where no browser can open on this machine — an SSH session
|
|
79
79
|
(`SSH_CONNECTION`/`SSH_TTY`), a Codespace, a dev container, `/.dockerenv`, or Linux with neither `DISPLAY`
|
|
80
80
|
nor `WAYLAND_DISPLAY` — or with `--device` (or `--no-browser`), it uses the **device flow** instead (RFC 8628,
|
|
81
81
|
run by Apex): it prints a code and `…/cli/device`, the person enters the code on any device, a phone included,
|
|
@@ -83,7 +83,7 @@ and the CLI polls — honouring `interval` and `slow_down` — until the approva
|
|
|
83
83
|
minutes end. `--browser` insists on the loopback. `apex token create` and a widening `apex token edit` take
|
|
84
84
|
the same three flags. A customer token carries **permissions** (`resource:read|write`, write implying
|
|
85
85
|
read; resources `actions`, `approvals`, `deployments`, `secrets`, `variables`, `environments`, `notifications`,
|
|
86
|
-
`administration`) and belongs to **one Account** (
|
|
86
|
+
`administration`) and belongs to **one Account** (`api` ADR-0071). It reaches the repository routes its
|
|
87
87
|
grants cover, within its person's role and the Account owner's token policy; `apex login` takes the policy's
|
|
88
88
|
defaults for the person's role (the Owner's: everything), in the Account `--account` names when there is more than
|
|
89
89
|
one. A route it lacks the grant for answers 403 `this token needs <permission>`, which the CLI completes with the
|
|
@@ -103,7 +103,7 @@ apex secret list --repo acme/api --json # names only
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
A pipeline gets its own token. It is approved in the browser like a login, printed once to stdout,
|
|
106
|
-
and never saved (
|
|
106
|
+
and never saved (`api` ADR-0064: a token never mints a token):
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
109
|
apex token create ci-deploy --permission actions:write --permission secrets:write \
|
|
@@ -124,8 +124,8 @@ refused · 2 usage or configuration · 3 not found on `delete`.
|
|
|
124
124
|
- `src/domain/` — pure decisions: run and job selection, input parsing, exit codes
|
|
125
125
|
- `src/application/` — use cases over ports
|
|
126
126
|
- `src/infrastructure/` — adapters: the engine and runner binaries, the REST API
|
|
127
|
-
-
|
|
128
|
-
- `VERSION` —
|
|
127
|
+
- Documentation is the root's: decisions in `docs/adrs/cli/`, the rest by category in `docs/` (TASK-0526)
|
|
128
|
+
- `VERSION` — the release version, which `version:sync` writes from the root (ADR-0048); never edited by hand
|
|
129
129
|
|
|
130
130
|
## Licence
|
|
131
131
|
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
**Generated — do not edit.** `pnpm notices` from `packages/
|
|
3
|
+
**Generated — do not edit.** `pnpm notices` from `packages/tooling`, for `cli`.
|
|
4
4
|
|
|
5
5
|
Production npm dependencies, as resolved by pnpm. 40 packages: 0BSD, Apache-2.0, ISC, MIT.
|
|
6
6
|
|
|
@@ -489,7 +489,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
489
489
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
490
490
|
```
|
|
491
491
|
|
|
492
|
-
## @types/node 24.
|
|
492
|
+
## @types/node 24.19.2
|
|
493
493
|
|
|
494
494
|
- License: MIT
|
|
495
495
|
- Homepage: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node
|
|
@@ -1162,7 +1162,7 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
1162
1162
|
PERFORMANCE OF THIS SOFTWARE.
|
|
1163
1163
|
```
|
|
1164
1164
|
|
|
1165
|
-
## undici-types 7.
|
|
1165
|
+
## undici-types 7.24.6
|
|
1166
1166
|
|
|
1167
1167
|
- License: MIT
|
|
1168
1168
|
- Homepage: https://undici.nodejs.org
|
|
@@ -127,7 +127,7 @@ const deleteCommand = new Command('delete')
|
|
|
127
127
|
return;
|
|
128
128
|
case 'deleted':
|
|
129
129
|
// Anything already waiting stays waiting: it was gated by rules that
|
|
130
|
-
// existed when it was gated (
|
|
130
|
+
// existed when it was gated (api ADR-0018).
|
|
131
131
|
console.log(`${chalk.green('✔')} ${name} is no longer protected`);
|
|
132
132
|
console.log(chalk.dim('Deployments already waiting stay waiting; approve or reject them.'));
|
|
133
133
|
}
|
package/dist/cli/grant/main.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createGrant, listGrants, revokeGrant, updateGrant } from '../../src/app
|
|
|
4
4
|
import { createGrantBody, createdLine, grantLine, revokedLine, updateGrantBody } from '../../src/domain/grants.js';
|
|
5
5
|
import { HttpControlPlane, NotConfiguredError } from '../../src/infrastructure/control-plane-http.js';
|
|
6
6
|
/**
|
|
7
|
-
* `apex grant`: complimentary access from the terminal (
|
|
7
|
+
* `apex grant`: complimentary access from the terminal (api ADR-0056 as amended by api ADR-0079,
|
|
8
8
|
* TASK-0199). The admin API's own shapes, unchanged. Every write needs a person: the operator token
|
|
9
9
|
* `apex login --console` saved, whose operator the grant row and the audit log name. The machine token
|
|
10
10
|
* (`APEX_ADMIN_TOKEN`) can list and is refused every write, so a leaked one cannot make a free tenant.
|
package/dist/cli/login/device.js
CHANGED
|
@@ -3,7 +3,7 @@ import { chooseFlow, deviceInstructions } from '../../src/domain/credentials.js'
|
|
|
3
3
|
import { HttpDeviceExchange, detectHeadless, machineReport, sleep } from '../../src/infrastructure/device-login.js';
|
|
4
4
|
/**
|
|
5
5
|
* How `apex login`, `apex token create` and a widening `apex token edit` get their approval back
|
|
6
|
-
* (
|
|
6
|
+
* (api ADR-0075): the loopback redirect where a browser opens on this machine, a device code
|
|
7
7
|
* typed on any device where one cannot. One set of flags for the three commands.
|
|
8
8
|
*/
|
|
9
9
|
export const withFlowOptions = (command) => command
|
package/dist/cli/login/main.js
CHANGED
|
@@ -8,20 +8,20 @@ import { HttpLoginExchange, listenForCallback, newPkcePair, openInBrowser } from
|
|
|
8
8
|
import { approvalFlow, withFlowOptions } from './device.js';
|
|
9
9
|
/**
|
|
10
10
|
* `apex login`: sign the CLI in with the browser a person already uses
|
|
11
|
-
* (
|
|
11
|
+
* (api ADR-0064, RFC 8252 + PKCE). The token it mints is the same
|
|
12
12
|
* one the dashboard mints, saved `0600` for this user alone.
|
|
13
13
|
*
|
|
14
14
|
* It opens a browser only because it was asked to — this command is the
|
|
15
15
|
* asking — and prints the URL either way. Where no browser can open on this
|
|
16
16
|
* machine (SSH, a container, a Codespace, a Linux box with no display), or
|
|
17
17
|
* with `--device`, it prints a code to enter on any device instead
|
|
18
|
-
* (
|
|
18
|
+
* (api ADR-0075); `--browser` insists on the browser here.
|
|
19
19
|
*
|
|
20
20
|
* The token carries the Account token policy's defaults for the person's role
|
|
21
21
|
* there (the Owner's: everything), and belongs to that one Account — named with
|
|
22
|
-
* `--account` when the person is in more than one (
|
|
22
|
+
* `--account` when the person is in more than one (api ADR-0071).
|
|
23
23
|
*
|
|
24
|
-
* `--console` is the operator's sign-in (
|
|
24
|
+
* `--console` is the operator's sign-in (api ADR-0079): approved on the
|
|
25
25
|
* Admin Console by an operator, it saves an **operator token** beside the Account
|
|
26
26
|
* login — a week at most, reaching only complimentary access (`apex grant`).
|
|
27
27
|
*/
|
|
@@ -77,7 +77,7 @@ export default withFlowOptions(new Command('login')
|
|
|
77
77
|
console.log(` permissions: ${describeGrants(outcome.permissions)}${outcome.accountId === undefined ? '' : ` · Account ${outcome.accountId}`}`);
|
|
78
78
|
}
|
|
79
79
|
console.log(chalk.dim(`Saved to ${outcome.location}. Revoke it any time at ${webBase}${TOKENS_PAGE}.`));
|
|
80
|
-
// The environment outranks the saved login (ADR-0005): say so, or the login looks like it did nothing.
|
|
80
|
+
// The environment outranks the saved login (cli ADR-0005): say so, or the login looks like it did nothing.
|
|
81
81
|
for (const name of [TOKEN_ENV, ADMIN_TOKEN_ENV]) {
|
|
82
82
|
if (process.env[name])
|
|
83
83
|
console.log(chalk.yellow(`${name} is set in this shell and is used before the saved login; unset it to use the login.`));
|
|
@@ -107,7 +107,7 @@ const reportFailure = (outcome, command) => {
|
|
|
107
107
|
}
|
|
108
108
|
};
|
|
109
109
|
/**
|
|
110
|
-
* `apex login --console` (
|
|
110
|
+
* `apex login --console` (api ADR-0079): the loopback approval on the Admin Console, saving the
|
|
111
111
|
* operator token to its own file. No device flow — `--device` is refused rather than silently ignored.
|
|
112
112
|
*/
|
|
113
113
|
const consoleSignIn = async (opts) => {
|
package/dist/cli/logout/main.js
CHANGED
|
@@ -9,12 +9,12 @@ import { ADMIN_TOKEN_ENV, CONSOLE_URL_ENV, FileCredentialStore, TOKEN_ENV, WEB_U
|
|
|
9
9
|
*
|
|
10
10
|
* The token is revoked at the control plane that minted it — never at whatever
|
|
11
11
|
* `APEX_API_URL` says now — with `DELETE /api/v1/me/token`, the one thing a
|
|
12
|
-
* token may do to itself (
|
|
12
|
+
* token may do to itself (api ADR-0064). The file goes whether or not
|
|
13
13
|
* the server could be reached; when it could not, the command says the token
|
|
14
14
|
* may still be valid and where to revoke it, rather than implying it is dead.
|
|
15
15
|
*
|
|
16
16
|
* `--console` does the same to the operator token `apex login --console` saved
|
|
17
|
-
* (
|
|
17
|
+
* (api ADR-0079), which may revoke itself and nothing else.
|
|
18
18
|
*/
|
|
19
19
|
export default new Command('logout')
|
|
20
20
|
.description('Revoke the token `apex login` saved, and forget it')
|
package/dist/cli/migrate/main.js
CHANGED
|
@@ -194,7 +194,7 @@ const ScanCommand = new Command('scan')
|
|
|
194
194
|
* coexistence **on** first — from that moment Apex plans nothing here — then add
|
|
195
195
|
* workflows as you gain confidence in them. Doing it the other way round leaves
|
|
196
196
|
* a window in which everything runs on both, which is how a publishing workflow
|
|
197
|
-
* nearly pushed the same images twice (
|
|
197
|
+
* nearly pushed the same images twice (api ADR-0046).
|
|
198
198
|
*/
|
|
199
199
|
const CoexistCommand = new Command('coexist')
|
|
200
200
|
.description('Run Apex beside GitHub Actions on one repository, opting workflows in one at a time')
|
package/dist/cli/notify/main.js
CHANGED
|
@@ -42,7 +42,7 @@ const printDelivery = (d) => {
|
|
|
42
42
|
const status = `${d.status}${d.httpStatus === undefined ? '' : ` ${String(d.httpStatus)}`}`;
|
|
43
43
|
console.log(` ${chalk.dim(d.createdAt)} ${d.ruleName.padEnd(18)} ${d.trigger.padEnd(10)} ${colour(status.padEnd(12))} ${d.attempts > 1 ? chalk.dim(`${String(d.attempts)} attempts`) : ''}`);
|
|
44
44
|
// Somebody else's server's words, so they are printed as data and never
|
|
45
|
-
// interpreted (
|
|
45
|
+
// interpreted (api ADR-0029).
|
|
46
46
|
if (d.detail !== undefined && d.detail !== '')
|
|
47
47
|
console.log(chalk.dim(` ${d.detail}`));
|
|
48
48
|
};
|
package/dist/cli/reference.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* sits in, who it is for, and a few examples a person can copy.
|
|
4
4
|
*
|
|
5
5
|
* `test/cli-reference.test.ts` walks `buildProgram` and writes
|
|
6
|
-
* `
|
|
6
|
+
* `apps/cli/cli-reference.json` from the tree plus this table; the
|
|
7
7
|
* marketing site renders that file. A top-level command missing from here
|
|
8
8
|
* fails the test, so nothing reaches the site — or escapes it — unclassified.
|
|
9
9
|
*
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* for.** A customer's credential is an API token carrying the permissions its
|
|
12
12
|
* person chose (`resource:read|write`, write implying read), within one
|
|
13
13
|
* Account, their role there and the Account's token policy (control-plane
|
|
14
|
-
* ADR-0071). A token reaches the control plane's repository routes its grants
|
|
14
|
+
* api ADR-0071). A token reaches the control plane's repository routes its grants
|
|
15
15
|
* cover, plus its own identity and tokens; every other route — the fleet, the
|
|
16
16
|
* operator's organisation, tenants, billing — answers it 403. So a command is
|
|
17
17
|
* `customer` exactly when every route it calls is a repository or self route in
|
|
@@ -49,7 +49,7 @@ export const REFERENCE = {
|
|
|
49
49
|
},
|
|
50
50
|
{ command: 'apex login --browser', description: 'Open a browser on this machine even where the CLI would choose a code.' },
|
|
51
51
|
],
|
|
52
|
-
// The operator's sign-in, approved on the Admin Console (
|
|
52
|
+
// The operator's sign-in, approved on the Admin Console (api ADR-0079).
|
|
53
53
|
operatorOptions: ['--console'],
|
|
54
54
|
},
|
|
55
55
|
logout: {
|
|
@@ -126,7 +126,7 @@ export const REFERENCE = {
|
|
|
126
126
|
secret: {
|
|
127
127
|
group: 'config',
|
|
128
128
|
audience: 'customer',
|
|
129
|
-
// `--org <organization>` asks for organization:read|write in that Organization instead (
|
|
129
|
+
// `--org <organization>` asks for organization:read|write in that Organization instead (api ADR-0084).
|
|
130
130
|
subcommandPermissions: { list: 'secrets:read', set: 'secrets:write', delete: 'secrets:write' },
|
|
131
131
|
examples: [
|
|
132
132
|
{ command: 'printf %s "$NPM_TOKEN" | apex secret set NPM_TOKEN --repo acme/web', description: 'Set a secret from a pipe; a value never goes on the command line.' },
|
|
@@ -266,7 +266,7 @@ export const REFERENCE = {
|
|
|
266
266
|
actions: {
|
|
267
267
|
group: 'config',
|
|
268
268
|
audience: 'customer',
|
|
269
|
-
// An organisation's action policy asks for organization:* in that Organization (
|
|
269
|
+
// An organisation's action policy asks for organization:* in that Organization (api ADR-0084, TASK-0279).
|
|
270
270
|
subcommandPermissions: { list: 'actions:read', pin: 'administration:write', policy: 'organization:read' },
|
|
271
271
|
examples: [
|
|
272
272
|
{ command: 'apex actions list acme/web', description: 'Every action a repository pulls in, least fixed first.' },
|
|
@@ -312,6 +312,6 @@ export const REFERENCE = {
|
|
|
312
312
|
runner: { group: 'runners', audience: OPERATOR },
|
|
313
313
|
terms: { group: 'billing', audience: OPERATOR },
|
|
314
314
|
tenant: { group: 'billing', audience: OPERATOR },
|
|
315
|
-
// Complimentary access: the operator token `apex login --console` saves writes; no Account token reaches it (
|
|
315
|
+
// Complimentary access: the operator token `apex login --console` saves writes; no Account token reaches it (api ADR-0079).
|
|
316
316
|
grant: { group: 'billing', audience: OPERATOR },
|
|
317
317
|
};
|
|
@@ -6,7 +6,7 @@ import { nameProblem, reachFrom, secretAppFrom } from '../../src/domain/secrets.
|
|
|
6
6
|
import { HttpControlPlane, NotConfiguredError } from '../../src/infrastructure/control-plane-http.js';
|
|
7
7
|
/** What `--org` says in every subcommand's help. */
|
|
8
8
|
export const ORG_OPTION = ['--org [organization]', 'an organization’s scope, by its GitHub login (bare: the operator organization)'];
|
|
9
|
-
/** `gh secret --app`'s spelling (
|
|
9
|
+
/** `gh secret --app`'s spelling (TASK-0394): Dependabot's store is handed only to a run Dependabot started. */
|
|
10
10
|
export const APP_OPTION = ['--app <app>', 'which store: actions (default) or dependabot, handed only to runs Dependabot started'];
|
|
11
11
|
export const collect = (value, previous) => [...previous, value];
|
|
12
12
|
export const usage = (message) => {
|
package/dist/cli/tenant/main.js
CHANGED
|
@@ -20,7 +20,7 @@ const api = () => {
|
|
|
20
20
|
};
|
|
21
21
|
const departuresCommand = new Command('departures')
|
|
22
22
|
.description('Every tenant that uninstalled, newest first, and when its data is deleted')
|
|
23
|
-
.option('--repositories', 'repositories removed from an installation that stays, instead (ADR-0069 amendment)')
|
|
23
|
+
.option('--repositories', 'repositories removed from an installation that stays, instead (api ADR-0069 amendment)')
|
|
24
24
|
.option('--json', 'machine-readable output')
|
|
25
25
|
.action(async (opts) => {
|
|
26
26
|
const client = api();
|
|
@@ -62,7 +62,7 @@ const departuresCommand = new Command('departures')
|
|
|
62
62
|
for (const d of outcome.value)
|
|
63
63
|
console.log(` ${departureLine(d)}`);
|
|
64
64
|
});
|
|
65
|
-
/** `apex tenant purge --repository <repoId>` (
|
|
65
|
+
/** `apex tenant purge --repository <repoId>` (TASK-0331). */
|
|
66
66
|
const purgeRemovedRepository = async (raw, opts) => {
|
|
67
67
|
const repoId = Number(raw);
|
|
68
68
|
if (!Number.isSafeInteger(repoId) || repoId <= 0) {
|
|
@@ -98,7 +98,7 @@ const purgeRemovedRepository = async (raw, opts) => {
|
|
|
98
98
|
const purgeCommand = new Command('purge')
|
|
99
99
|
.description("Delete one departed tenant's data now, whatever its window — a dry run unless --apply")
|
|
100
100
|
.argument('[installationId]', 'the installation that left')
|
|
101
|
-
.option('--repository <repoId>', 'a repository removed from an installation that stays, instead (ADR-0069 amendment)')
|
|
101
|
+
.option('--repository <repoId>', 'a repository removed from an installation that stays, instead (api ADR-0069 amendment)')
|
|
102
102
|
.option('--apply', 'delete it (default: report what would be deleted)')
|
|
103
103
|
.option('--json', 'machine-readable output')
|
|
104
104
|
.action(async (raw, opts) => {
|
|
@@ -227,7 +227,7 @@ const installationsCommand = new Command('installations')
|
|
|
227
227
|
.option('--fresh', 'only installations that never ran, paid or were granted')
|
|
228
228
|
.option('--json', 'machine-readable output')
|
|
229
229
|
.action(async (opts) => {
|
|
230
|
-
// The operator token `apex login --console` saved reaches this route too (
|
|
230
|
+
// The operator token `apex login --console` saved reaches this route too (api ADR-0079).
|
|
231
231
|
let client;
|
|
232
232
|
try {
|
|
233
233
|
client = new HttpControlPlane({ operator: true });
|
package/dist/cli/token/main.js
CHANGED
|
@@ -8,14 +8,14 @@ import { api, collect, usage } from '../secret/shared.js';
|
|
|
8
8
|
import { approvalFlow, withFlowOptions } from '../login/device.js';
|
|
9
9
|
/**
|
|
10
10
|
* `apex token`: the person's API tokens from the terminal (control-plane
|
|
11
|
-
* ADR-0064, amended 2026-09-27).
|
|
11
|
+
* api ADR-0064, amended 2026-09-27).
|
|
12
12
|
*
|
|
13
13
|
* `create` asks for the person's approval in the browser, exactly as `apex
|
|
14
14
|
* login` does, and prints the token once — the token alone on stdout, so
|
|
15
15
|
* `$(apex token create …)` captures it, and everything a person reads on
|
|
16
16
|
* stderr. It is never saved, and the CLI's own login is left as it was.
|
|
17
17
|
* A token carries the permissions asked for with `--permission` and belongs
|
|
18
|
-
* to one Account (
|
|
18
|
+
* to one Account (api ADR-0071).
|
|
19
19
|
* `edit` narrows a token at once with whichever credential the CLI has, and
|
|
20
20
|
* widens one only through the same browser approval.
|
|
21
21
|
* `list` and `revoke` use whichever credential the CLI already has.
|
|
@@ -25,7 +25,7 @@ import { approvalFlow, withFlowOptions } from '../login/device.js';
|
|
|
25
25
|
const PERMISSION_HELP = `resource:read or resource:write (write implies read); resource is one of ${TOKEN_RESOURCES.join(', ')}`;
|
|
26
26
|
/**
|
|
27
27
|
* The approval half `create` and a widening `edit` share — the loopback on this machine, or a device code
|
|
28
|
-
* (
|
|
28
|
+
* (api ADR-0075). Everything a person reads goes to stderr: stdout is the token's alone.
|
|
29
29
|
*/
|
|
30
30
|
const approvalDeps = (flags, what) => {
|
|
31
31
|
const apiUrl = apiUrlFrom();
|
package/dist/cli/whoami/main.js
CHANGED
|
@@ -24,7 +24,7 @@ const credentialLine = (credential) => {
|
|
|
24
24
|
* credential got it there. The answer is the server's (`GET /api/v1/me`), not
|
|
25
25
|
* a guess from the local file; the credential itself is never printed.
|
|
26
26
|
*
|
|
27
|
-
* It also names any credential that was present and lost (ADR-0005: the
|
|
27
|
+
* It also names any credential that was present and lost (cli ADR-0005: the
|
|
28
28
|
* environment outranks the saved login), because the surprise this command
|
|
29
29
|
* exists to prevent is acting as somebody other than the one you meant.
|
|
30
30
|
*
|
|
@@ -35,7 +35,7 @@ export const approveInBrowser = async (deps, token, accountId) => {
|
|
|
35
35
|
/** RFC 8628 §3.2's default when a server names no interval. */
|
|
36
36
|
const DEFAULT_INTERVAL_SECONDS = 5;
|
|
37
37
|
/**
|
|
38
|
-
* The device flow (
|
|
38
|
+
* The device flow (api ADR-0075, RFC 8628 run by Apex): ask for a request, show the code, and
|
|
39
39
|
* poll with the device code — which never leaves this process — every `interval` seconds, slowing down
|
|
40
40
|
* when told, until the person approves or denies on any device, or the code's ten minutes pass.
|
|
41
41
|
*/
|
|
@@ -113,7 +113,7 @@ export const login = async (deps) => {
|
|
|
113
113
|
};
|
|
114
114
|
};
|
|
115
115
|
/**
|
|
116
|
-
* `apex login --console` (
|
|
116
|
+
* `apex login --console` (api ADR-0079): the same loopback + PKCE approval, opened on the Admin
|
|
117
117
|
* Console's bare page rather than the app's — `deps.webBase` is the Console's — and redeemed at the same
|
|
118
118
|
* control plane, for an **operator token**. Only the loopback: an operator approves in the Console's own
|
|
119
119
|
* browser session. What comes back must be an operator token, or nothing is saved.
|
|
@@ -132,7 +132,7 @@ export const consoleLogin = async (deps) => {
|
|
|
132
132
|
};
|
|
133
133
|
/**
|
|
134
134
|
* Revoke the saved token at the control plane that minted it, then forget it
|
|
135
|
-
* (
|
|
135
|
+
* (api ADR-0064: a token may revoke itself and nothing else). The file
|
|
136
136
|
* goes either way — keeping a credential because the network was down would be
|
|
137
137
|
* the wrong failure — and a revocation that did not happen is reported as such,
|
|
138
138
|
* never implied.
|
|
@@ -16,7 +16,7 @@ export const listDepartures = async (api) => await attempt(() => api.departures(
|
|
|
16
16
|
* left to ask.
|
|
17
17
|
*/
|
|
18
18
|
export const purgeDeparture = async (api, installationId, apply) => await attempt(() => api.purgeDeparture(installationId, apply));
|
|
19
|
-
/** Every repository the App was removed from, newest first (
|
|
19
|
+
/** Every repository the App was removed from, newest first (api ADR-0069 amendment). */
|
|
20
20
|
export const listRepositoryDepartures = async (api) => await attempt(() => api.repositoryDepartures());
|
|
21
21
|
/** Purge one removed repository's data now, whatever its window — a dry run unless `apply`; machine token only. */
|
|
22
22
|
export const purgeRepositoryDeparture = async (api, repoId, apply) => await attempt(() => api.purgeRepositoryDeparture(repoId, apply));
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { targetLabel } from '../domain/secrets.js';
|
|
3
3
|
/**
|
|
4
4
|
* Why the unnamed organisation scope refused: it is the operator organisation's
|
|
5
|
-
* (BUG-0112). A customer names theirs — `--org <login>` (ADR-0084) — and that
|
|
5
|
+
* (BUG-0112). A customer names theirs — `--org <login>` (api ADR-0084) — and that
|
|
6
6
|
* route's refusal explains itself, so it is relayed as worded.
|
|
7
7
|
*/
|
|
8
8
|
export const ORG_SCOPE_REFUSAL = 'the bare --org scope is the operator organisation’s — name yours: --org <your-organization>';
|
|
@@ -12,7 +12,7 @@ const attempt = async (fn) => {
|
|
|
12
12
|
* The API answers what *this caller* may act for; the CLI authenticates as the
|
|
13
13
|
* machine principal, which administers every account, so an operator sees the
|
|
14
14
|
* whole list. Nothing is classified here — outstanding is the control plane's
|
|
15
|
-
* judgement (
|
|
15
|
+
* judgement (api ADR-0039 amendment 1) and a second one would be a
|
|
16
16
|
* second answer to a question that must have one.
|
|
17
17
|
*/
|
|
18
18
|
export const readTermsStanding = async (api) => await attempt(() => api.termsStanding());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Accounts backfill as the control plane reports it (control-plane
|
|
3
|
-
* `POST /api/v1/
|
|
3
|
+
* `POST /api/v1/office/accounts/backfill`, TASK-0195), and how a person reads it.
|
|
4
4
|
*/
|
|
5
5
|
const ORG_VERB = {
|
|
6
6
|
connect: ['would join', 'joined'],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
/**
|
|
3
3
|
* A customer's terminal credential, and the browser login that mints one
|
|
4
|
-
* (
|
|
4
|
+
* (api ADR-0064).
|
|
5
5
|
*
|
|
6
6
|
* Pure decisions only: where the consent page is, whether a loopback callback
|
|
7
7
|
* is the one this login asked for, whether a file mode is private enough to
|
|
@@ -24,10 +24,10 @@ export const describeSource = (source) => {
|
|
|
24
24
|
return 'the operator token `apex login --console` saved';
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
-
/** An operator token's prefix (
|
|
27
|
+
/** An operator token's prefix (api ADR-0079); an Account token's is `apex_pat_`. */
|
|
28
28
|
export const OPERATOR_TOKEN_PREFIX = 'apex_opt_';
|
|
29
29
|
/**
|
|
30
|
-
* The Admin Console's base URL, where `apex login --console` is approved (
|
|
30
|
+
* The Admin Console's base URL, where `apex login --console` is approved (api ADR-0079):
|
|
31
31
|
* `APEX_CONSOLE_URL` when set, else the API's host with `app.` read as `admin.` — the hosted service's
|
|
32
32
|
* pair — else `undefined`, and the command asks for `APEX_CONSOLE_URL`.
|
|
33
33
|
*/
|
|
@@ -116,19 +116,19 @@ export const authorizeUrl = (webBase, request) => {
|
|
|
116
116
|
return `${normaliseUrl(webBase)}/cli/authorize?${query.toString()}`;
|
|
117
117
|
};
|
|
118
118
|
/**
|
|
119
|
-
* Where the loopback callback sends the browser once the code is redeemed (
|
|
119
|
+
* Where the loopback callback sends the browser once the code is redeemed (api ADR-0075): the
|
|
120
120
|
* web app's bare `/cli/done`, so the tab ends on our domain rather than on this process's `127.0.0.1`.
|
|
121
121
|
* The page is copy only and confirms nothing server-side, so it carries only the outcome and the purpose
|
|
122
122
|
* — never the code, the state or anything else the callback held.
|
|
123
123
|
*/
|
|
124
124
|
export const doneUrl = (webBase, outcome, purpose) => `${normaliseUrl(webBase)}/cli/done?${new URLSearchParams({ outcome, purpose }).toString()}`;
|
|
125
|
-
/** A token's lifetime bounds (
|
|
125
|
+
/** A token's lifetime bounds (api ADR-0064): every token expires, 90 days at most. */
|
|
126
126
|
export const MAX_TOKEN_DAYS = 90;
|
|
127
127
|
/** Repositories one token may be held to. */
|
|
128
128
|
export const MAX_TOKEN_REPOS = 100;
|
|
129
129
|
const REPO = /^[A-Za-z0-9-]+\/[A-Za-z0-9._-]+$/;
|
|
130
130
|
/**
|
|
131
|
-
* What a token may be granted (
|
|
131
|
+
* What a token may be granted (ADR-0038): `resource:read|write`, write implying read. The control plane's
|
|
132
132
|
* `RepoPermission`, in the order a person reads them.
|
|
133
133
|
*/
|
|
134
134
|
export const TOKEN_RESOURCES = ['actions', 'approvals', 'deployments', 'secrets', 'variables', 'environments', 'notifications', 'administration', 'organization'];
|
|
@@ -235,7 +235,7 @@ export const judgeCallback = (params, expectedState) => {
|
|
|
235
235
|
const set = (v) => v !== undefined && v !== '';
|
|
236
236
|
/**
|
|
237
237
|
* Why no browser can open on this machine, in words for the terminal — or `undefined` when one can.
|
|
238
|
-
* ADR-0075's list: an SSH session, a Codespace, a dev container, a container, or Linux with no display.
|
|
238
|
+
* api ADR-0075's list: an SSH session, a Codespace, a dev container, a container, or Linux with no display.
|
|
239
239
|
*/
|
|
240
240
|
export const headlessReason = ({ env, platform, dockerenv }) => {
|
|
241
241
|
if (set(env['SSH_CONNECTION']) || set(env['SSH_TTY']))
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// How big was the fleet, and when was it nothing?
|
|
4
4
|
//
|
|
5
5
|
// The control plane serves runner *lifetimes* — `registeredAt` to `lastSeenAt`,
|
|
6
|
-
// departed runners included (
|
|
6
|
+
// departed runners included (api ADR-0041). Everything below is the
|
|
7
7
|
// pure arithmetic that turns a pile of intervals into the two answers a capacity
|
|
8
8
|
// question actually wants: the peak, and the time at zero. It is here rather
|
|
9
9
|
// than in the control plane so that it can be tested without a server, and so
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
/**
|
|
3
|
-
* Complimentary access from the terminal (
|
|
4
|
-
* ADR-0079, TASK-0199): the grant and installation shapes the admin API
|
|
3
|
+
* Complimentary access from the terminal (api ADR-0056 as amended by
|
|
4
|
+
* api ADR-0079, TASK-0199): the grant and installation shapes the admin API
|
|
5
5
|
* answers with, the checks `apex grant` makes before a round trip — the
|
|
6
6
|
* control plane makes them again — and how each reads as a line.
|
|
7
7
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// The REST shapes for "what got slower" and "what is flaky", mirroring
|
|
3
|
-
//
|
|
3
|
+
// packages/contracts/openapi/openapi.yaml, plus the pure decisions the CLI makes
|
|
4
4
|
// about presenting them.
|
|
5
5
|
/**
|
|
6
6
|
* A duration a person can compare at a glance.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// The REST shapes for a log search and a structured log view, mirroring
|
|
3
|
-
//
|
|
3
|
+
// packages/contracts/openapi/openapi.yaml, plus the pure decisions the CLI makes
|
|
4
4
|
// about presenting them.
|
|
5
5
|
/**
|
|
6
6
|
* `ci.yml #12 · build` — how a result names the job it found.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// The REST shapes for notification routing, mirroring
|
|
3
|
-
//
|
|
3
|
+
// packages/contracts/openapi/openapi.yaml, plus the pure presentation decisions
|
|
4
4
|
// the CLI makes about them.
|
|
5
5
|
//
|
|
6
6
|
// There is no destination URL in any of these types, and that is not an
|
|
7
|
-
// oversight: the control plane never returns one (
|
|
7
|
+
// oversight: the control plane never returns one (api ADR-0029), so
|
|
8
8
|
// a field for it here would be a field that is always empty and would invite
|
|
9
9
|
// somebody to try to fill it.
|
|
10
10
|
export const TRIGGERS = ['broke', 'recovered', 'failed', 'succeeded'];
|
|
@@ -38,7 +38,7 @@ export const lastAttemptLine = (rule) => {
|
|
|
38
38
|
* all is neither, and calling it a rejection points the reader at the
|
|
39
39
|
* destination when the fault is on this side of the socket. It read as Slack
|
|
40
40
|
* turning us away while the real cause was a `TypeError` in our own resolver
|
|
41
|
-
* (
|
|
41
|
+
* (api ADR-0036), which is a day of somebody's time in the wrong
|
|
42
42
|
* logs.
|
|
43
43
|
*/
|
|
44
44
|
export const sendOutcomeLine = (outcome) => {
|
|
@@ -53,7 +53,7 @@ export const sendOutcomeLine = (outcome) => {
|
|
|
53
53
|
*
|
|
54
54
|
* Written out because the default subscription is the thing people misread:
|
|
55
55
|
* "broke, recovered" looks like it might mean *every* failure, and it does not
|
|
56
|
-
* (
|
|
56
|
+
* (api ADR-0028).
|
|
57
57
|
*/
|
|
58
58
|
export const subscriptionLine = (rule) => {
|
|
59
59
|
const transitions = rule.on.filter((t) => t === 'broke' || t === 'recovered');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// The REST shapes for "why did this run" and "how do I run it here", mirroring
|
|
3
|
-
//
|
|
3
|
+
// packages/contracts/openapi/openapi.yaml, plus the pure decisions the CLI makes
|
|
4
4
|
// about presenting them.
|
|
5
5
|
/**
|
|
6
6
|
* The retry note for one job, or `undefined` when it ran first time.
|
package/dist/src/domain/runs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// What the CLI knows about runs and jobs, and the pure decisions it makes
|
|
3
|
-
// about them. The REST shapes come from
|
|
3
|
+
// about them. The REST shapes come from packages/contracts/openapi.
|
|
4
4
|
/** The run's action-policy findings as `apex logs --summary` prints them: where, which job, the policy's sentence. */
|
|
5
5
|
export const policyLines = (run) => (run.policyFindings ?? []).map((f) => `${f.workflowPath}${f.line === undefined || f.line <= 0 ? '' : `:${String(f.line)}`} (job ${f.jobId}): ${f.reason}`);
|
|
6
6
|
/** A run is worth following while anything can still change. */
|
package/dist/src/domain/spend.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
|
-
// The REST shapes for a spend report, mirroring
|
|
2
|
+
// The REST shapes for a spend report, mirroring packages/contracts/openapi/openapi.yaml,
|
|
3
3
|
// plus the pure decisions the CLI makes about presenting money.
|
|
4
4
|
/**
|
|
5
5
|
* Money, at a precision that does not pretend.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// domain layer — see ../../../../docs/conventions/clean-architecture.md §1
|
|
2
2
|
// The REST shapes for the action supply chain, mirroring
|
|
3
|
-
//
|
|
3
|
+
// packages/contracts/openapi/openapi.yaml, plus the pure decisions the CLI makes
|
|
4
4
|
// about presenting them. No judgement lives here: whether a reference is
|
|
5
5
|
// allowed is the control plane's answer, and a second implementation would be
|
|
6
6
|
// a second answer.
|
|
@@ -8,7 +8,7 @@ import { homedir } from 'node:os';
|
|
|
8
8
|
import { delimiter, dirname, join } from 'node:path';
|
|
9
9
|
import { promisify } from 'node:util';
|
|
10
10
|
/**
|
|
11
|
-
* Where `engine` and `runner` publish their binaries on a release tag (
|
|
11
|
+
* Where `engine` and `runner` publish their binaries on a release tag (ADR-0040): one archive per
|
|
12
12
|
* platform and a SHA256SUMS file per command, under the lockstep version.
|
|
13
13
|
*/
|
|
14
14
|
export const DIST_BASE = 'https://apexactions.com/dl';
|
|
@@ -14,7 +14,7 @@ export class HttpError extends Error {
|
|
|
14
14
|
this.status = status;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/** A named Organization's reach, as the API takes it (
|
|
17
|
+
/** A named Organization's reach, as the API takes it (api ADR-0084); nothing for any other scope. */
|
|
18
18
|
/** Dependabot's store is named; the Actions store is the routes' default, so its paths are unchanged (TASK-0394). */
|
|
19
19
|
const appQuery = (target) => (target.app === 'dependabot' ? '?app=dependabot' : '');
|
|
20
20
|
const reachBody = (target, reach) => target.kind === 'org' && target.org !== undefined && reach !== undefined
|
|
@@ -23,13 +23,13 @@ const reachBody = (target, reach) => target.kind === 'org' && target.org !== und
|
|
|
23
23
|
/**
|
|
24
24
|
* Adapter: the control-plane REST API over the platform's `fetch`.
|
|
25
25
|
*
|
|
26
|
-
* The credential is resolved in
|
|
27
|
-
* ADR-0005 amends it (see `credentials.ts`): a customer's `APEX_TOKEN`, then
|
|
26
|
+
* The credential is resolved in api ADR-0064's order as this repo's
|
|
27
|
+
* cli ADR-0005 amends it (see `credentials.ts`): a customer's `APEX_TOKEN`, then
|
|
28
28
|
* the operator's `APEX_ADMIN_TOKEN` — the machine principal, which keeps
|
|
29
29
|
* scripts and boot paths working when GitHub is unreachable (ADR-0006) — then
|
|
30
30
|
* the token `apex login` saved. The environment outranks the file.
|
|
31
31
|
* A customer token reaches the repository routes its permissions cover, in its
|
|
32
|
-
* Account (
|
|
32
|
+
* Account (api ADR-0071); a route it lacks the grant for answers 403
|
|
33
33
|
* `this token needs <permission>`, which this client extends with the edit
|
|
34
34
|
* that would add it.
|
|
35
35
|
*
|
|
@@ -69,7 +69,7 @@ export class HttpControlPlane {
|
|
|
69
69
|
async whoami() {
|
|
70
70
|
return await this.#json('GET', '/api/v1/me');
|
|
71
71
|
}
|
|
72
|
-
// ── the person's tokens: read and revoke, never mint (ADR-0064, amended 2026-09-27) ──
|
|
72
|
+
// ── the person's tokens: read and revoke, never mint (api ADR-0064, amended 2026-09-27) ──
|
|
73
73
|
async listTokens() {
|
|
74
74
|
const body = await this.#json('GET', '/api/v1/me/tokens');
|
|
75
75
|
if (!Array.isArray(body.tokens))
|
|
@@ -235,7 +235,7 @@ export class HttpControlPlane {
|
|
|
235
235
|
* The terms in force and who owes an acceptance, read from `/me`.
|
|
236
236
|
*
|
|
237
237
|
* It rides on `/me` in the API because the remedy is at somebody's next
|
|
238
|
-
* sign-in and that is the call every screen makes (
|
|
238
|
+
* sign-in and that is the call every screen makes (api ADR-0039
|
|
239
239
|
* amendment 1); here it means one request answers both halves.
|
|
240
240
|
*/
|
|
241
241
|
async termsStanding() {
|
|
@@ -247,23 +247,23 @@ export class HttpControlPlane {
|
|
|
247
247
|
return body.accepted;
|
|
248
248
|
}
|
|
249
249
|
async departures() {
|
|
250
|
-
return (await this.#json('GET', '/api/v1/
|
|
250
|
+
return (await this.#json('GET', '/api/v1/office/departures')).departures;
|
|
251
251
|
}
|
|
252
252
|
async purgeDeparture(installationId, apply) {
|
|
253
253
|
return await this.#json('POST', `/api/v1/departures/${installationId}/purge${apply ? '?apply=true' : ''}`);
|
|
254
254
|
}
|
|
255
255
|
async repositoryDepartures() {
|
|
256
|
-
return (await this.#json('GET', '/api/v1/
|
|
256
|
+
return (await this.#json('GET', '/api/v1/office/repository-departures')).departures;
|
|
257
257
|
}
|
|
258
258
|
async purgeRepositoryDeparture(repoId, apply) {
|
|
259
259
|
return await this.#json('POST', `/api/v1/repository-departures/${repoId}/purge${apply ? '?apply=true' : ''}`);
|
|
260
260
|
}
|
|
261
261
|
async accountsBackfill(input, apply) {
|
|
262
|
-
return await this.#json('POST', `/api/v1/
|
|
262
|
+
return await this.#json('POST', `/api/v1/office/accounts/backfill${apply ? '?apply=true' : ''}`, input);
|
|
263
263
|
}
|
|
264
|
-
// ── complimentary access (
|
|
264
|
+
// ── complimentary access (api ADR-0056, api ADR-0079): the admin API's shapes, unchanged ──
|
|
265
265
|
/**
|
|
266
|
-
* Every grant. The list is paged since
|
|
266
|
+
* Every grant. The list is paged since TASK-0241 (api ADR-0081), so this follows `page.nextCursor`
|
|
267
267
|
* to the end; a control plane from before the grammar answers everything and no `page`, which ends the loop at once.
|
|
268
268
|
*/
|
|
269
269
|
async listGrants() {
|
|
@@ -273,7 +273,7 @@ export class HttpControlPlane {
|
|
|
273
273
|
const query = new URLSearchParams({ limit: '200' });
|
|
274
274
|
if (cursor !== undefined)
|
|
275
275
|
query.set('cursor', cursor);
|
|
276
|
-
const body = await this.#json('GET', `/api/v1/
|
|
276
|
+
const body = await this.#json('GET', `/api/v1/office/grants?${query.toString()}`);
|
|
277
277
|
all.push(...body.grants);
|
|
278
278
|
cursor = body.page?.nextCursor;
|
|
279
279
|
if (cursor === undefined)
|
|
@@ -282,22 +282,22 @@ export class HttpControlPlane {
|
|
|
282
282
|
return all;
|
|
283
283
|
}
|
|
284
284
|
async createGrant(body) {
|
|
285
|
-
return await this.#json('POST', '/api/v1/
|
|
285
|
+
return await this.#json('POST', '/api/v1/office/grants', body);
|
|
286
286
|
}
|
|
287
287
|
async updateGrant(id, body) {
|
|
288
|
-
return (await this.#json('PATCH', `/api/v1/
|
|
288
|
+
return (await this.#json('PATCH', `/api/v1/office/grants/${encodeURIComponent(id)}`, body)).grant;
|
|
289
289
|
}
|
|
290
290
|
async revokeGrant(id) {
|
|
291
|
-
return await this.#json('DELETE', `/api/v1/
|
|
291
|
+
return await this.#json('DELETE', `/api/v1/office/grants/${encodeURIComponent(id)}`);
|
|
292
292
|
}
|
|
293
293
|
async listInstallations() {
|
|
294
|
-
return (await this.#json('GET', '/api/v1/
|
|
294
|
+
return (await this.#json('GET', '/api/v1/office/installations')).installations;
|
|
295
295
|
}
|
|
296
296
|
async accountsUnconnected() {
|
|
297
|
-
return (await this.#json('GET', '/api/v1/
|
|
297
|
+
return (await this.#json('GET', '/api/v1/office/accounts/unconnected')).installations;
|
|
298
298
|
}
|
|
299
299
|
async accountsConnect(input) {
|
|
300
|
-
return await this.#json('POST', '/api/v1/
|
|
300
|
+
return await this.#json('POST', '/api/v1/office/accounts/connect', input);
|
|
301
301
|
}
|
|
302
302
|
async actionPolicy(owner) {
|
|
303
303
|
return await this.#json('GET', `/api/v1/orgs/${encodeURIComponent(owner)}/action-policy`);
|
|
@@ -382,7 +382,7 @@ export class HttpControlPlane {
|
|
|
382
382
|
return await this.#json('GET', `/api/v1/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/coexistence/diff?${q.toString()}`);
|
|
383
383
|
}
|
|
384
384
|
/**
|
|
385
|
-
* Every live runner. The list is paged since
|
|
385
|
+
* Every live runner. The list is paged since api ADR-0081, so this follows `page.nextCursor` to the end —
|
|
386
386
|
* `apex runner list` means all of them, and the fleet alone can hold more than one page. A control plane from before
|
|
387
387
|
* the grammar answers everything and no `page`, which ends the loop at once.
|
|
388
388
|
*/
|
|
@@ -434,7 +434,7 @@ export class HttpControlPlane {
|
|
|
434
434
|
return await this.#json('GET', `/api/v1/runs/${encodeURIComponent(runId)}/artifacts`);
|
|
435
435
|
}
|
|
436
436
|
/**
|
|
437
|
-
* Every artifact a repository holds. The list is paged since
|
|
437
|
+
* Every artifact a repository holds. The list is paged since api ADR-0081 (TASK-0228), so this
|
|
438
438
|
* follows `page.nextCursor` to the end; before, the list stopped at 200 without saying so. The total is the API's,
|
|
439
439
|
* over every artifact. A control plane from before the grammar answers one page and no `page`, which ends the loop.
|
|
440
440
|
*/
|
|
@@ -38,7 +38,7 @@ export class InsecureCredentialsError extends NotConfiguredError {
|
|
|
38
38
|
export const credentialsPath = (env = process.env) => join(set(env['XDG_CONFIG_HOME']) ? env['XDG_CONFIG_HOME'] : join(homedir(), '.config'), 'apex', 'credentials');
|
|
39
39
|
/**
|
|
40
40
|
* `$XDG_CONFIG_HOME/apex/console-credentials`: where `apex login --console` keeps the operator token
|
|
41
|
-
* (
|
|
41
|
+
* (api ADR-0079) — beside the Account login, never in place of it.
|
|
42
42
|
*/
|
|
43
43
|
export const consoleCredentialsPath = (env = process.env) => join(dirname(credentialsPath(env)), 'console-credentials');
|
|
44
44
|
/**
|
|
@@ -105,8 +105,8 @@ export class FileCredentialStore {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
|
-
* Which credential the CLI uses (
|
|
109
|
-
*
|
|
108
|
+
* Which credential the CLI uses (api ADR-0064's order, amended by
|
|
109
|
+
* cli ADR-0005 — the environment outranks the file):
|
|
110
110
|
*
|
|
111
111
|
* 1. `APEX_TOKEN` — a dashboard token, for CI and scripts;
|
|
112
112
|
* 2. `APEX_ADMIN_TOKEN` — the operator's. An explicitly exported variable is
|
|
@@ -162,7 +162,7 @@ export const resolveCredential = (env = process.env, store = new FileCredentialS
|
|
|
162
162
|
};
|
|
163
163
|
/**
|
|
164
164
|
* Which credential an operator command — `apex grant`, `apex tenant installations` — uses (control-plane
|
|
165
|
-
* ADR-0079): the operator token `apex login --console` saved, when it is private, unexpired and was minted by
|
|
165
|
+
* api ADR-0079): the operator token `apex login --console` saved, when it is private, unexpired and was minted by
|
|
166
166
|
* the control plane `APEX_API_URL` names; otherwise the ordinary order ({@link resolveCredential}), in which
|
|
167
167
|
* the machine token can read grants and is refused every write. An expired operator token is skipped, and
|
|
168
168
|
* the refusal that follows says to sign in again.
|
|
@@ -4,7 +4,7 @@ import { hostname, platform, release, type } from 'node:os';
|
|
|
4
4
|
import { headlessReason, isDevicePollError, normaliseUrl } from '../domain/credentials.js';
|
|
5
5
|
import { redeemedFrom } from './loopback-login.js';
|
|
6
6
|
/**
|
|
7
|
-
* The device flow's adapters (
|
|
7
|
+
* The device flow's adapters (api ADR-0075): the two calls, what this machine says about
|
|
8
8
|
* itself, and whether a browser can open here at all.
|
|
9
9
|
*/
|
|
10
10
|
/** Why no browser can open here, read from this process's environment; `undefined` when one can. */
|
|
@@ -5,7 +5,7 @@ import { createServer } from 'node:http';
|
|
|
5
5
|
import { judgeCallback, normaliseUrl } from '../domain/credentials.js';
|
|
6
6
|
/**
|
|
7
7
|
* Adapters for `apex login`: the PKCE pair, the loopback listener, the
|
|
8
|
-
* browser, and the code exchange (RFC 8252, RFC 7636;
|
|
8
|
+
* browser, and the code exchange (RFC 8252, RFC 7636; api ADR-0064).
|
|
9
9
|
*/
|
|
10
10
|
/** base64url(sha256(verifier)) — RFC 7636's S256. */
|
|
11
11
|
export const s256 = (verifier) => createHash('sha256').update(verifier, 'ascii').digest('base64url');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apex-actions/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"//files": "Without this npm ships src, test and the CI config alongside the binary a user installs.",
|
|
6
6
|
"files": [
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/Apex-Actions/
|
|
16
|
+
"url": "git+https://github.com/Apex-Actions/apex-actions.git",
|
|
17
|
+
"directory": "apps/cli"
|
|
17
18
|
},
|
|
18
19
|
"description": "apex — developer and operator CLI for Apex Actions",
|
|
19
20
|
"type": "module",
|
|
@@ -33,16 +34,11 @@
|
|
|
33
34
|
"@types/node": "^24.0.0",
|
|
34
35
|
"dependency-cruiser": "^16.10.0",
|
|
35
36
|
"eslint": "^9.28.0",
|
|
36
|
-
"husky": "^9.1.7",
|
|
37
|
-
"lint-staged": "^16.1.0",
|
|
38
37
|
"tsx": "^4.19.4",
|
|
39
38
|
"typescript": "^5.8.3",
|
|
40
39
|
"typescript-eslint": "^8.67.0",
|
|
41
40
|
"vitest": "^3.2.0"
|
|
42
41
|
},
|
|
43
|
-
"lint-staged": {
|
|
44
|
-
"*.ts": "eslint --max-warnings=0"
|
|
45
|
-
},
|
|
46
42
|
"scripts": {
|
|
47
43
|
"build": "tsc -p tsconfig.build.json",
|
|
48
44
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|