@vorlek/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/cli.js +76 -0
- package/dist/client.js +148 -0
- package/dist/commands/auth.js +157 -0
- package/dist/commands/campaign.js +108 -0
- package/dist/commands/catalog.js +53 -0
- package/dist/commands/connect.js +126 -0
- package/dist/commands/connection.js +45 -0
- package/dist/commands/contact.js +120 -0
- package/dist/commands/detail.js +12 -0
- package/dist/commands/email.js +67 -0
- package/dist/commands/operation.js +38 -0
- package/dist/commands/provider.js +16 -0
- package/dist/commands/status.js +65 -0
- package/dist/commands/table.js +10 -0
- package/dist/commands/template.js +82 -0
- package/dist/config.js +83 -0
- package/dist/deps.js +39 -0
- package/dist/idempotency.js +22 -0
- package/dist/run-context.js +79 -0
- package/package.json +49 -0
package/dist/deps.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as clackPrompts from '@clack/prompts';
|
|
2
|
+
import { createApiClient } from './client.js';
|
|
3
|
+
import { deleteConfig as defaultDeleteConfig, loadConfig as defaultLoadConfig, saveConfig as defaultSaveConfig, } from './config.js';
|
|
4
|
+
/** Default Deps bundle for production. */
|
|
5
|
+
export function defaultDeps() {
|
|
6
|
+
return {
|
|
7
|
+
loadConfig: defaultLoadConfig,
|
|
8
|
+
saveConfig: defaultSaveConfig,
|
|
9
|
+
deleteConfig: defaultDeleteConfig,
|
|
10
|
+
createApiClient,
|
|
11
|
+
prompts: clackPrompts,
|
|
12
|
+
stdout: process.stdout,
|
|
13
|
+
stderr: process.stderr,
|
|
14
|
+
exit: process.exit,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Tests use this to construct a Deps bundle whose `exit` throws an
|
|
19
|
+
* `ExitError(code)` — pair with `expect(() => fn()).toThrow(ExitError)`
|
|
20
|
+
* + `.catch(e => e.code)` to assert the intended exit code without
|
|
21
|
+
* mocking process.exit (which can leak across tests).
|
|
22
|
+
*/
|
|
23
|
+
export class ExitError extends Error {
|
|
24
|
+
code;
|
|
25
|
+
constructor(code) {
|
|
26
|
+
super(`process.exit(${code})`);
|
|
27
|
+
this.name = 'ExitError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Helper: load config or instruct the user to run `vorlek auth signup` /
|
|
33
|
+
* `vorlek auth use`. Used by every command except `auth signup` itself.
|
|
34
|
+
* Returns null if no config; caller is responsible for printing + exiting.
|
|
35
|
+
*/
|
|
36
|
+
export async function tryLoadConfig(deps) {
|
|
37
|
+
return deps.loadConfig();
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=deps.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
3
|
+
const ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
4
|
+
function encodeTime(nowMs) {
|
|
5
|
+
let value = Math.floor(nowMs);
|
|
6
|
+
const chars = Array.from({ length: 10 }, () => '0');
|
|
7
|
+
for (let i = chars.length - 1; i >= 0; i--) {
|
|
8
|
+
chars[i] = CROCKFORD[value % 32] ?? '0';
|
|
9
|
+
value = Math.floor(value / 32);
|
|
10
|
+
}
|
|
11
|
+
return chars.join('');
|
|
12
|
+
}
|
|
13
|
+
function encodeRandom() {
|
|
14
|
+
return Array.from(randomBytes(16), (byte) => CROCKFORD[byte & 31] ?? '0').join('');
|
|
15
|
+
}
|
|
16
|
+
export function newIdempotencyKey(nowMs = Date.now()) {
|
|
17
|
+
return `${encodeTime(nowMs)}${encodeRandom()}`;
|
|
18
|
+
}
|
|
19
|
+
export function isIdempotencyKey(value) {
|
|
20
|
+
return typeof value === 'string' && ULID_RE.test(value);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=idempotency.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { ApiError } from './client.js';
|
|
3
|
+
/** Print a success result. JSON mode emits one machine-readable line. */
|
|
4
|
+
export function renderSuccess(deps, ctx, data, human, meta) {
|
|
5
|
+
if (ctx.json) {
|
|
6
|
+
const out = { status: 'success', data };
|
|
7
|
+
if (meta?.request_id)
|
|
8
|
+
out.request_id = meta.request_id;
|
|
9
|
+
deps.stdout.write(`${JSON.stringify(out)}\n`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
deps.stdout.write(`${human()}\n`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Render an error and return the appropriate exit code per spec § 11. The
|
|
16
|
+
* caller passes the result to `deps.exit()` to actually terminate.
|
|
17
|
+
*
|
|
18
|
+
* - ApiError → exit 2 (server error). request_id rendered for support triage.
|
|
19
|
+
* - Error tagged with `.code === 'CONFIG_MISSING'` → exit 3.
|
|
20
|
+
* - Anything else → exit 1 (user error).
|
|
21
|
+
*/
|
|
22
|
+
export function renderError(deps, ctx, err) {
|
|
23
|
+
if (err instanceof ApiError) {
|
|
24
|
+
if (ctx.json) {
|
|
25
|
+
const out = {
|
|
26
|
+
status: 'error',
|
|
27
|
+
error: { code: err.code, message: err.envelope.error.message },
|
|
28
|
+
request_id: err.requestId,
|
|
29
|
+
};
|
|
30
|
+
if (err.envelope.error.fix)
|
|
31
|
+
out.fix = err.envelope.error.fix;
|
|
32
|
+
if (err.envelope.error.detail !== undefined)
|
|
33
|
+
out.detail = err.envelope.error.detail;
|
|
34
|
+
deps.stderr.write(`${JSON.stringify(out)}\n`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
deps.stderr.write(`${pc.red('error')} ${pc.bold(err.code)} — ${err.envelope.error.message}\n`);
|
|
38
|
+
if (err.envelope.error.fix) {
|
|
39
|
+
deps.stderr.write(` ${pc.dim('fix:')} ${err.envelope.error.fix.hint}\n`);
|
|
40
|
+
}
|
|
41
|
+
deps.stderr.write(` ${pc.dim('request_id:')} ${err.requestId}\n`);
|
|
42
|
+
}
|
|
43
|
+
return 2;
|
|
44
|
+
}
|
|
45
|
+
// Local config / setup errors carry a tagged `code`.
|
|
46
|
+
const localCode = err.code;
|
|
47
|
+
if (localCode === 'CONFIG_MISSING') {
|
|
48
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
+
if (ctx.json) {
|
|
50
|
+
deps.stderr.write(`${JSON.stringify({ status: 'error', error: { code: 'CONFIG_MISSING', message: msg } })}\n`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
deps.stderr.write(`${pc.red('error')} ${pc.bold('CONFIG_MISSING')} — ${msg}\n`);
|
|
54
|
+
deps.stderr.write(` ${pc.dim('hint:')} run ${pc.bold('vorlek auth signup')} or ${pc.bold('vorlek auth use --api-key vk_live_...')}\n`);
|
|
55
|
+
}
|
|
56
|
+
return 3;
|
|
57
|
+
}
|
|
58
|
+
// Default: user-facing error (bad flags, validation rejection).
|
|
59
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60
|
+
if (ctx.json) {
|
|
61
|
+
deps.stderr.write(`${JSON.stringify({ status: 'error', error: { code: 'USER_ERROR', message: msg } })}\n`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
deps.stderr.write(`${pc.red('error')} ${msg}\n`);
|
|
65
|
+
}
|
|
66
|
+
if (ctx.verbose && err instanceof Error && err.stack) {
|
|
67
|
+
deps.stderr.write(`${pc.dim(err.stack)}\n`);
|
|
68
|
+
}
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
/** Tagged config-missing error. Throw from any command that requires config. */
|
|
72
|
+
export class ConfigMissingError extends Error {
|
|
73
|
+
code = 'CONFIG_MISSING';
|
|
74
|
+
constructor(message = 'No config file found.') {
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = 'ConfigMissingError';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=run-context.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vorlek/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vorlek CLI — thin REST API wrapper for the Vorlek aggregator",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"homepage": "https://vorlek.dev/docs/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/vorlek/vorlek-cli.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"packageManager": "pnpm@9.15.0",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"vorlek": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": ["dist/**/*.js"],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "tsx src/cli.ts",
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"start": "node dist/cli.js",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"lint": "biome check .",
|
|
29
|
+
"lint:fix": "biome check --write .",
|
|
30
|
+
"format": "biome format --write .",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@clack/prompts": "^0.9.0",
|
|
36
|
+
"@vorlek/sdk": "^0.4.1",
|
|
37
|
+
"cac": "^6.7.14",
|
|
38
|
+
"ky": "^1.7.0",
|
|
39
|
+
"picocolors": "^1.1.0",
|
|
40
|
+
"zod": "^3.25.76"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@biomejs/biome": "^1.9.4",
|
|
44
|
+
"@types/node": "^24.0.0",
|
|
45
|
+
"tsx": "^4.19.0",
|
|
46
|
+
"typescript": "^5.6.0",
|
|
47
|
+
"vitest": "^2.1.0"
|
|
48
|
+
}
|
|
49
|
+
}
|