@certen.io/cli 0.3.1 → 0.4.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/CHANGELOG.md +55 -0
- package/README.md +24 -0
- package/dist/commands/auth.js +3 -3
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/keys.js +3 -3
- package/dist/commands/keys.js.map +1 -1
- package/dist/config.js +5 -6
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +46 -0
- package/dist/errors.js.map +1 -0
- package/dist/help-json.d.ts +2 -0
- package/dist/help-json.js +51 -0
- package/dist/help-json.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +96 -36
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +31 -1
- package/dist/output.js +106 -1
- package/dist/output.js.map +1 -1
- package/package.json +57 -56
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog — @certen.io/cli
|
|
2
|
+
|
|
3
|
+
## 0.4.0 — `--json` is a machine contract
|
|
4
|
+
|
|
5
|
+
Adds a stable, tested output contract for scripts and AI agents. **Human output is unchanged**: if
|
|
6
|
+
you do not pass `--json`, this release behaves exactly as 0.3.1 did, with one exception noted under
|
|
7
|
+
Breaking.
|
|
8
|
+
|
|
9
|
+
Before this, every failure exited `1` and explained itself in English on stderr. An automated caller
|
|
10
|
+
could not distinguish "you passed a malformed address" from "the gateway is down" without parsing
|
|
11
|
+
prose — and those want opposite responses. One is a bug to fix; the other is worth retrying. For a
|
|
12
|
+
CLI that authorizes cross-chain execution against real funds, guessing wrong is expensive in a way
|
|
13
|
+
that is not recoverable.
|
|
14
|
+
|
|
15
|
+
The full specification is [docs/CLI-CONTRACT.md](../../docs/CLI-CONTRACT.md), enforced by
|
|
16
|
+
`test/conformance.test.ts`, which runs the built binary as a subprocess and checks the real process's
|
|
17
|
+
stdout and exit code.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Global `--json`**, accepted anywhere in the argument list — `certen --json tx status X` and
|
|
22
|
+
`certen tx status X --json` are identical. It is resolved before argument parsing, so it applies
|
|
23
|
+
even to failures that occur while resolving credentials.
|
|
24
|
+
- **One JSON envelope on stdout and nothing else.** `{"ok":true,"data":…}` or
|
|
25
|
+
`{"ok":false,"error":{"code","message","retryable","status?","requestId?"}}`. A command producing
|
|
26
|
+
several payloads emits an array in `data`; one producing none emits `"data":null`. stdout is never
|
|
27
|
+
empty and never carries two concatenated objects. All human-facing text moves to stderr.
|
|
28
|
+
- **Meaningful exit codes:** `0` ok · `1` operation failed · `2` usage error · `3` gateway
|
|
29
|
+
unreachable. `3` guarantees nothing was submitted, so a retry cannot double-execute.
|
|
30
|
+
- **`error.retryable`**, taken from the SDK's own `CertenError.isRetryable`, so the CLI and the SDK
|
|
31
|
+
hand an automated caller the identical retry decision.
|
|
32
|
+
- **`certen --help --json`** returns the entire command tree — every command, argument, flag and exit
|
|
33
|
+
code — in one call, instead of scraping help text once per subcommand.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **Usage errors on subcommands bypassed error handling entirely.** `exitOverride()` is not inherited
|
|
38
|
+
by commander subcommands, so a missing required flag (`certen identity create` with no `--name`)
|
|
39
|
+
called `process.exit(1)` inside commander: no envelope was emitted, stdout stayed empty, and a
|
|
40
|
+
usage error was indistinguishable from a failed request. It is now applied to every command in the
|
|
41
|
+
tree.
|
|
42
|
+
|
|
43
|
+
### Breaking
|
|
44
|
+
|
|
45
|
+
- **"No API key configured" now exits `2` instead of `1`.** It is a usage error: nothing was sent,
|
|
46
|
+
and retrying cannot help. The same applies to a config file with unsafe permissions and to a
|
|
47
|
+
missing keyring backend. Scripts treating any non-zero exit as failure are unaffected; scripts
|
|
48
|
+
testing specifically for `-eq 1` need updating.
|
|
49
|
+
|
|
50
|
+
### Note
|
|
51
|
+
|
|
52
|
+
`output: "json"` in `~/.certen/config.json` is **not** this contract. That setting predates the
|
|
53
|
+
envelope and makes commands print their raw payload — no `ok`, no `error`, no exit-code guarantees.
|
|
54
|
+
It is kept for backward compatibility. Automated callers should pass `--json` explicitly rather than
|
|
55
|
+
depend on a machine's local config, which they cannot see.
|
package/README.md
CHANGED
|
@@ -97,6 +97,30 @@ certen admin audit-log | usage
|
|
|
97
97
|
|
|
98
98
|
Run `certen <group> --help` for the flags on any of them.
|
|
99
99
|
|
|
100
|
+
## Scripting and AI agents: `--json`
|
|
101
|
+
|
|
102
|
+
`--json` turns the CLI into a machine interface. It is a contract, documented in full in
|
|
103
|
+
[docs/CLI-CONTRACT.md](../../docs/CLI-CONTRACT.md) and enforced by a conformance suite.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
certen --json tx status <id>
|
|
107
|
+
# {"ok":true,"data":{"intent_id":"…","status":"completed"}}
|
|
108
|
+
|
|
109
|
+
certen --json portfolio
|
|
110
|
+
# {"ok":false,"error":{"code":"NETWORK_ERROR","message":"connect ECONNREFUSED","retryable":true,"status":0}}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- **Exactly one JSON object on stdout**, nothing else. Every human-facing line goes to stderr.
|
|
114
|
+
- **Exit codes:** `0` ok · `1` operation failed · `2` usage error · `3` gateway unreachable. Branch on
|
|
115
|
+
these instead of parsing text. `3` guarantees nothing was submitted, so a retry cannot
|
|
116
|
+
double-execute.
|
|
117
|
+
- **`error.retryable`** comes from the SDK's own `CertenError.isRetryable`, so the CLI and the SDK
|
|
118
|
+
give an identical retry decision.
|
|
119
|
+
- **`certen --help --json`** returns the entire command tree — every command, flag and exit code — in
|
|
120
|
+
one call.
|
|
121
|
+
|
|
122
|
+
Without `--json`, output is the human table format as before. Do not parse it.
|
|
123
|
+
|
|
100
124
|
## Configuration
|
|
101
125
|
|
|
102
126
|
| | |
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readConfig, writeConfig, setApiKey, clearApiKey, DEFAULT_API_URL } from '../config.js';
|
|
2
|
-
import { printOutput } from '../output.js';
|
|
2
|
+
import { printOutput, human } from '../output.js';
|
|
3
3
|
export function registerAuthCommands(program) {
|
|
4
4
|
const auth = program.command('auth').description('Authentication management');
|
|
5
5
|
auth
|
|
@@ -23,14 +23,14 @@ export function registerAuthCommands(program) {
|
|
|
23
23
|
cfg.api_url = opts.apiUrl;
|
|
24
24
|
writeConfig(cfg);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
human(useKeyring ? 'API key saved to OS keyring' : 'API key saved to ~/.certen/config.json (mode 0600)');
|
|
27
27
|
});
|
|
28
28
|
auth
|
|
29
29
|
.command('logout')
|
|
30
30
|
.description('Remove the saved API key from keyring or config file')
|
|
31
31
|
.action(async () => {
|
|
32
32
|
await clearApiKey();
|
|
33
|
-
|
|
33
|
+
human('API key cleared');
|
|
34
34
|
});
|
|
35
35
|
auth
|
|
36
36
|
.command('status')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/keys.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { printOutput } from '../output.js';
|
|
1
|
+
import { printOutput, human } from '../output.js';
|
|
2
2
|
import { resolvePassphrase, resolveNewPassphrase, PASSPHRASE_ENV_VAR } from '../passphrase.js';
|
|
3
3
|
import { generateKey, listKeys, getKeyInfo, deleteKey, signHash, selfTest, keyPath, KEYS_DIR, } from '../keystore.js';
|
|
4
4
|
/**
|
|
@@ -41,7 +41,7 @@ export function registerKeysCommands(program) {
|
|
|
41
41
|
.action(() => {
|
|
42
42
|
const all = listKeys();
|
|
43
43
|
if (all.length === 0) {
|
|
44
|
-
|
|
44
|
+
human(`(no keys in ${KEYS_DIR})`);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
printOutput(all.map((k) => ({
|
|
@@ -96,7 +96,7 @@ export function registerKeysCommands(program) {
|
|
|
96
96
|
// should not be deletable by an accidental Enter on a y/N prompt.
|
|
97
97
|
const path = keyPath(name);
|
|
98
98
|
deleteKey(name);
|
|
99
|
-
|
|
99
|
+
human(`Deleted ${path}`);
|
|
100
100
|
console.error('If this key was on a key page, it is still on that page — remove it there too.');
|
|
101
101
|
});
|
|
102
102
|
keys
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, statSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
+
import { UsageError } from './errors.js';
|
|
4
5
|
const CONFIG_DIR = join(homedir(), '.certen');
|
|
5
6
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
6
7
|
export function readConfig() {
|
|
@@ -86,19 +87,17 @@ export async function getApiKey() {
|
|
|
86
87
|
return v;
|
|
87
88
|
}
|
|
88
89
|
else {
|
|
89
|
-
|
|
90
|
-
process.exit(1);
|
|
90
|
+
throw new UsageError('storage=keyring requested but `keytar` is not installed; install it or set CERTEN_API_KEY.', 'KEYRING_UNAVAILABLE');
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
if (cfg.api_key) {
|
|
94
94
|
if (!configFileIsSecure()) {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
throw new UsageError('refusing to read ~/.certen/config.json — file permissions are not 0600. '
|
|
96
|
+
+ 'Run `certen auth login` again to fix.', 'CONFIG_PERMISSIONS');
|
|
97
97
|
}
|
|
98
98
|
return cfg.api_key;
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
process.exit(1);
|
|
100
|
+
throw new UsageError('No API key configured. Run "certen auth login --api-key <key>" or set CERTEN_API_KEY.', 'NO_API_KEY');
|
|
102
101
|
}
|
|
103
102
|
export async function setApiKey(apiKey, useKeyring) {
|
|
104
103
|
const prefix = apiKey.substring(0, 12);
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC7F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exit codes and the local error taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* The CLI used to exit `1` for everything — a bad flag, a rejected request, and an unreachable
|
|
5
|
+
* gateway were indistinguishable without reading English off stderr. An automated caller cannot
|
|
6
|
+
* make a retry decision from that: "you passed a malformed address" and "the gateway is down" want
|
|
7
|
+
* opposite responses, and only one of them is worth retrying.
|
|
8
|
+
*
|
|
9
|
+
* These four codes are a contract. See docs/CLI-CONTRACT.md.
|
|
10
|
+
*/
|
|
11
|
+
export declare const EXIT: {
|
|
12
|
+
/** Success. */
|
|
13
|
+
readonly OK: 0;
|
|
14
|
+
/** The request was well-formed and the gateway answered, but the operation did not succeed. */
|
|
15
|
+
readonly FAILED: 1;
|
|
16
|
+
/** The invocation was wrong: unknown command, missing flag, no API key configured. */
|
|
17
|
+
readonly USAGE: 2;
|
|
18
|
+
/** The gateway could not be reached at all. Nothing was submitted. */
|
|
19
|
+
readonly UNREACHABLE: 3;
|
|
20
|
+
};
|
|
21
|
+
export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
|
|
22
|
+
/**
|
|
23
|
+
* An error raised by the CLI itself rather than by the gateway.
|
|
24
|
+
*
|
|
25
|
+
* `code` is machine-readable and lives in the same namespace as the gateway's codes, so a caller
|
|
26
|
+
* branching on `error.code` does not need to know whether the failure was local or remote.
|
|
27
|
+
*/
|
|
28
|
+
export declare class CliError extends Error {
|
|
29
|
+
readonly code: string;
|
|
30
|
+
readonly exitCode: ExitCode;
|
|
31
|
+
readonly retryable: boolean;
|
|
32
|
+
constructor(message: string, code: string, exitCode?: ExitCode, retryable?: boolean);
|
|
33
|
+
}
|
|
34
|
+
/** A wrong invocation: exits 2, never retryable. */
|
|
35
|
+
export declare class UsageError extends CliError {
|
|
36
|
+
constructor(message: string, code?: string);
|
|
37
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exit codes and the local error taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* The CLI used to exit `1` for everything — a bad flag, a rejected request, and an unreachable
|
|
5
|
+
* gateway were indistinguishable without reading English off stderr. An automated caller cannot
|
|
6
|
+
* make a retry decision from that: "you passed a malformed address" and "the gateway is down" want
|
|
7
|
+
* opposite responses, and only one of them is worth retrying.
|
|
8
|
+
*
|
|
9
|
+
* These four codes are a contract. See docs/CLI-CONTRACT.md.
|
|
10
|
+
*/
|
|
11
|
+
export const EXIT = {
|
|
12
|
+
/** Success. */
|
|
13
|
+
OK: 0,
|
|
14
|
+
/** The request was well-formed and the gateway answered, but the operation did not succeed. */
|
|
15
|
+
FAILED: 1,
|
|
16
|
+
/** The invocation was wrong: unknown command, missing flag, no API key configured. */
|
|
17
|
+
USAGE: 2,
|
|
18
|
+
/** The gateway could not be reached at all. Nothing was submitted. */
|
|
19
|
+
UNREACHABLE: 3,
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* An error raised by the CLI itself rather than by the gateway.
|
|
23
|
+
*
|
|
24
|
+
* `code` is machine-readable and lives in the same namespace as the gateway's codes, so a caller
|
|
25
|
+
* branching on `error.code` does not need to know whether the failure was local or remote.
|
|
26
|
+
*/
|
|
27
|
+
export class CliError extends Error {
|
|
28
|
+
code;
|
|
29
|
+
exitCode;
|
|
30
|
+
retryable;
|
|
31
|
+
constructor(message, code, exitCode = EXIT.FAILED, retryable = false) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = 'CliError';
|
|
34
|
+
this.code = code;
|
|
35
|
+
this.exitCode = exitCode;
|
|
36
|
+
this.retryable = retryable;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** A wrong invocation: exits 2, never retryable. */
|
|
40
|
+
export class UsageError extends CliError {
|
|
41
|
+
constructor(message, code = 'USAGE_ERROR') {
|
|
42
|
+
super(message, code, EXIT.USAGE, false);
|
|
43
|
+
this.name = 'UsageError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,eAAe;IACf,EAAE,EAAE,CAAC;IACL,+FAA+F;IAC/F,MAAM,EAAE,CAAC;IACT,sFAAsF;IACtF,KAAK,EAAE,CAAC;IACR,sEAAsE;IACtE,WAAW,EAAE,CAAC;CACN,CAAC;AAIX;;;;;GAKG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACb,QAAQ,CAAW;IACnB,SAAS,CAAU;IAE5B,YAAY,OAAe,EAAE,IAAY,EAAE,WAAqB,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,KAAK;QAC5F,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED,oDAAoD;AACpD,MAAM,OAAO,UAAW,SAAQ,QAAQ;IACtC,YAAY,OAAe,EAAE,IAAI,GAAG,aAAa;QAC/C,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function optionNode(o) {
|
|
2
|
+
return {
|
|
3
|
+
flags: o.flags,
|
|
4
|
+
name: o.attributeName ? o.attributeName() : (o.long ?? o.short ?? o.flags).replace(/^--?/, ''),
|
|
5
|
+
description: o.description ?? '',
|
|
6
|
+
required: Boolean(o.mandatory),
|
|
7
|
+
// `required` on a commander Option means "this flag takes a mandatory value", not "this flag
|
|
8
|
+
// must be present" — that is `mandatory`. Conflating them is an easy and very confusing bug.
|
|
9
|
+
takesValue: Boolean(o.required || o.optional),
|
|
10
|
+
...(o.defaultValue !== undefined ? { default: o.defaultValue } : {}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function argumentsOf(cmd) {
|
|
14
|
+
// `registeredArguments` is commander >= 12; `_args` is the older internal. Fall back rather than
|
|
15
|
+
// throw, so a commander bump degrades the help tree instead of breaking the command.
|
|
16
|
+
const raw = (cmd.registeredArguments
|
|
17
|
+
?? cmd._args
|
|
18
|
+
?? []);
|
|
19
|
+
return raw.map((a) => ({
|
|
20
|
+
name: a.name(),
|
|
21
|
+
required: Boolean(a.required),
|
|
22
|
+
description: a.description ?? '',
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
function walk(cmd, prefix) {
|
|
26
|
+
const path = [...prefix, cmd.name()].filter(Boolean);
|
|
27
|
+
return {
|
|
28
|
+
name: cmd.name(),
|
|
29
|
+
description: cmd.description(),
|
|
30
|
+
path: path.join(' '),
|
|
31
|
+
arguments: argumentsOf(cmd),
|
|
32
|
+
options: cmd.options.map(optionNode),
|
|
33
|
+
commands: cmd.commands.map((c) => walk(c, path)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function commandTree(program, version) {
|
|
37
|
+
return JSON.stringify({
|
|
38
|
+
ok: true,
|
|
39
|
+
data: {
|
|
40
|
+
...walk(program, []),
|
|
41
|
+
version,
|
|
42
|
+
exitCodes: {
|
|
43
|
+
0: 'ok',
|
|
44
|
+
1: 'operation failed',
|
|
45
|
+
2: 'usage error',
|
|
46
|
+
3: 'gateway unreachable',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=help-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help-json.js","sourceRoot":"","sources":["../src/help-json.ts"],"names":[],"mappings":"AAoDA,SAAS,UAAU,CAAC,CAAsB;IACxC,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9F,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;QAChC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9B,6FAA6F;QAC7F,6FAA6F;QAC7F,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC;QAC7C,GAAG,CAAC,CAAC,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,iGAAiG;IACjG,qFAAqF;IACrF,MAAM,GAAG,GACP,CAAE,GAAoE,CAAC,mBAAmB;WACpF,GAAsD,CAAC,KAAK;WAC7D,EAAE,CAAC,CAAC;IACX,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACd,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7B,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE;KACjC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,IAAI,CAAC,GAAY,EAAE,MAAgB;IAC1C,MAAM,IAAI,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE;QAC9B,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACpB,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC;QAC3B,OAAO,EAAG,GAAG,CAAC,OAA4C,CAAC,GAAG,CAAC,UAAU,CAAC;QAC1E,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAY,EAAE,IAAI,CAAC,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,OAAe;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,EAAE,EAAE,IAAI;QACR,IAAI,EAAE;YACJ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACpB,OAAO;YACP,SAAS,EAAE;gBACT,CAAC,EAAE,IAAI;gBACP,CAAC,EAAE,kBAAkB;gBACrB,CAAC,EAAE,aAAa;gBAChB,CAAC,EAAE,qBAAqB;aACzB;SACF;KACF,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { Command } from 'commander';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
+
import { Command, CommanderError } from 'commander';
|
|
6
6
|
import { CertenError } from '@certen.io/sdk';
|
|
7
|
+
import { EXIT } from './errors.js';
|
|
8
|
+
import { setJsonMode, flushSuccess, emitFailure } from './output.js';
|
|
9
|
+
import { commandTree } from './help-json.js';
|
|
7
10
|
/** Read the version from package.json — dist/ sits one level below the manifest. */
|
|
8
11
|
function readVersion() {
|
|
9
12
|
try {
|
|
@@ -22,43 +25,100 @@ import { registerPendingCommands } from './commands/pending.js';
|
|
|
22
25
|
import { registerGovernanceCommands } from './commands/governance.js';
|
|
23
26
|
import { registerPortfolioCommands } from './commands/portfolio.js';
|
|
24
27
|
import { registerAdminCommands } from './commands/admin.js';
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
program
|
|
42
|
-
program
|
|
43
|
-
|
|
28
|
+
const VERSION = readVersion();
|
|
29
|
+
export function buildProgram() {
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program
|
|
32
|
+
.name('certen')
|
|
33
|
+
.description('CERTEN Gateway CLI')
|
|
34
|
+
// Was hardcoded to 0.1.0 while package.json said 0.2.0, so `certen --version` reported a
|
|
35
|
+
// release that does not exist. Read it from the manifest so the two cannot drift again.
|
|
36
|
+
.version(VERSION)
|
|
37
|
+
// Declared so it appears in --help. It is stripped from argv before parsing (see run()), because
|
|
38
|
+
// a global flag that may appear after a subcommand is not something commander models.
|
|
39
|
+
.option('--json', 'Emit one JSON envelope on stdout; human output goes to stderr');
|
|
40
|
+
registerAuthCommands(program);
|
|
41
|
+
registerKeysCommands(program);
|
|
42
|
+
registerIdentityCommands(program);
|
|
43
|
+
registerTransactionCommands(program);
|
|
44
|
+
registerPendingCommands(program);
|
|
45
|
+
registerGovernanceCommands(program);
|
|
46
|
+
registerPortfolioCommands(program);
|
|
47
|
+
registerAdminCommands(program);
|
|
48
|
+
// Every command, not just the root. `exitOverride()` is NOT inherited by subcommands, so without
|
|
49
|
+
// this walk a missing required flag on `identity create` called process.exit(1) inside commander:
|
|
50
|
+
// stdout stayed empty, no envelope was ever emitted, and a usage error was indistinguishable from
|
|
51
|
+
// a failed request. Every nested command has to opt in explicitly.
|
|
52
|
+
applyExitOverride(program);
|
|
53
|
+
return program;
|
|
54
|
+
}
|
|
55
|
+
function applyExitOverride(cmd) {
|
|
56
|
+
cmd.exitOverride();
|
|
57
|
+
for (const sub of cmd.commands)
|
|
58
|
+
applyExitOverride(sub);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Map a thrown value to an exit code, and emit the matching envelope.
|
|
62
|
+
*
|
|
63
|
+
* The four codes exist so an automated caller never has to parse English to decide what to do:
|
|
64
|
+
* `2` means fix the invocation, `3` means the gateway was unreachable and nothing was submitted,
|
|
65
|
+
* `1` means the gateway answered and said no.
|
|
66
|
+
*/
|
|
67
|
+
function handleError(err) {
|
|
68
|
+
if (err instanceof CommanderError) {
|
|
69
|
+
// --help and --version are successful terminations that commander reports by throwing.
|
|
70
|
+
if (err.exitCode === 0 || err.code === 'commander.helpDisplayed' || err.code === 'commander.version') {
|
|
71
|
+
return EXIT.OK;
|
|
72
|
+
}
|
|
73
|
+
return emitFailure({
|
|
74
|
+
message: err.message.replace(/^error: /, ''),
|
|
75
|
+
code: 'USAGE_ERROR',
|
|
76
|
+
exitCode: EXIT.USAGE,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (err instanceof CertenError) {
|
|
80
|
+
// isRetryable is the SDK's own judgement — reuse it so both transports agree.
|
|
81
|
+
return emitFailure({
|
|
82
|
+
message: err.message,
|
|
83
|
+
code: err.code,
|
|
84
|
+
status: err.status,
|
|
85
|
+
requestId: err.requestId,
|
|
86
|
+
isRetryable: err.isRetryable,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return emitFailure(err);
|
|
90
|
+
}
|
|
91
|
+
export async function run(argv) {
|
|
92
|
+
// `--json` is resolved before parsing so it applies to every code path, including failures that
|
|
93
|
+
// happen while resolving credentials — those must be reportable in the envelope too.
|
|
94
|
+
const json = argv.includes('--json');
|
|
95
|
+
setJsonMode(json);
|
|
96
|
+
const cleanArgv = argv.filter((a) => a !== '--json');
|
|
97
|
+
const program = buildProgram();
|
|
98
|
+
// `certen --help --json` answers "what can this CLI do" in one call, instead of scraping help
|
|
99
|
+
// text once per subcommand.
|
|
100
|
+
if (json && (cleanArgv.includes('--help') || cleanArgv.includes('-h'))) {
|
|
101
|
+
process.stdout.write(`${commandTree(program, VERSION)}\n`);
|
|
102
|
+
return EXIT.OK;
|
|
103
|
+
}
|
|
44
104
|
try {
|
|
45
|
-
await program.parseAsync(
|
|
105
|
+
await program.parseAsync(cleanArgv);
|
|
106
|
+
flushSuccess();
|
|
107
|
+
return EXIT.OK;
|
|
46
108
|
}
|
|
47
109
|
catch (err) {
|
|
48
|
-
|
|
49
|
-
console.error(`Error [${err.code}]: ${err.message}`);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
if (err instanceof Error) {
|
|
53
|
-
// Commander exit override throws for --help etc
|
|
54
|
-
if (err.exitCode === 0)
|
|
55
|
-
return;
|
|
56
|
-
console.error(`Error: ${err.message}`);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
console.error('Unknown error:', err);
|
|
60
|
-
process.exit(1);
|
|
110
|
+
return handleError(err);
|
|
61
111
|
}
|
|
62
112
|
}
|
|
63
|
-
|
|
113
|
+
/* c8 ignore start — entrypoint wiring, exercised by the conformance suite as a subprocess */
|
|
114
|
+
// Run only when executed as the binary, not when imported by a test. `pathToFileURL` is what makes
|
|
115
|
+
// this correct on Windows, where a naive `file://` + path concatenation produces a URL that never
|
|
116
|
+
// matches `import.meta.url`.
|
|
117
|
+
const invokedDirectly = Boolean(process.argv[1]) && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
118
|
+
if (invokedDirectly) {
|
|
119
|
+
run(process.argv).then((code) => {
|
|
120
|
+
process.exitCode = code;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/* c8 ignore stop */
|
|
64
124
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/output.d.ts
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
|
+
import { type ExitCode } from './errors.js';
|
|
2
|
+
export declare function setJsonMode(on: boolean): void;
|
|
3
|
+
export declare function isJsonMode(): boolean;
|
|
4
|
+
/** Test seam: reset module state between cases. */
|
|
5
|
+
export declare function resetOutput(): void;
|
|
1
6
|
/**
|
|
2
|
-
* Print
|
|
7
|
+
* Print a result. In JSON mode this buffers; call `flushSuccess()` once the command completes.
|
|
3
8
|
*/
|
|
4
9
|
export declare function printOutput(data: Record<string, unknown>[] | Record<string, unknown>, opts?: {
|
|
5
10
|
forceJson?: boolean;
|
|
6
11
|
}): void;
|
|
12
|
+
/**
|
|
13
|
+
* A human-facing note — "API key saved", "(no keys)".
|
|
14
|
+
*
|
|
15
|
+
* In JSON mode this goes to stderr, because stdout is reserved for the single envelope. In table
|
|
16
|
+
* mode it is ordinary output and goes to stdout.
|
|
17
|
+
*/
|
|
18
|
+
export declare function human(message: string): void;
|
|
19
|
+
/** A hint or warning. Always stderr — it is never the result of the command. */
|
|
20
|
+
export declare function hint(message: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Emit the success envelope. No-op outside JSON mode, and safe to call more than once.
|
|
23
|
+
*
|
|
24
|
+
* A command that produced no payload still emits `{"ok":true,"data":null}` rather than nothing, so
|
|
25
|
+
* a caller can always parse stdout instead of special-casing empty output.
|
|
26
|
+
*/
|
|
27
|
+
export declare function flushSuccess(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Emit the failure envelope (JSON mode) or a human error line (table mode), and return the exit code.
|
|
30
|
+
*
|
|
31
|
+
* `retryable` is taken from the SDK's own `CertenError.isRetryable` wherever the error came from the
|
|
32
|
+
* gateway, so the CLI and the SDK hand an automated caller the identical retry decision. That
|
|
33
|
+
* equivalence is the property worth having: a script can switch transports without re-deriving which
|
|
34
|
+
* failures are worth another attempt.
|
|
35
|
+
*/
|
|
36
|
+
export declare function emitFailure(err: unknown): ExitCode;
|
package/dist/output.js
CHANGED
|
@@ -1,13 +1,118 @@
|
|
|
1
1
|
import { getOutputFormat } from './config.js';
|
|
2
|
+
import { CliError, EXIT } from './errors.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Two output modes with different audiences and different guarantees.
|
|
5
|
+
*
|
|
6
|
+
* Table mode is for humans and is NOT a contract — column widths and wording may change.
|
|
7
|
+
*
|
|
8
|
+
* JSON mode is a machine contract: exactly one envelope object on stdout and nothing else, every
|
|
9
|
+
* human-facing line on stderr, and an exit code that says which kind of failure occurred. See
|
|
10
|
+
* docs/CLI-CONTRACT.md. The single-object guarantee is why payloads are buffered rather than
|
|
11
|
+
* printed as they arrive: a command that prints twice would otherwise emit two top-level objects
|
|
12
|
+
* and break every streaming JSON parser pointed at it.
|
|
13
|
+
*/
|
|
14
|
+
let jsonMode = false;
|
|
15
|
+
/** Payloads collected in JSON mode, flushed as one envelope when the command finishes. */
|
|
16
|
+
let collected = [];
|
|
17
|
+
let flushed = false;
|
|
18
|
+
export function setJsonMode(on) {
|
|
19
|
+
jsonMode = on;
|
|
20
|
+
}
|
|
21
|
+
export function isJsonMode() {
|
|
22
|
+
return jsonMode;
|
|
23
|
+
}
|
|
24
|
+
/** Test seam: reset module state between cases. */
|
|
25
|
+
export function resetOutput() {
|
|
26
|
+
jsonMode = false;
|
|
27
|
+
collected = [];
|
|
28
|
+
flushed = false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Print a result. In JSON mode this buffers; call `flushSuccess()` once the command completes.
|
|
4
32
|
*/
|
|
5
33
|
export function printOutput(data, opts) {
|
|
34
|
+
if (jsonMode) {
|
|
35
|
+
collected.push(data);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
6
38
|
const format = opts?.forceJson ? 'json' : getOutputFormat();
|
|
7
39
|
if (format === 'json') {
|
|
8
40
|
console.log(JSON.stringify(data, null, 2));
|
|
9
41
|
return;
|
|
10
42
|
}
|
|
43
|
+
printTable(data);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A human-facing note — "API key saved", "(no keys)".
|
|
47
|
+
*
|
|
48
|
+
* In JSON mode this goes to stderr, because stdout is reserved for the single envelope. In table
|
|
49
|
+
* mode it is ordinary output and goes to stdout.
|
|
50
|
+
*/
|
|
51
|
+
export function human(message) {
|
|
52
|
+
if (jsonMode)
|
|
53
|
+
console.error(message);
|
|
54
|
+
else
|
|
55
|
+
console.log(message);
|
|
56
|
+
}
|
|
57
|
+
/** A hint or warning. Always stderr — it is never the result of the command. */
|
|
58
|
+
export function hint(message) {
|
|
59
|
+
console.error(message);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Emit the success envelope. No-op outside JSON mode, and safe to call more than once.
|
|
63
|
+
*
|
|
64
|
+
* A command that produced no payload still emits `{"ok":true,"data":null}` rather than nothing, so
|
|
65
|
+
* a caller can always parse stdout instead of special-casing empty output.
|
|
66
|
+
*/
|
|
67
|
+
export function flushSuccess() {
|
|
68
|
+
if (!jsonMode || flushed)
|
|
69
|
+
return;
|
|
70
|
+
flushed = true;
|
|
71
|
+
const data = collected.length === 0 ? null : collected.length === 1 ? collected[0] : collected;
|
|
72
|
+
process.stdout.write(`${JSON.stringify({ ok: true, data })}\n`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Emit the failure envelope (JSON mode) or a human error line (table mode), and return the exit code.
|
|
76
|
+
*
|
|
77
|
+
* `retryable` is taken from the SDK's own `CertenError.isRetryable` wherever the error came from the
|
|
78
|
+
* gateway, so the CLI and the SDK hand an automated caller the identical retry decision. That
|
|
79
|
+
* equivalence is the property worth having: a script can switch transports without re-deriving which
|
|
80
|
+
* failures are worth another attempt.
|
|
81
|
+
*/
|
|
82
|
+
export function emitFailure(err) {
|
|
83
|
+
const e = (err ?? {});
|
|
84
|
+
const message = e.message ?? String(err);
|
|
85
|
+
const code = e.code ?? 'UNKNOWN_ERROR';
|
|
86
|
+
const retryable = e instanceof CliError ? e.retryable : Boolean(e.isRetryable);
|
|
87
|
+
const exitCode = resolveExitCode(e);
|
|
88
|
+
if (jsonMode) {
|
|
89
|
+
flushed = true;
|
|
90
|
+
process.stdout.write(`${JSON.stringify({
|
|
91
|
+
ok: false,
|
|
92
|
+
error: {
|
|
93
|
+
code,
|
|
94
|
+
message,
|
|
95
|
+
retryable,
|
|
96
|
+
...(e.status !== undefined ? { status: e.status } : {}),
|
|
97
|
+
...(e.requestId ? { requestId: e.requestId } : {}),
|
|
98
|
+
},
|
|
99
|
+
})}\n`);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.error(code === 'UNKNOWN_ERROR' ? `Error: ${message}` : `Error [${code}]: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
return exitCode;
|
|
105
|
+
}
|
|
106
|
+
function resolveExitCode(e) {
|
|
107
|
+
if (typeof e.exitCode === 'number')
|
|
108
|
+
return e.exitCode;
|
|
109
|
+
// status 0 is how the SDK reports "the request never reached the gateway".
|
|
110
|
+
if (e.code === 'NETWORK_ERROR' || e.status === 0)
|
|
111
|
+
return EXIT.UNREACHABLE;
|
|
112
|
+
return EXIT.FAILED;
|
|
113
|
+
}
|
|
114
|
+
// ── table rendering (human mode only) ───────────────────────────────────────────────────────────
|
|
115
|
+
function printTable(data) {
|
|
11
116
|
// Single object: print key-value pairs
|
|
12
117
|
if (!Array.isArray(data)) {
|
|
13
118
|
const entries = Object.entries(data);
|
package/dist/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,56 +1,57 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@certen.io/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Command line for the CERTEN Gateway API",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"keywords": [
|
|
8
|
-
"certen",
|
|
9
|
-
"accumulate",
|
|
10
|
-
"cli",
|
|
11
|
-
"blockchain"
|
|
12
|
-
],
|
|
13
|
-
"homepage": "https://github.com/certenIO/certen-sdk#readme",
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/certenIO/certen-sdk.git",
|
|
17
|
-
"directory": "packages/cli"
|
|
18
|
-
},
|
|
19
|
-
"bugs": {
|
|
20
|
-
"url": "https://github.com/certenIO/certen-sdk/issues"
|
|
21
|
-
},
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=18"
|
|
24
|
-
},
|
|
25
|
-
"bin": {
|
|
26
|
-
"certen": "dist/index.js"
|
|
27
|
-
},
|
|
28
|
-
"//files": "Build output plus docs. Without this npm ships the whole directory, including src/ and test/.",
|
|
29
|
-
"files": [
|
|
30
|
-
"dist/",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"prepack": "
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"dependencies":
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@certen.io/cli",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Command line for the CERTEN Gateway API",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"certen",
|
|
9
|
+
"accumulate",
|
|
10
|
+
"cli",
|
|
11
|
+
"blockchain"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/certenIO/certen-sdk#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/certenIO/certen-sdk.git",
|
|
17
|
+
"directory": "packages/cli"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/certenIO/certen-sdk/issues"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"certen": "dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"//files": "Build output plus docs. Without this npm ships the whole directory, including src/ and test/.",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist/",
|
|
31
|
+
"CHANGELOG.md",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"//prepack": "Build before packing so the published bin can never point at a missing dist/index.js.",
|
|
40
|
+
"prepack": "npm run build",
|
|
41
|
+
"prepublishOnly": "npm run build && npm test"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"//dependencies": "@certen.io/sdk MUST be a version range, not file:../sdk. A published package carrying a file: dependency installs and then fails to resolve on the consumer's machine, because that path does not exist there. Local development uses `npm link` or a workspace instead.",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@certen.io/sdk": "^0.4.0",
|
|
49
|
+
"commander": "^12.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"typescript": "^5.5.0",
|
|
53
|
+
"vitest": "^1.6.1",
|
|
54
|
+
"@types/node": "^22.0.0"
|
|
55
|
+
},
|
|
56
|
+
"//publishConfig": "Provenance is passed as --provenance by .github/workflows/release.yml. Setting it here instead would break every publish outside CI with an error that does not explain itself."
|
|
57
|
+
}
|