@knowttl/gitea-axi 0.1.3 → 0.1.4
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 +9 -6
- package/dist/cli.js +2 -1
- package/dist/local-workflows.js +15 -18
- package/dist/main.js +18 -15
- package/dist/output.js +10 -6
- package/dist/registry.js +2 -1
- package/dist/search.js +1 -1
- package/dist/writes.js +4 -4
- package/generated/help.txt +384 -0
- package/generated/host-inventory.json +512 -0
- package/package.json +1 -1
- package/generated/capabilities.json +0 -964
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Agent-facing Gitea CLI with private credential profiles, read-only by default.
|
|
|
8
8
|
|
|
9
9
|
Agents need bounded, predictable output and explicit write controls instead of guessed API calls and unbounded responses.
|
|
10
10
|
gitea-axi offers a bounded repository dashboard, detailed reads, Actions task summaries, bounded search, repository, release, issue, label and pull request mutations, and classified raw API requests.
|
|
11
|
-
Output
|
|
11
|
+
Output defaults to compact TOON, with optional JSON and structured errors.
|
|
12
12
|
Reads are bounded, and writes require an exact operation grant, a preview and a matching confirmation.
|
|
13
13
|
See [Limitations](#limitations) for verification scope.
|
|
14
14
|
|
|
@@ -107,7 +107,9 @@ Encoded route separators and traversal remain refused.
|
|
|
107
107
|
Anonymous reads require an explicit origin; repository selection follows the rules below.
|
|
108
108
|
Anonymous HTTP reads require `--allow-insecure-http` for that selected origin.
|
|
109
109
|
The tool follows no redirects and never imports tea or Git credentials.
|
|
110
|
-
Output uses TOON
|
|
110
|
+
Output uses TOON by default; `--json` selects JSON for results, structured errors and help.
|
|
111
|
+
Version aliases remain plain text when combined with `--json`.
|
|
112
|
+
Structured errors use stdout, with exit 1 for operational failures and exit 2 for usage errors.
|
|
111
113
|
Business reads share a 30-second deadline, except check watches use their explicit timeout; each request has a 2,000,000-byte response limit.
|
|
112
114
|
The check-watch timeout includes initial repository reads; polling cannot extend the client's deadline.
|
|
113
115
|
`repo view` instead gives each of its server version, specification and repository requests a separate 30-second timeout.
|
|
@@ -197,8 +199,7 @@ Boolean flags accept explicit `=true` and `=false`, including disabling issues o
|
|
|
197
199
|
Fork takes a source through `--repo` or a positional OWNER/NAME and optionally `--org` for the destination.
|
|
198
200
|
Verification checks the destination identity, requested fields and fork parent.
|
|
199
201
|
Internal visibility refuses before mutation.
|
|
200
|
-
|
|
201
|
-
Repository creation `--push` and `--remote` require `--source`.
|
|
202
|
+
See [Local Git workflows and owned stacks](#local-git-workflows-and-owned-stacks) for supported workspace flag combinations and explicit `--workspace` selection.
|
|
202
203
|
|
|
203
204
|
### Releases
|
|
204
205
|
|
|
@@ -329,7 +330,7 @@ Config files and file SecretRefs must be regular files; symlinks are refused.
|
|
|
329
330
|
See [the credential schema](axi-credential.schema.json) for profile fields and env/file SecretRef shapes.
|
|
330
331
|
`origin` and `tokenOrigin` must identify the same canonical origin.
|
|
331
332
|
Inline tokens and secret arguments are refused.
|
|
332
|
-
Resolved credentials are redacted from response and error string values before TOON serialization, including their JSON-escaped and URL-encoded forms.
|
|
333
|
+
Resolved credentials are redacted from response and error string values before TOON or JSON serialization, including their JSON-escaped and URL-encoded forms.
|
|
333
334
|
Redaction also masks recognized secret fields, secret query values, URL credentials, JWTs, private keys and account keys.
|
|
334
335
|
Generated confirmation IDs and fixed confirmation/reconciliation instructions remain intact when they overlap a protected value; payloads and upstream response text remain subject to redaction.
|
|
335
336
|
The complete environment profile uses `GITEA_AXI_ORIGIN`, `GITEA_AXI_TOKEN_ORIGIN` and a JSON SecretRef in `GITEA_AXI_TOKEN`.
|
|
@@ -348,7 +349,9 @@ Local workflows require an explicit absolute `--workspace` and use native Git ar
|
|
|
348
349
|
Select the native repository root, or an existing parent directory for cloning.
|
|
349
350
|
`repo clone OWNER/NAME` clones into a new child directory of the selected parent and checks out the previewed default commit.
|
|
350
351
|
`repo create --source /absolute/repo --push --remote NAME` publishes the current source branch with its complete history.
|
|
351
|
-
|
|
352
|
+
Workspace repository creation requires both `--source` and `--push` and refuses `--clone`.
|
|
353
|
+
`repo fork --clone` clones the verified fork; `--remote NAME` selects the clone's origin name.
|
|
354
|
+
Workspace forks require `--clone` and refuse `--source` and `--push`.
|
|
352
355
|
Existing remotes are never overwritten.
|
|
353
356
|
Use a repository-local Git user name and email for workflows that create commits.
|
|
354
357
|
Git credential helpers, hooks, transport rewrites and redirects are disabled or refused.
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
const args = process.argv.slice(2);
|
|
4
|
-
|
|
4
|
+
const versionArgs = args.filter(arg => !['--json', '--json=true', '--json=false'].includes(arg));
|
|
5
|
+
if (args.length <= 2 && versionArgs.length === 1 && ['--version', '-v', '-V'].includes(versionArgs[0] ?? '')) {
|
|
5
6
|
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
6
7
|
console.log(pkg.version);
|
|
7
8
|
}
|
package/dist/local-workflows.js
CHANGED
|
@@ -20,6 +20,10 @@ export function validateLocal(operation, options) {
|
|
|
20
20
|
if (process.env.GITEA_AXI_HOST_WRITE_OPERATIONS !== undefined || process.env.GITEA_AXI_CREDENTIAL_REVISION !== undefined) {
|
|
21
21
|
throw new CliError('READ_ONLY', 'Workspace workflows are standalone only');
|
|
22
22
|
}
|
|
23
|
+
if (operation === 'repo create' && (!options.source || !options.push || options.clone))
|
|
24
|
+
usage('Workspace repository creation requires --source and --push without --clone');
|
|
25
|
+
if (operation === 'repo fork' && (!options.clone || options.source !== undefined || options.push))
|
|
26
|
+
usage('Workspace forks require --clone without --source or --push');
|
|
23
27
|
if (operation.startsWith('stack ') && (options.repo || process.env.GH_REPO))
|
|
24
28
|
usage('Stack commands reject -R/--repo and GH_REPO; use the explicit local workspace');
|
|
25
29
|
if (typeof options.workspace !== 'string' || !isAbsolute(options.workspace))
|
|
@@ -68,10 +72,7 @@ export async function localWorkflow(client, identity, operation, args, options)
|
|
|
68
72
|
if (operation === 'repo create' || operation === 'repo fork') {
|
|
69
73
|
if (args.length > 1)
|
|
70
74
|
usage('Select at most one repository');
|
|
71
|
-
|
|
72
|
-
usage('--push and repository creation --remote require --source');
|
|
73
|
-
if (options.source && options.clone)
|
|
74
|
-
usage('Choose --source or --clone');
|
|
75
|
+
const creating = operation === 'repo create';
|
|
75
76
|
const apiOptions = { ...options };
|
|
76
77
|
for (const key of ['source', 'push', 'remote', 'clone', 'workspace'])
|
|
77
78
|
delete apiOptions[key];
|
|
@@ -85,39 +86,35 @@ export async function localWorkflow(client, identity, operation, args, options)
|
|
|
85
86
|
if (!/^[A-Za-z0-9_-]+$/.test(remote))
|
|
86
87
|
usage('Select a simple remote name');
|
|
87
88
|
const destination = join(workspace, destinationName.split('/')[1]);
|
|
88
|
-
if (
|
|
89
|
-
const sourcePath = String(options.source
|
|
89
|
+
if (creating) {
|
|
90
|
+
const sourcePath = String(options.source);
|
|
90
91
|
if (!isAbsolute(sourcePath))
|
|
91
92
|
usage('--source requires an absolute native repository root');
|
|
92
93
|
source = await new Git(await realpath(sourcePath), identity.origin, identity.token).workspace();
|
|
93
94
|
if (source.cwd !== workspace)
|
|
94
95
|
usage('--source must equal the explicit --workspace');
|
|
95
96
|
await source.clean();
|
|
96
|
-
if (
|
|
97
|
+
if (!await source.run(['symbolic-ref', '--quiet', '--short', 'HEAD'], [0, 1]))
|
|
97
98
|
usage('Publishing requires a named source branch');
|
|
98
99
|
if ((await source.run(['remote'])).split('\n').includes(remote))
|
|
99
100
|
usage('Selected remote already exists; choose another --remote');
|
|
100
101
|
preconditions = await source.snapshot();
|
|
101
102
|
}
|
|
102
|
-
else
|
|
103
|
+
else {
|
|
103
104
|
if (existsSync(destination))
|
|
104
105
|
usage('Clone destination already exists');
|
|
105
106
|
preconditions = { parent: workspace, contents: await readdir(workspace) };
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
-
usage('Workspace composite requires --source or --clone');
|
|
109
|
-
return localEffect(client, identity, 'composite', options, git, preconditions, { source: source?.cwd, remote, url, clone: Boolean(options.clone), push: Boolean(options.push) }, async () => {
|
|
108
|
+
return localEffect(client, identity, 'composite', options, git, preconditions, { source: source?.cwd, remote, url, clone: !creating, push: creating }, async () => {
|
|
110
109
|
if (source) {
|
|
111
110
|
if (!same(await source.snapshot(), preconditions))
|
|
112
111
|
throw new CliError('CONFIRMATION_MISMATCH', 'Source changed after API creation; repository created, local publication stopped');
|
|
113
112
|
await source.run(['remote', 'add', remote, url]);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
return { workspace, remote, published: Boolean(options.push), head: await source.tip() };
|
|
113
|
+
const branch = await source.run(['symbolic-ref', '--short', 'HEAD']);
|
|
114
|
+
await source.run(['push', '--', url, 'refs/heads/' + branch + ':refs/heads/' + branch]);
|
|
115
|
+
if (!(await source.run(['ls-remote', '--', url, 'refs/heads/' + branch])).startsWith(await source.tip()))
|
|
116
|
+
throw new CliError('READBACK_MISMATCH', 'Published head was not confirmed');
|
|
117
|
+
return { workspace, remote, published: true, head: await source.tip() };
|
|
121
118
|
}
|
|
122
119
|
if (existsSync(destination))
|
|
123
120
|
throw new CliError('CONFIRMATION_MISMATCH', 'Clone destination appeared; API repository was created');
|
package/dist/main.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
2
|
import { isAbsolute } from 'node:path';
|
|
3
|
-
import { encode } from '@toon-format/toon';
|
|
4
3
|
import { commands } from './registry.js';
|
|
5
4
|
import { CliError } from './error.js';
|
|
6
5
|
import { readRepository } from './repository.js';
|
|
7
|
-
import { configPath, credentials, profileList,
|
|
6
|
+
import { configPath, credentials, profileList, registerSecret } from './credentials.js';
|
|
8
7
|
import { Client } from './transport.js';
|
|
9
8
|
import { collect, object, rawPath, rawRead, read } from './api.js';
|
|
10
9
|
import { boundedList, issueFields, listIssues, listOptions, positive, prFields, repository, selectedFields, view, watchChecks } from './reads.js';
|
|
@@ -19,7 +18,7 @@ import { actionValues, valuePlan } from './action-values.js';
|
|
|
19
18
|
import { assetPath, attachToPlan, download, uploadPlan } from './attachments.js';
|
|
20
19
|
import { isLocal, localWorkflow, validateLocal } from './local-workflows.js';
|
|
21
20
|
import { capabilityOutput, commandGap } from './catalogue.js';
|
|
22
|
-
import { diffOutput, maxOutputChars } from './output.js';
|
|
21
|
+
import { diffOutput, maxOutputChars, render } from './output.js';
|
|
23
22
|
async function input(path) {
|
|
24
23
|
if (path !== '-')
|
|
25
24
|
return readPrivateFile(path, true, 1_000_000);
|
|
@@ -33,6 +32,8 @@ async function input(path) {
|
|
|
33
32
|
return body;
|
|
34
33
|
}
|
|
35
34
|
export async function main(args) {
|
|
35
|
+
let json = args.some(arg => arg === '--json' || arg === '--json=true');
|
|
36
|
+
const print = (value) => console.log(render(value, json));
|
|
36
37
|
let operation = args[0] && !args[0].startsWith('-') ? args[0] : 'dashboard';
|
|
37
38
|
try {
|
|
38
39
|
for (const arg of args) {
|
|
@@ -47,8 +48,9 @@ export async function main(args) {
|
|
|
47
48
|
[...commands].sort((a, b) => b.path.length - a.path.length).find(command => command.path.every((part, index) => args[index] === part));
|
|
48
49
|
if (!command) {
|
|
49
50
|
const children = commands.filter(command => command.path[0] === args[0]);
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const helpArgs = args.filter(arg => arg !== '--json');
|
|
52
|
+
if (children.length && helpArgs.length === 2 && helpArgs[1] === '--help' && args.length <= 3) {
|
|
53
|
+
print({ command: 'gitea-axi ' + operation, commands: children.map(command => 'gitea-axi ' + command.path.join(' ')) });
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
throw new CliError('USAGE', children.length ? 'Choose a command: ' + children.map(command => 'gitea-axi ' + command.path.join(' ')).join(', ') : 'Unknown command; use gitea-axi --help', 2);
|
|
@@ -81,23 +83,24 @@ export async function main(args) {
|
|
|
81
83
|
throw new CliError('USAGE', 'Invalid arguments; valid flags: ' + Object.keys(command.flags).map(f => '--' + f).join(', '), 2);
|
|
82
84
|
}
|
|
83
85
|
const { values, positionals } = parsed;
|
|
86
|
+
json = Boolean(values.json);
|
|
84
87
|
const names = parsed.tokens.flatMap(token => token.kind === 'option' && !command.flags[token.name]?.multiple ? [token.name] : []);
|
|
85
88
|
if (new Set(names).size !== names.length) {
|
|
86
89
|
throw new CliError('USAGE', 'Choose each flag only once; --repo requires exactly one selector', 2);
|
|
87
90
|
}
|
|
88
91
|
if (values.help) {
|
|
89
|
-
|
|
92
|
+
print({ bin: process.argv[1], description: command.description, command: 'gitea-axi ' + operation,
|
|
90
93
|
flags: Object.entries(command.flags).map(([name, flag]) => ({ flag: '--' + name, ...flag })),
|
|
91
94
|
...(operation === 'dashboard' ? { commands: commands.map(command => command.path.join(' ')) } : {}),
|
|
92
95
|
examples: operation === 'api' ? ['gitea-axi api GET repos/OWNER/NAME', 'gitea-axi api version'] :
|
|
93
|
-
[`gitea-axi ${operation} --help`, `gitea-axi ${operation} --profile <name>`] })
|
|
96
|
+
[`gitea-axi ${operation} --help`, `gitea-axi ${operation} --profile <name>`] });
|
|
94
97
|
return;
|
|
95
98
|
}
|
|
96
99
|
const options = values;
|
|
97
100
|
if (operation === 'capabilities') {
|
|
98
101
|
if (positionals.length > 1)
|
|
99
102
|
throw new CliError('USAGE', 'Use capabilities [C001-C124]', 2);
|
|
100
|
-
|
|
103
|
+
print(capabilityOutput(commands, prFields, positionals[0]));
|
|
101
104
|
return;
|
|
102
105
|
}
|
|
103
106
|
if (command.gap)
|
|
@@ -119,7 +122,7 @@ export async function main(args) {
|
|
|
119
122
|
...(typeof options.hostname === 'string' ? { hostname: options.hostname } : {}),
|
|
120
123
|
});
|
|
121
124
|
const output = await localWorkflow(new Client(identity.origin, identity.token, 300_000), identity, operation, positionals, options);
|
|
122
|
-
const rendered =
|
|
125
|
+
const rendered = render(output, json);
|
|
123
126
|
if (rendered.length > maxOutputChars)
|
|
124
127
|
throw new CliError('OUTPUT_LIMIT', 'Rendered output exceeds 100000 characters');
|
|
125
128
|
console.log(rendered);
|
|
@@ -262,11 +265,11 @@ export async function main(args) {
|
|
|
262
265
|
if (options.config && (typeof options.config !== 'string' || !isAbsolute(options.config)))
|
|
263
266
|
throw new CliError('USAGE', '--config must be absolute', 2);
|
|
264
267
|
if (operation === 'config path') {
|
|
265
|
-
|
|
268
|
+
print({ path: configPath(options.config) });
|
|
266
269
|
return;
|
|
267
270
|
}
|
|
268
271
|
if (operation === 'profile list') {
|
|
269
|
-
|
|
272
|
+
print(await profileList(options.config));
|
|
270
273
|
return;
|
|
271
274
|
}
|
|
272
275
|
const identity = await credentials(typeof values.origin === 'string' ? values.origin : undefined, Boolean(values['allow-insecure-http']), {
|
|
@@ -284,7 +287,7 @@ export async function main(args) {
|
|
|
284
287
|
else if (searching)
|
|
285
288
|
output = await search(client, path[1], positionals[0], options);
|
|
286
289
|
else if (operation === 'write reconcile')
|
|
287
|
-
output = await reconcile(client, positionals[0]);
|
|
290
|
+
output = await reconcile(client, positionals[0], json);
|
|
288
291
|
else if (operation === 'api')
|
|
289
292
|
output = method === 'GET' ? await rawRead(client, apiPath) :
|
|
290
293
|
await write(client, identity, await rawWritePlan(client, method, apiPath, payload), options);
|
|
@@ -360,17 +363,17 @@ export async function main(args) {
|
|
|
360
363
|
else if (operation === 'pr diff') {
|
|
361
364
|
const response = await client.request(base + '/pulls/' + positionals[0] + '.diff', true);
|
|
362
365
|
const text = String(response.data);
|
|
363
|
-
output = diffOutput(text, Boolean(options.full));
|
|
366
|
+
output = diffOutput(text, Boolean(options.full), json);
|
|
364
367
|
}
|
|
365
368
|
}
|
|
366
|
-
const rendered =
|
|
369
|
+
const rendered = render(output, json);
|
|
367
370
|
if (rendered.length > maxOutputChars)
|
|
368
371
|
throw new CliError('OUTPUT_LIMIT', 'Rendered output exceeds 100000 characters; select fewer fields or a lower --limit');
|
|
369
372
|
console.log(rendered);
|
|
370
373
|
}
|
|
371
374
|
catch (error) {
|
|
372
375
|
const failure = error instanceof CliError ? error : new CliError('INTERNAL_ERROR', 'Unable to read the repository');
|
|
373
|
-
|
|
376
|
+
print({ error: { code: failure.code, operation, reason: failure.message }, help: commands.some(command => command.path.join(' ') === operation || command.path[0] === operation) ? 'gitea-axi ' + operation + ' --help' : 'gitea-axi --help' });
|
|
374
377
|
process.exitCode = failure.exitCode;
|
|
375
378
|
}
|
|
376
379
|
}
|
package/dist/output.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { encode } from '@toon-format/toon';
|
|
2
2
|
import { redact } from './credentials.js';
|
|
3
3
|
export const maxOutputChars = 100_000;
|
|
4
|
+
export function render(value, json = false) {
|
|
5
|
+
const safe = redact(value);
|
|
6
|
+
return json ? JSON.stringify(safe) : encode(safe);
|
|
7
|
+
}
|
|
4
8
|
// Reserve the newline added by console.log.
|
|
5
|
-
const fits = (value) =>
|
|
9
|
+
const fits = (value, json) => render(value, json).length < maxOutputChars;
|
|
6
10
|
function summary(value) {
|
|
7
11
|
if (Array.isArray(value))
|
|
8
12
|
return { summary: true, total_items: value.length };
|
|
@@ -13,12 +17,12 @@ function summary(value) {
|
|
|
13
17
|
}
|
|
14
18
|
return typeof value === 'string' ? { summary: true, total_chars: value.length } : value;
|
|
15
19
|
}
|
|
16
|
-
export function writeOutput(value) {
|
|
20
|
+
export function writeOutput(value, json = false) {
|
|
17
21
|
const displayed = { ...value };
|
|
18
22
|
const truncated = [];
|
|
19
23
|
// Keep requested effects complete; project only their verification context.
|
|
20
24
|
for (const field of ['preconditions', 'observations', 'results']) {
|
|
21
|
-
if (fits(displayed))
|
|
25
|
+
if (fits(displayed, json))
|
|
22
26
|
return displayed;
|
|
23
27
|
if (!(field in value))
|
|
24
28
|
continue;
|
|
@@ -39,15 +43,15 @@ export function writeOutput(value) {
|
|
|
39
43
|
}
|
|
40
44
|
return displayed;
|
|
41
45
|
}
|
|
42
|
-
export function diffOutput(text, full) {
|
|
46
|
+
export function diffOutput(text, full, json = false) {
|
|
43
47
|
const candidate = (length) => ({ diff: text.slice(0, length), chars: text.length, truncated: text.length > length,
|
|
44
48
|
...(text.length > length ? { help: full ? 'Diff is bounded to the rendered output ceiling' : 'Use --full for a larger bounded diff' } : {}) });
|
|
45
49
|
let low = 0, high = Math.min(text.length, full ? maxOutputChars : 10_000);
|
|
46
|
-
if (fits(candidate(high)))
|
|
50
|
+
if (fits(candidate(high), json))
|
|
47
51
|
return candidate(high);
|
|
48
52
|
while (low < high) {
|
|
49
53
|
const middle = Math.ceil((low + high) / 2);
|
|
50
|
-
if (fits(candidate(middle)))
|
|
54
|
+
if (fits(candidate(middle), json))
|
|
51
55
|
low = middle;
|
|
52
56
|
else
|
|
53
57
|
high = middle - 1;
|
package/dist/registry.js
CHANGED
|
@@ -22,6 +22,7 @@ export const command = {
|
|
|
22
22
|
};
|
|
23
23
|
export const limits = { timeoutMs: 30_000, responseBytes: 2_000_000 };
|
|
24
24
|
const globalFlags = {
|
|
25
|
+
json: { type: 'boolean', description: 'Emit JSON instead of TOON; help follows this format, version remains plain text' },
|
|
25
26
|
origin: command.flags.origin, 'allow-insecure-http': command.flags['allow-insecure-http'], help: command.flags.help,
|
|
26
27
|
config: { type: 'string', description: 'Absolute configuration path' },
|
|
27
28
|
profile: { type: 'string', description: 'Select one file profile' },
|
|
@@ -144,7 +145,7 @@ for (const command of commands) {
|
|
|
144
145
|
if (['repo create', 'repo fork'].includes(command.path.join(' ')))
|
|
145
146
|
Object.assign(command.flags, workspace);
|
|
146
147
|
}
|
|
147
|
-
commands.push({ path: ['capabilities'], description: 'Inspect all baseline command IDs, flags, globals and PR fields without claiming full parity', flags: { help: globalFlags.help }, effect: 'local-read' });
|
|
148
|
+
commands.push({ path: ['capabilities'], description: 'Inspect all baseline command IDs, flags, globals and PR fields without claiming full parity', flags: { help: globalFlags.help, json: globalFlags.json }, effect: 'local-read' });
|
|
148
149
|
const refusedFlags = [
|
|
149
150
|
['repo create', ['internal']],
|
|
150
151
|
['release create', ['latest', 'discussion-category']],
|
package/dist/search.js
CHANGED
|
@@ -9,7 +9,7 @@ export function validateSearch(kind, query, options) {
|
|
|
9
9
|
const allowed = kind === 'issues' || kind === 'prs' ? ['owner', 'state', 'label', 'assignee', 'author', 'sort', 'order', 'limit', 'fields', ...(kind === 'prs' ? ['draft'] : [])] :
|
|
10
10
|
kind === 'repos' ? ['owner', 'sort', 'order', 'limit', 'language', 'stars', 'fields'] : ['author', 'sort', 'order', 'limit', 'revision', 'fields'];
|
|
11
11
|
for (const key of Object.keys(options)) {
|
|
12
|
-
if (!['origin', 'allow-insecure-http', 'config', 'profile', 'hostname', 'repo'].includes(key) && !allowed.includes(key))
|
|
12
|
+
if (!['origin', 'allow-insecure-http', 'config', 'profile', 'hostname', 'repo', 'json'].includes(key) && !allowed.includes(key))
|
|
13
13
|
throw new CliError('CAPABILITY_GAP', '--' + key + ' is not evidenced for search ' + kind);
|
|
14
14
|
}
|
|
15
15
|
positive(options.limit, 1000, 1000);
|
package/dist/writes.js
CHANGED
|
@@ -46,7 +46,7 @@ async function key(root) {
|
|
|
46
46
|
}
|
|
47
47
|
return readPrivateFile(path, true);
|
|
48
48
|
}
|
|
49
|
-
export async function reconcile(client, id) {
|
|
49
|
+
export async function reconcile(client, id, json = false) {
|
|
50
50
|
if (!/^[a-f0-9-]{36}$/.test(id))
|
|
51
51
|
usage('Select the exact confirmation ID');
|
|
52
52
|
const receipt = JSON.parse(await readPrivateFile(join(directory(), id + '.json'), true));
|
|
@@ -55,7 +55,7 @@ export async function reconcile(client, id) {
|
|
|
55
55
|
const observations = await observe(client, receipt.reads);
|
|
56
56
|
controlText(id);
|
|
57
57
|
return writeOutput({ confirmation: id, operations: receipt.operations, states: receipt.states, observations,
|
|
58
|
-
help: 'Inspect the exact identity and requested fields in these reads before authorizing any new attempt' });
|
|
58
|
+
help: 'Inspect the exact identity and requested fields in these reads before authorizing any new attempt' }, json);
|
|
59
59
|
}
|
|
60
60
|
async function observe(client, paths) {
|
|
61
61
|
const observations = [];
|
|
@@ -270,7 +270,7 @@ export async function write(client, identity, plan, options, executeLocal) {
|
|
|
270
270
|
...(plan.steps.every(step => step.local) ? { localTarget: plan.target, localGuards: guards.map(path => path.slice(root.length + 1, -8)) } : {}) };
|
|
271
271
|
await writePrivateFile(join(root, id + '.json'), JSON.stringify(receipt));
|
|
272
272
|
return writeOutput({ confirmation: id, origin: client.origin, target: plan.target, operations, steps: plan.steps.map(({ upload, ...step }) => ({ ...step, ...(plan.sensitive && step.payload !== undefined ? { payload: '[REDACTED]' } : {}), ...(upload ? { upload: { name: upload.name, type: upload.type, size: upload.size, sha256: upload.sha256 } } : {}) })), preconditions: plan.preconditions,
|
|
273
|
-
...(plan.notes ? { notes: plan.notes } : {}), help: controlText('Repeat the same command with --confirm ' + id + ' instead of --preview') });
|
|
273
|
+
...(plan.notes ? { notes: plan.notes } : {}), help: controlText('Repeat the same command with --confirm ' + id + ' instead of --preview') }, Boolean(options.json));
|
|
274
274
|
}
|
|
275
275
|
const id = String(options.confirm);
|
|
276
276
|
if (!/^[a-f0-9-]{36}$/.test(id))
|
|
@@ -429,7 +429,7 @@ export async function write(client, identity, plan, options, executeLocal) {
|
|
|
429
429
|
recovered.states = recovered.states.map(state => state === 'completed' ? state : 'recovered');
|
|
430
430
|
await writePrivateFile(join(root, previous + '.json'), JSON.stringify(recovered));
|
|
431
431
|
}
|
|
432
|
-
return writeOutput({ confirmation: id, states: receipt.states, results, observations, ...(plan.notes ? { notes: plan.notes } : {}), reconcile: command(id, options) });
|
|
432
|
+
return writeOutput({ confirmation: id, states: receipt.states, results, observations, ...(plan.notes ? { notes: plan.notes } : {}), reconcile: command(id, options) }, Boolean(options.json));
|
|
433
433
|
}
|
|
434
434
|
export async function rawWritePlan(client, method, path, payload) {
|
|
435
435
|
const route = validatePayload(method, path, payload);
|