@inboxychat/cli 0.0.0-stage → 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 CHANGED
@@ -1,3 +1,121 @@
1
- # Temporary Holding Version
1
+ # @inboxychat/cli — `inboxy` (read-only)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A command line for agencies and support: check a key, read orders, contacts and
4
+ usage, and diagnose a setup. It uses only the public API v1
5
+ (`/api/v1/public`) and never writes.
6
+
7
+ Status: local package (plan task API-04). Not published to npm.
8
+
9
+ ## Install (local)
10
+
11
+ ```bash
12
+ pnpm install
13
+ pnpm --filter @inboxychat/cli build # builds the SDK too
14
+ node packages/inboxy-cli/dist/bin.js --help
15
+ # optional: alias inboxy="node $PWD/packages/inboxy-cli/dist/bin.js"
16
+ ```
17
+
18
+ ## Sign in
19
+
20
+ ```bash
21
+ inboxy auth login # paste the key at the hidden prompt
22
+ pbpaste | inboxy auth login # or pipe it (keeps it out of shell history)
23
+ inboxy auth login --profile client-a # one profile per company (agencies)
24
+ inboxy auth status # where the key comes from + is it accepted
25
+ inboxy auth logout --profile client-a
26
+ ```
27
+
28
+ The key is read from stdin, never from a command-line argument: `inboxy auth login <key>`
29
+ or a `--key` / `--token` / `--api-key` flag on any command is refused (exit 2) without
30
+ echoing what was typed. It is stored in
31
+ the OS keychain (macOS Keychain via `security`, Linux Secret Service via
32
+ `secret-tool`; the secret goes to those tools on stdin, not argv). Without a
33
+ keychain (or with `--store file`) it goes into the config file with mode `0600`
34
+ in a `0700` directory; a config file other users can read is refused.
35
+
36
+ Config: `$INBOXY_CONFIG_DIR`, else `$XDG_CONFIG_HOME/inboxy`, else
37
+ `~/.config/inboxy/config.json`. It holds the API URL and where the key lives.
38
+
39
+ `INBOXY_API_KEY` / `INBOXY_API_URL` / `INBOXY_PROFILE` override everything.
40
+ `INBOXY_KEYCHAIN=off` disables the keychain.
41
+
42
+ ## Commands
43
+
44
+ ```
45
+ inboxy whoami company, key name, scopes, expiry, rate limits
46
+ inboxy usage plan usage against limits
47
+ inboxy stores list connected-store count vs plan limit (*)
48
+ inboxy orders list [--status pending] [--payment-status ..] [--fulfillment-status ..]
49
+ [--source shopify] [--store <id>] [--contact <id>] [--search <text>]
50
+ [--after <iso>] [--before <iso>]
51
+ inboxy orders get <id>
52
+ inboxy contacts search [text] [--status ..] [--stage lead] [--tags <ids>]
53
+ [--created-after <iso>] [--created-before <iso>]
54
+ inboxy doctor URL, key, key format, reachability, scopes,
55
+ key expiry, clock skew, config permissions
56
+ ```
57
+
58
+ (\*) The public API has no stores route yet, so `stores list` shows the count
59
+ from plan usage and says so (`items: null` in JSON). Per-store health is in the
60
+ dashboard.
61
+
62
+ Paging: `--limit 1-100`, `--cursor <nextCursor>`, or `--all` (follows cursors,
63
+ up to `--max-pages`, default 10). Global: `--json`, `--profile`, `--url`,
64
+ `--timeout <ms>`. Global options may come before or after the command.
65
+
66
+ ## `--json`
67
+
68
+ One document on stdout, schema version 1 (fields may be added, never renamed or removed):
69
+
70
+ ```json
71
+ { "schemaVersion": 1, "ok": true, "command": "orders list",
72
+ "data": [ ... ], "meta": { "limit": 20, "hasMore": true, "nextCursor": "...", "pages": 1 } }
73
+
74
+ { "schemaVersion": 1, "ok": false, "command": "orders list",
75
+ "error": { "kind": "forbidden", "code": "INSUFFICIENT_PERMISSIONS", "status": 403,
76
+ "message": "This API key does not have the \"orders:read\" scope. ...",
77
+ "details": { "requiredScope": "orders:read" }, "retryAfterSeconds": null } }
78
+ ```
79
+
80
+ `data` items are the public API v1 objects unchanged.
81
+
82
+ ## Exit codes
83
+
84
+ | Code | Meaning |
85
+ | ---- | ------------------------------------------------------------------------- |
86
+ | 0 | OK |
87
+ | 1 | Unexpected error |
88
+ | 2 | Usage error (bad flag, bad value, invalid id) — nothing was sent |
89
+ | 3 | Configuration: no key, bad URL, config file permissions, no keychain |
90
+ | 4 | Key refused (401) |
91
+ | 5 | Forbidden (403): missing scope, company paused, key owner no longer admin |
92
+ | 6 | Not found (404) |
93
+ | 7 | Rate limited (429) — see `retryAfterSeconds` |
94
+ | 8 | Network error, timeout or cancelled |
95
+ | 9 | Server error or a response that is not the API envelope |
96
+ | 10 | `doctor` found at least one failing check |
97
+
98
+ ## Safety
99
+
100
+ - The key is never printed. Errors and JSON pass through redaction (any
101
+ `inboxy_…` value, any `Bearer …` value and the exact key in use), even when a
102
+ server message echoes the header back. Status output shows only the 8-character
103
+ prefix the dashboard also shows.
104
+ - No shell: inputs reach the API only as validated, percent-encoded path
105
+ segments or query values; ids must be 1–64 letters, digits, `_` or `-`.
106
+ - HTTPS only (plain `http://` for `localhost` only); redirects are refused.
107
+
108
+ ## Tests
109
+
110
+ ```bash
111
+ pnpm --filter @inboxychat/cli test
112
+ ```
113
+
114
+ `test/cli.e2e.test.ts` runs the built binary against the local mock API:
115
+ every command, paging, filters, exit codes, the key never in stdout/stderr,
116
+ no shell interpolation, auth login/status/logout with the 0600 file, refusal
117
+ of a world-readable config, profiles per company, and doctor (clock skew,
118
+ scopes, expiry, refused key, unreachable URL). `test/cli.inprocess.test.ts`
119
+ covers the keychain path with a fake keychain and redaction of a key echoed by
120
+ the server. The macOS Keychain adapter was also checked once by hand with a
121
+ throwaway entry (store, update, read, delete).
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from './cli.js';
3
+ async function readAll(stream) {
4
+ let data = '';
5
+ stream.setEncoding('utf8');
6
+ for await (const chunk of stream)
7
+ data += chunk;
8
+ return data;
9
+ }
10
+ /** Reads a line from the terminal without echoing it. */
11
+ function promptHidden(prompt) {
12
+ const stdin = process.stdin;
13
+ process.stderr.write(prompt);
14
+ return new Promise((resolve) => {
15
+ let input = '';
16
+ stdin.setRawMode(true);
17
+ stdin.resume();
18
+ stdin.setEncoding('utf8');
19
+ const onData = (chunk) => {
20
+ for (const ch of chunk) {
21
+ if (ch === '\r' || ch === '\n' || ch === '\u0004') {
22
+ stdin.setRawMode(false);
23
+ stdin.pause();
24
+ stdin.off('data', onData);
25
+ process.stderr.write('\n');
26
+ resolve(input);
27
+ return;
28
+ }
29
+ if (ch === '\u0003') {
30
+ stdin.setRawMode(false);
31
+ process.stderr.write('\n');
32
+ process.exit(130);
33
+ }
34
+ if (ch === '\u007f' || ch === '\b')
35
+ input = input.slice(0, -1);
36
+ else
37
+ input += ch;
38
+ }
39
+ };
40
+ stdin.on('data', onData);
41
+ });
42
+ }
43
+ const io = {
44
+ stdout: (t) => process.stdout.write(t),
45
+ stderr: (t) => process.stderr.write(t),
46
+ readSecret: (prompt) => (process.stdin.isTTY ? promptHidden(prompt) : readAll(process.stdin)),
47
+ env: process.env,
48
+ now: () => Date.now(),
49
+ };
50
+ runCli(process.argv.slice(2), io).then((code) => {
51
+ process.exitCode = code;
52
+ }, () => {
53
+ process.stderr.write('inboxy: unexpected failure\n');
54
+ process.exitCode = 1;
55
+ });
56
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { type Keychain } from '@inboxychat/sdk';
2
+ import { type CliIO } from './output.js';
3
+ export declare const CLI_VERSION = "0.1.0";
4
+ export interface CliDeps {
5
+ /** Override the OS keychain (tests); undefined = detect, null = none. */
6
+ keychain?: Keychain | null;
7
+ }
8
+ /** Commands that only touch local configuration (no capability behind them). */
9
+ export declare const LOCAL_COMMANDS: readonly ["auth login", "auth logout", "auth status", "doctor"];
10
+ /**
11
+ * Data commands: each is the adapter of one registered capability (UX26-F,
12
+ * apps/api/src/lib/capabilities). A command without one is not offered.
13
+ */
14
+ export declare const DATA_COMMANDS: string[];
15
+ export declare function runCli(argv: string[], io: CliIO, deps?: CliDeps): Promise<number>;
16
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AASA,OAAO,EAqBL,KAAK,QAAQ,EAKd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAE1E,eAAO,MAAM,WAAW,UAAU,CAAC;AAwDnC,MAAM,WAAW,OAAO;IACtB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC5B;AA6DD,gFAAgF;AAChF,eAAO,MAAM,cAAc,iEAAkE,CAAC;AAE9F;;;GAGG;AACH,eAAO,MAAM,aAAa,UAEzB,CAAC;AAcF,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAE,OAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAub3F"}
package/dist/cli.js ADDED
@@ -0,0 +1,538 @@
1
+ /**
2
+ * The `inboxy` command line (local, read-only).
3
+ *
4
+ * Inputs are parsed with node:util parseArgs and only ever reach the network
5
+ * as validated, percent-encoded path segments or URLSearchParams values.
6
+ * Nothing here spawns a shell; the only child processes are the OS keychain
7
+ * tools, started with argument arrays by @inboxychat/sdk.
8
+ */
9
+ import { parseArgs } from 'node:util';
10
+ import { CONTACT_STATUSES, FULFILLMENT_STATUSES, InboxyClient, InboxyError, LIFECYCLE_STAGES, ORDER_SOURCES, ORDER_STATUSES, PAGE_LIMIT_MAX, PAYMENT_STATUSES, PUBLIC_CAPABILITIES, collectPages, loadConfig, looksLikeInboxyKey, normalizeBaseUrl, resolveCredentials, saveConfig, systemKeychain, validateProfileName, } from '@inboxychat/sdk';
11
+ import { runDoctor } from './doctor.js';
12
+ import { EXIT, Output, UsageError, table } from './output.js';
13
+ export const CLI_VERSION = '0.1.0';
14
+ const HELP = `inboxy ${CLI_VERSION} — read-only command line for the Inboxy public API
15
+
16
+ Usage: inboxy <command> [options]
17
+
18
+ Account
19
+ auth login [--store keychain|file] [--no-verify] Store an API key (read from stdin or a hidden prompt)
20
+ auth logout Remove the stored key of the profile
21
+ auth status [--offline] Where the key comes from, and whether Inboxy accepts it
22
+ whoami Company, key name, scopes, expiry, rate limits
23
+ usage Plan usage against limits
24
+ stores list Connected-store count against the plan limit
25
+ doctor Check URL, key, scopes, clock skew and config permissions
26
+
27
+ Data
28
+ orders list [filters] [paging] --status --payment-status --fulfillment-status --source
29
+ --store <id> --contact <id> --search <text>
30
+ --after <iso> --before <iso>
31
+ orders get <id>
32
+ contacts search [text] [paging] --status --stage --tags <ids> --created-after --created-before
33
+
34
+ Paging
35
+ --limit <1-${PAGE_LIMIT_MAX}> --cursor <nextCursor> --all [--max-pages <n>, default 10]
36
+
37
+ Global options
38
+ --json One JSON document on stdout (schemaVersion 1)
39
+ --profile <name> Use a stored profile (one per company; default "default", or INBOXY_PROFILE)
40
+ --url <origin> API origin (default INBOXY_API_URL, the profile, or https://api.inboxy.chat)
41
+ --timeout <ms> Per-request timeout (default 20000)
42
+ -h, --help This help --version Print the version
43
+
44
+ Credentials: INBOXY_API_KEY, else the OS keychain entry written by \`auth login\`,
45
+ else (machines without a keychain) the 0600 config file. The key is never printed.
46
+ Exit codes: 0 ok, 2 usage, 3 config, 4 key refused, 5 forbidden, 6 not found,
47
+ 7 rate limited, 8 network/timeout, 9 server, 10 doctor checks failed, 1 unexpected.`;
48
+ const KEY_ON_COMMAND_LINE = 'The API key is never passed as an argument (it would stay in shell history). Run `inboxy auth login` and type it at the hidden prompt, or pipe it: `pbpaste | inboxy auth login`.';
49
+ const GLOBAL_OPTIONS = {
50
+ json: { type: 'boolean' },
51
+ profile: { type: 'string' },
52
+ url: { type: 'string' },
53
+ timeout: { type: 'string' },
54
+ help: { type: 'boolean', short: 'h' },
55
+ version: { type: 'boolean' },
56
+ };
57
+ const PAGING_OPTIONS = {
58
+ limit: { type: 'string' },
59
+ cursor: { type: 'string' },
60
+ all: { type: 'boolean' },
61
+ 'max-pages': { type: 'string' },
62
+ };
63
+ function intOption(value, name, min, max) {
64
+ if (value === undefined)
65
+ return undefined;
66
+ if (!/^\d+$/.test(value))
67
+ throw new UsageError(`--${name} must be a whole number.`);
68
+ const n = Number(value);
69
+ if (n < min || n > max)
70
+ throw new UsageError(`--${name} must be between ${min} and ${max}.`);
71
+ return n;
72
+ }
73
+ function enumOption(value, name, allowed) {
74
+ if (value === undefined)
75
+ return undefined;
76
+ if (!allowed.includes(value)) {
77
+ throw new UsageError(`--${name} must be one of: ${allowed.join(', ')}.`);
78
+ }
79
+ return value;
80
+ }
81
+ function isoOption(value, name) {
82
+ if (value === undefined)
83
+ return undefined;
84
+ if (Number.isNaN(Date.parse(value)))
85
+ throw new UsageError(`--${name} must be an ISO-8601 date or date-time.`);
86
+ return value;
87
+ }
88
+ /**
89
+ * Splits `argv` into the command words and the rest, without interpreting
90
+ * values. Global options may come before the command (`inboxy --profile a whoami`).
91
+ */
92
+ const GLOBAL_VALUE_FLAGS = new Set(['--profile', '--url', '--timeout']);
93
+ function commandWords(argv) {
94
+ const words = [];
95
+ const rest = [];
96
+ let i = 0;
97
+ while (i < argv.length && words.length < 2) {
98
+ const arg = argv[i];
99
+ if (GLOBAL_VALUE_FLAGS.has(arg)) {
100
+ rest.push(arg, ...(i + 1 < argv.length ? [argv[i + 1]] : []));
101
+ i += 2;
102
+ }
103
+ else if (arg === '--json') {
104
+ rest.push(arg);
105
+ i += 1;
106
+ }
107
+ else if (arg.startsWith('-')) {
108
+ break;
109
+ }
110
+ else {
111
+ words.push(arg);
112
+ i += 1;
113
+ }
114
+ }
115
+ return { words, rest: [...rest, ...argv.slice(i)] };
116
+ }
117
+ /** Commands that only touch local configuration (no capability behind them). */
118
+ export const LOCAL_COMMANDS = ['auth login', 'auth logout', 'auth status', 'doctor'];
119
+ /**
120
+ * Data commands: each is the adapter of one registered capability (UX26-F,
121
+ * apps/api/src/lib/capabilities). A command without one is not offered.
122
+ */
123
+ export const DATA_COMMANDS = PUBLIC_CAPABILITIES.flatMap((c) => c.cliCommand ? [c.cliCommand] : []);
124
+ const COMMANDS = new Set([...LOCAL_COMMANDS, ...DATA_COMMANDS]);
125
+ function matchCommand(words) {
126
+ if (words.length >= 2 && COMMANDS.has(`${words[0]} ${words[1]}`)) {
127
+ return { name: `${words[0]} ${words[1]}`, positionals: [] };
128
+ }
129
+ if (words.length >= 1 && COMMANDS.has(words[0])) {
130
+ return { name: words[0], positionals: words.slice(1) };
131
+ }
132
+ return null;
133
+ }
134
+ export async function runCli(argv, io, deps = {}) {
135
+ const wantsJson = argv.includes('--json');
136
+ const { words, rest } = commandWords(argv);
137
+ const matched = matchCommand(words);
138
+ const commandName = matched?.name ?? words.join(' ');
139
+ let activeKey = null;
140
+ const out = new Output(io, wantsJson, commandName || 'help', () => activeKey ? [activeKey] : []);
141
+ try {
142
+ if (!matched) {
143
+ if (argv.includes('--version')) {
144
+ io.stdout(`${CLI_VERSION}\n`);
145
+ return EXIT.OK;
146
+ }
147
+ if (words.length === 0 || argv.includes('--help') || argv.includes('-h')) {
148
+ io.stdout(`${HELP}\n`);
149
+ return words.length === 0 && !argv.includes('--help') && !argv.includes('-h')
150
+ ? EXIT.USAGE
151
+ : EXIT.OK;
152
+ }
153
+ throw new UsageError(`Unknown command "${words.join(' ')}".`);
154
+ }
155
+ const extra = {};
156
+ const name = matched.name;
157
+ if (name === 'orders list') {
158
+ Object.assign(extra, PAGING_OPTIONS, {
159
+ status: { type: 'string' },
160
+ 'payment-status': { type: 'string' },
161
+ 'fulfillment-status': { type: 'string' },
162
+ source: { type: 'string' },
163
+ store: { type: 'string' },
164
+ contact: { type: 'string' },
165
+ search: { type: 'string' },
166
+ after: { type: 'string' },
167
+ before: { type: 'string' },
168
+ });
169
+ }
170
+ else if (name === 'contacts search') {
171
+ Object.assign(extra, PAGING_OPTIONS, {
172
+ status: { type: 'string' },
173
+ stage: { type: 'string' },
174
+ tags: { type: 'string' },
175
+ 'created-after': { type: 'string' },
176
+ 'created-before': { type: 'string' },
177
+ });
178
+ }
179
+ else if (name === 'auth login') {
180
+ Object.assign(extra, { store: { type: 'string' }, 'no-verify': { type: 'boolean' } });
181
+ }
182
+ else if (name === 'auth status') {
183
+ Object.assign(extra, { offline: { type: 'boolean' } });
184
+ }
185
+ // A key typed as an argument stays in shell history and process lists:
186
+ // refuse it before parsing, without echoing what was typed.
187
+ const KEY_FLAG = /^--(key|token|api-key|apikey|secret)(=|$)/i;
188
+ if (rest.some((arg) => KEY_FLAG.test(arg))) {
189
+ throw new UsageError(KEY_ON_COMMAND_LINE);
190
+ }
191
+ let parsed;
192
+ try {
193
+ parsed = parseArgs({
194
+ args: rest,
195
+ options: { ...GLOBAL_OPTIONS, ...extra },
196
+ allowPositionals: true,
197
+ strict: true,
198
+ });
199
+ }
200
+ catch (error) {
201
+ throw new UsageError(error.message.replace(/\. To specify.*$/s, '.'));
202
+ }
203
+ const opts = parsed.values;
204
+ const positionals = [...matched.positionals, ...parsed.positionals];
205
+ if (opts.help) {
206
+ io.stdout(`${HELP}\n`);
207
+ return EXIT.OK;
208
+ }
209
+ const profile = typeof opts.profile === 'string' ? validateProfileName(opts.profile) : undefined;
210
+ const timeoutMs = intOption(opts.timeout, 'timeout', 100, 120_000);
211
+ const env = typeof opts.url === 'string' ? { ...io.env, INBOXY_API_URL: opts.url } : io.env;
212
+ const keychainFor = async () => deps.keychain !== undefined ? deps.keychain : systemKeychain(env);
213
+ const credentials = async () => {
214
+ const creds = await resolveCredentials({ env, profile, keychain: await keychainFor() });
215
+ activeKey = creds.apiKey;
216
+ return creds;
217
+ };
218
+ const clientFrom = (creds) => {
219
+ if (!creds.apiKey) {
220
+ throw new InboxyError({
221
+ kind: 'config',
222
+ code: 'API_KEY_MISSING',
223
+ message: `No API key for profile "${creds.profile}". Set INBOXY_API_KEY or run \`inboxy auth login\`.`,
224
+ });
225
+ }
226
+ return new InboxyClient({
227
+ apiKey: creds.apiKey,
228
+ baseUrl: creds.apiUrl,
229
+ timeoutMs,
230
+ userAgent: `inboxy-cli/${CLI_VERSION}`,
231
+ });
232
+ };
233
+ const noPositionals = () => {
234
+ if (!positionals.length)
235
+ return;
236
+ // `auth login <key>`: never echo the argument back (it is probably the key).
237
+ if (name === 'auth login')
238
+ throw new UsageError(KEY_ON_COMMAND_LINE);
239
+ throw new UsageError(`Unexpected argument "${positionals[0]}".`);
240
+ };
241
+ switch (name) {
242
+ // ------------------------------------------------------------------
243
+ case 'whoami': {
244
+ noPositionals();
245
+ const { data } = await clientFrom(await credentials()).getCompany();
246
+ return out.success(data, undefined, () => [
247
+ `Company ${data.company.name} (${data.company.id})`,
248
+ `Key ${data.token.name} · ${data.token.prefix}…`,
249
+ `Scopes ${data.token.scopes.join(', ') || 'none'}`,
250
+ `Expires ${data.token.expiresAt ?? 'never'}`,
251
+ `Limits ${data.rateLimits.perKey.limit}/min per key · ${data.rateLimits.perCompany.limit}/min per company`,
252
+ ].join('\n'));
253
+ }
254
+ // ------------------------------------------------------------------
255
+ case 'usage': {
256
+ noPositionals();
257
+ const { data } = await clientFrom(await credentials()).getUsage();
258
+ return out.success(data, undefined, () => {
259
+ const rows = Object.entries(data.usage).map(([k, c]) => [
260
+ k,
261
+ c.used,
262
+ c.limit === null ? 'unlimited' : c.limit,
263
+ ]);
264
+ return [
265
+ `Plan ${data.plan.name} (${data.plan.status}${data.plan.isTrial ? ', trial' : ''}) · period ends ${data.plan.currentPeriodEnd}`,
266
+ table(['counter', 'used', 'limit'], rows),
267
+ `API requests in the last 24 h: ${data.api.requestsLast24h}`,
268
+ ].join('\n');
269
+ });
270
+ }
271
+ // ------------------------------------------------------------------
272
+ case 'stores list': {
273
+ noPositionals();
274
+ const { data } = await clientFrom(await credentials()).getUsage();
275
+ const result = {
276
+ stores: data.usage.stores,
277
+ items: null,
278
+ note: 'Public API v1 has no stores route yet; this is the connected-store count from plan usage. Per-store details and health are in the dashboard.',
279
+ };
280
+ return out.success(result, undefined, () => [
281
+ `Connected stores: ${data.usage.stores.used} of ${data.usage.stores.limit ?? 'unlimited'}`,
282
+ result.note,
283
+ ].join('\n'));
284
+ }
285
+ // ------------------------------------------------------------------
286
+ case 'orders list': {
287
+ noPositionals();
288
+ const query = {
289
+ status: enumOption(opts.status, 'status', ORDER_STATUSES),
290
+ paymentStatus: enumOption(opts['payment-status'], 'payment-status', PAYMENT_STATUSES),
291
+ fulfillmentStatus: enumOption(opts['fulfillment-status'], 'fulfillment-status', FULFILLMENT_STATUSES),
292
+ source: enumOption(opts.source, 'source', ORDER_SOURCES),
293
+ storeId: opts.store,
294
+ contactId: opts.contact,
295
+ search: opts.search,
296
+ dateAfter: isoOption(opts.after, 'after'),
297
+ dateBefore: isoOption(opts.before, 'before'),
298
+ limit: intOption(opts.limit, 'limit', 1, PAGE_LIMIT_MAX),
299
+ cursor: opts.cursor,
300
+ };
301
+ const client = clientFrom(await credentials());
302
+ const page = await listPages((q) => client.listOrders(q), query, opts);
303
+ return out.success(page.items, page.meta, () => [
304
+ table(['id', 'number', 'status', 'payment', 'total', 'customer', 'date'], page.items.map((o) => [
305
+ o.id,
306
+ o.orderNumber,
307
+ o.status,
308
+ o.paymentStatus,
309
+ `${o.total} ${o.currency}`,
310
+ o.contact?.name,
311
+ o.orderDate.slice(0, 10),
312
+ ])),
313
+ pagingHint(page.meta),
314
+ ]
315
+ .filter(Boolean)
316
+ .join('\n'));
317
+ }
318
+ case 'orders get': {
319
+ if (positionals.length !== 1)
320
+ throw new UsageError('Usage: inboxy orders get <id>');
321
+ const { data: o } = await clientFrom(await credentials()).getOrder(positionals[0]);
322
+ return out.success(o, undefined, () => [
323
+ `Order ${o.orderNumber} (${o.id}) · ${o.source}`,
324
+ `Status ${o.status} · payment ${o.paymentStatus} · fulfillment ${o.fulfillmentStatus} · confirmation ${o.confirmationStatus}`,
325
+ `Total ${o.total} ${o.currency} (subtotal ${o.subtotal}, shipping ${o.shipping ?? '-'}, discount ${o.discount ?? '-'})`,
326
+ `Customer ${o.contact ? `${o.contact.name} ${o.contact.phone ?? ''}`.trim() : '-'}`,
327
+ `Date ${o.orderDate}`,
328
+ table(['item', 'sku', 'qty', 'total'], o.items.map((i) => [
329
+ i.variant ? `${i.name} (${i.variant})` : i.name,
330
+ i.sku,
331
+ i.quantity,
332
+ i.totalPrice,
333
+ ])),
334
+ ].join('\n'));
335
+ }
336
+ // ------------------------------------------------------------------
337
+ case 'contacts search': {
338
+ if (positionals.length > 1)
339
+ throw new UsageError('Usage: inboxy contacts search [text]');
340
+ const query = {
341
+ search: positionals[0],
342
+ status: enumOption(opts.status, 'status', CONTACT_STATUSES),
343
+ lifecycleStage: enumOption(opts.stage, 'stage', LIFECYCLE_STAGES),
344
+ tagIds: opts.tags,
345
+ createdAfter: isoOption(opts['created-after'], 'created-after'),
346
+ createdBefore: isoOption(opts['created-before'], 'created-before'),
347
+ limit: intOption(opts.limit, 'limit', 1, PAGE_LIMIT_MAX),
348
+ cursor: opts.cursor,
349
+ };
350
+ const client = clientFrom(await credentials());
351
+ const page = await listPages((q) => client.listContacts(q), query, opts);
352
+ return out.success(page.items, page.meta, () => [
353
+ table(['id', 'name', 'phone', 'email', 'stage', 'orders'], page.items.map((c) => [
354
+ c.id,
355
+ c.name,
356
+ c.phone,
357
+ c.email,
358
+ c.lifecycleStage,
359
+ c.totalOrders,
360
+ ])),
361
+ pagingHint(page.meta),
362
+ ]
363
+ .filter(Boolean)
364
+ .join('\n'));
365
+ }
366
+ // ------------------------------------------------------------------
367
+ case 'doctor': {
368
+ noPositionals();
369
+ let creds;
370
+ let credentialError;
371
+ try {
372
+ creds = await credentials();
373
+ }
374
+ catch (error) {
375
+ // A refused config (e.g. a key in a world-readable file) is a failed
376
+ // check, not a crash.
377
+ if (!(error instanceof InboxyError) || error.kind !== 'config')
378
+ throw error;
379
+ credentialError = `${error.code}: ${error.message}`;
380
+ creds = {
381
+ profile: profile ?? env.INBOXY_PROFILE ?? 'default',
382
+ apiKey: null,
383
+ keySource: 'none',
384
+ apiUrl: env.INBOXY_API_URL ?? 'https://api.inboxy.chat',
385
+ urlSource: env.INBOXY_API_URL ? 'env' : 'default',
386
+ configPath: '',
387
+ warnings: [],
388
+ };
389
+ }
390
+ const report = await runDoctor(creds, { env, timeoutMs, now: io.now, credentialError });
391
+ const code = out.success(report, undefined, () => [
392
+ `Profile "${report.profile}" · ${report.apiUrl}`,
393
+ ...report.checks.map((c) => `${c.status.toUpperCase().padEnd(4)} ${c.id.padEnd(14)} ${c.message}`),
394
+ report.ok ? 'All checks passed.' : 'Some checks failed.',
395
+ ].join('\n'));
396
+ return report.ok ? code : EXIT.CHECKS_FAILED;
397
+ }
398
+ // ------------------------------------------------------------------
399
+ case 'auth status': {
400
+ noPositionals();
401
+ const creds = await credentials();
402
+ const status = {
403
+ profile: creds.profile,
404
+ apiUrl: creds.apiUrl,
405
+ urlSource: creds.urlSource,
406
+ keySource: creds.keySource,
407
+ keyPrefix: creds.apiKey ? `${creds.apiKey.slice(0, 8)}…` : null,
408
+ verified: null,
409
+ company: null,
410
+ };
411
+ if (!creds.apiKey) {
412
+ throw new InboxyError({
413
+ kind: 'config',
414
+ code: 'API_KEY_MISSING',
415
+ message: `Not signed in (profile "${creds.profile}"). Run \`inboxy auth login\` or set INBOXY_API_KEY.`,
416
+ });
417
+ }
418
+ if (!opts.offline) {
419
+ const { data } = await clientFrom(creds).getCompany();
420
+ status.verified = true;
421
+ status.company = { id: data.company.id, name: data.company.name };
422
+ }
423
+ return out.success(status, undefined, () => [
424
+ `Profile ${creds.profile}`,
425
+ `API ${creds.apiUrl} (from ${creds.urlSource})`,
426
+ `Key ${status.keyPrefix} (from ${creds.keySource})`,
427
+ status.company
428
+ ? `Verified ${status.company.name} accepts this key`
429
+ : 'Verified skipped (--offline)',
430
+ ].join('\n'));
431
+ }
432
+ case 'auth login': {
433
+ noPositionals();
434
+ const profileName = profile ?? io.env.INBOXY_PROFILE ?? 'default';
435
+ validateProfileName(profileName);
436
+ const storeOpt = opts.store;
437
+ if (storeOpt !== undefined && storeOpt !== 'keychain' && storeOpt !== 'file') {
438
+ throw new UsageError('--store must be keychain or file.');
439
+ }
440
+ const apiUrl = normalizeBaseUrl(opts.url ?? io.env.INBOXY_API_URL ?? 'https://api.inboxy.chat');
441
+ const key = (await io.readSecret('Inboxy API key (input hidden): ')).trim();
442
+ activeKey = key;
443
+ if (!key)
444
+ throw new UsageError('No API key received on stdin.');
445
+ if (!looksLikeInboxyKey(key)) {
446
+ throw new InboxyError({
447
+ kind: 'config',
448
+ code: 'API_KEY_INVALID_FORMAT',
449
+ message: 'That does not look like an Inboxy API key (inboxy_ followed by 64 hex characters).',
450
+ });
451
+ }
452
+ let company = null;
453
+ if (!opts['no-verify']) {
454
+ const client = new InboxyClient({
455
+ apiKey: key,
456
+ baseUrl: apiUrl,
457
+ timeoutMs,
458
+ userAgent: `inboxy-cli/${CLI_VERSION}`,
459
+ });
460
+ const { data } = await client.getCompany();
461
+ company = { id: data.company.id, name: data.company.name };
462
+ }
463
+ const keychain = storeOpt === 'file' ? null : await keychainFor();
464
+ if (storeOpt === 'keychain' && !keychain) {
465
+ throw new InboxyError({
466
+ kind: 'config',
467
+ code: 'KEYCHAIN_UNAVAILABLE',
468
+ message: 'No OS keychain is available on this machine. Use --store file, or set INBOXY_API_KEY.',
469
+ });
470
+ }
471
+ const loaded = await loadConfig(env);
472
+ const config = loaded.config;
473
+ if (keychain) {
474
+ await keychain.set(profileName, key);
475
+ config.profiles[profileName] = { apiUrl, keyStorage: 'keychain' };
476
+ }
477
+ else {
478
+ config.profiles[profileName] = { apiUrl, keyStorage: 'file', apiKey: key };
479
+ }
480
+ if (!config.defaultProfile)
481
+ config.defaultProfile = profileName;
482
+ const file = await saveConfig(config, env);
483
+ const result = {
484
+ profile: profileName,
485
+ apiUrl,
486
+ keyStorage: keychain ? 'keychain' : 'file',
487
+ keychain: keychain?.name ?? null,
488
+ configPath: file,
489
+ verified: company !== null,
490
+ company,
491
+ };
492
+ if (!keychain)
493
+ out.note(`No OS keychain in use: the key is stored in ${file} (mode 600).`);
494
+ return out.success(result, undefined, () => `Saved profile "${profileName}" (${keychain ? keychain.name : `file ${file}`}).${company ? ` Company: ${company.name}.` : ' Not verified (--no-verify).'}`);
495
+ }
496
+ case 'auth logout': {
497
+ noPositionals();
498
+ const loaded = await loadConfig(env);
499
+ const profileName = profile ?? io.env.INBOXY_PROFILE ?? loaded.config.defaultProfile ?? 'default';
500
+ validateProfileName(profileName);
501
+ const keychain = await keychainFor();
502
+ let removedFromKeychain = false;
503
+ if (keychain)
504
+ removedFromKeychain = await keychain.delete(profileName).catch(() => false);
505
+ const had = profileName in loaded.config.profiles;
506
+ if (had) {
507
+ delete loaded.config.profiles[profileName];
508
+ if (loaded.config.defaultProfile === profileName)
509
+ delete loaded.config.defaultProfile;
510
+ await saveConfig(loaded.config, env);
511
+ }
512
+ const result = { profile: profileName, removedFromKeychain, removedFromConfig: had };
513
+ return out.success(result, undefined, () => had || removedFromKeychain
514
+ ? `Removed profile "${profileName}".`
515
+ : `Profile "${profileName}" was not stored.`);
516
+ }
517
+ }
518
+ throw new UsageError(`Unknown command "${name}".`);
519
+ }
520
+ catch (error) {
521
+ return out.failure(error);
522
+ }
523
+ }
524
+ async function listPages(fetchPage, query, opts) {
525
+ const maxPages = intOption(opts['max-pages'], 'max-pages', 1, 1000);
526
+ if (maxPages !== undefined && !opts.all)
527
+ throw new UsageError('--max-pages needs --all.');
528
+ if (!opts.all) {
529
+ const page = await fetchPage(query);
530
+ return { items: page.data, meta: { ...page.meta, pages: 1 } };
531
+ }
532
+ const all = await collectPages(fetchPage, query, maxPages ?? 10);
533
+ return { items: all.items, meta: { ...all.meta, pages: all.pages } };
534
+ }
535
+ function pagingHint(meta) {
536
+ return meta.hasMore && meta.nextCursor ? `More results: --cursor ${meta.nextCursor}` : '';
537
+ }
538
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `inboxy doctor` — checks the local setup end to end and says what to fix.
3
+ * Every check is read-only. The key is never printed (only its source and the
4
+ * same short prefix the dashboard shows).
5
+ */
6
+ import { type ResolvedCredentials } from '@inboxychat/sdk';
7
+ export type CheckStatus = 'pass' | 'warn' | 'fail' | 'skip';
8
+ export interface Check {
9
+ id: string;
10
+ status: CheckStatus;
11
+ message: string;
12
+ }
13
+ export interface DoctorReport {
14
+ ok: boolean;
15
+ profile: string;
16
+ apiUrl: string;
17
+ checks: Check[];
18
+ }
19
+ /**
20
+ * Scopes the CLI's read commands use, derived from the capability each
21
+ * command is an adapter of (UX26-F): scope → the commands that need it.
22
+ */
23
+ export declare const CLI_SCOPES: Record<string, string>;
24
+ export declare const SKEW_WARN_SECONDS = 60;
25
+ export declare const SKEW_FAIL_SECONDS = 300;
26
+ export declare function runDoctor(creds: ResolvedCredentials, options: {
27
+ env: NodeJS.ProcessEnv;
28
+ timeoutMs?: number;
29
+ now: () => number;
30
+ credentialError?: string;
31
+ }): Promise<DoctorReport>;
32
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAUL,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQxC,CAAC;AAEP,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAGrC,wBAAsB,SAAS,CAC7B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE;IACP,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,YAAY,CAAC,CAwIvB"}
package/dist/doctor.js ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * `inboxy doctor` — checks the local setup end to end and says what to fix.
3
+ * Every check is read-only. The key is never printed (only its source and the
4
+ * same short prefix the dashboard shows).
5
+ */
6
+ import { InboxyClient, InboxyError, describeError, isModeTooOpen, loadConfig, looksLikeInboxyKey, normalizeBaseUrl, PUBLIC_CAPABILITIES, } from '@inboxychat/sdk';
7
+ /**
8
+ * Scopes the CLI's read commands use, derived from the capability each
9
+ * command is an adapter of (UX26-F): scope → the commands that need it.
10
+ */
11
+ export const CLI_SCOPES = PUBLIC_CAPABILITIES.reduce((acc, capability) => {
12
+ if (!capability.cliCommand || !capability.scope)
13
+ return acc;
14
+ acc[capability.scope] = acc[capability.scope]
15
+ ? `${acc[capability.scope]}, ${capability.cliCommand}`
16
+ : capability.cliCommand;
17
+ return acc;
18
+ }, {});
19
+ export const SKEW_WARN_SECONDS = 60;
20
+ export const SKEW_FAIL_SECONDS = 300;
21
+ const EXPIRY_WARN_DAYS = 7;
22
+ export async function runDoctor(creds, options) {
23
+ const checks = [];
24
+ const add = (id, status, message) => checks.push({ id, status, message });
25
+ // 1. Config file
26
+ const loaded = await loadConfig(options.env);
27
+ if (!loaded.exists)
28
+ add('config-file', 'pass', `No config file (${loaded.path}); using environment/defaults.`);
29
+ else if (isModeTooOpen(loaded.mode)) {
30
+ const holdsKey = Object.values(loaded.config.profiles).some((p) => !!p.apiKey);
31
+ add('config-file', holdsKey ? 'fail' : 'warn', `${loaded.path} is readable by other users (mode ${(loaded.mode ?? 0).toString(8)}). Run: chmod 600 "${loaded.path}"`);
32
+ }
33
+ else
34
+ add('config-file', 'pass', `${loaded.path} (mode ${loaded.mode === null ? 'n/a' : loaded.mode.toString(8)})`);
35
+ // 2. API URL
36
+ let urlOk = true;
37
+ try {
38
+ const url = normalizeBaseUrl(creds.apiUrl);
39
+ add('api-url', 'pass', `${url} (from ${creds.urlSource})`);
40
+ }
41
+ catch (error) {
42
+ urlOk = false;
43
+ add('api-url', 'fail', error instanceof InboxyError ? error.message : 'Invalid API URL.');
44
+ }
45
+ // 3. Key present + format
46
+ if (options.credentialError) {
47
+ add('api-key', 'fail', options.credentialError);
48
+ }
49
+ else if (!creds.apiKey) {
50
+ add('api-key', 'fail', 'No API key. Set INBOXY_API_KEY or run `inboxy auth login`.');
51
+ }
52
+ else {
53
+ add('api-key', 'pass', `Found (from ${creds.keySource}, starts ${creds.apiKey.slice(0, 8)}…).`);
54
+ add('key-format', looksLikeInboxyKey(creds.apiKey) ? 'pass' : 'warn', looksLikeInboxyKey(creds.apiKey)
55
+ ? 'Looks like an Inboxy key (inboxy_ + 64 hex).'
56
+ : 'Does not look like an Inboxy key (expected inboxy_ followed by 64 hex characters).');
57
+ }
58
+ for (const w of creds.warnings)
59
+ add('keychain', 'warn', w);
60
+ // 4. Reach the API with the key
61
+ let company = null;
62
+ if (!creds.apiKey || !urlOk) {
63
+ add('api-reachable', 'skip', 'Skipped: no key or invalid URL.');
64
+ }
65
+ else {
66
+ const client = new InboxyClient({
67
+ apiKey: creds.apiKey,
68
+ baseUrl: creds.apiUrl,
69
+ timeoutMs: options.timeoutMs,
70
+ });
71
+ const started = options.now();
72
+ try {
73
+ const { body, info } = await client.request('/company');
74
+ company = body.data;
75
+ add('api-reachable', 'pass', `Inboxy answered in ${info.latencyMs} ms.`);
76
+ add('key-valid', 'pass', `Key "${company.token.name}" belongs to ${company.company.name} (${company.company.id}).`);
77
+ // Clock skew: server Date (1 s resolution) vs the request's midpoint.
78
+ if (info.date) {
79
+ const server = Date.parse(info.date);
80
+ const local = started + info.latencyMs / 2;
81
+ const skew = Math.round((local - server) / 1000);
82
+ const abs = Math.abs(skew);
83
+ const status = abs > SKEW_FAIL_SECONDS ? 'fail' : abs > SKEW_WARN_SECONDS ? 'warn' : 'pass';
84
+ add('clock-skew', status, abs <= 1
85
+ ? 'Local clock matches the server.'
86
+ : `Local clock is ${abs} s ${skew > 0 ? 'ahead of' : 'behind'} the server${status === 'pass' ? '.' : '. Enable automatic time sync (NTP).'}`);
87
+ }
88
+ else
89
+ add('clock-skew', 'skip', 'The server sent no Date header.');
90
+ }
91
+ catch (error) {
92
+ const err = error instanceof InboxyError ? error : null;
93
+ if (err && (err.kind === 'network' || err.kind === 'timeout')) {
94
+ add('api-reachable', 'fail', describeError(err));
95
+ }
96
+ else {
97
+ add('api-reachable', 'pass', 'Inboxy answered.');
98
+ add('key-valid', 'fail', err ? `${err.code}: ${describeError(err)}` : 'Unexpected error.');
99
+ }
100
+ }
101
+ }
102
+ // 5. Scopes and expiry (need a valid key)
103
+ if (company) {
104
+ const scopes = new Set(company.token.scopes);
105
+ const missing = Object.entries(CLI_SCOPES).filter(([s]) => !scopes.has(s));
106
+ add('scopes', missing.length ? 'warn' : 'pass', missing.length
107
+ ? `Missing ${missing.map(([s, cmds]) => `${s} (${cmds})`).join(', ')}. Key scopes: ${[...scopes].join(', ') || 'none'}.`
108
+ : `Key has ${[...scopes].join(', ')}.`);
109
+ const expiresAt = company.token.expiresAt ? Date.parse(company.token.expiresAt) : null;
110
+ if (expiresAt === null)
111
+ add('key-expiry', 'pass', 'The key does not expire.');
112
+ else {
113
+ const days = (expiresAt - options.now()) / 86_400_000;
114
+ add('key-expiry', days <= 0 ? 'fail' : days <= EXPIRY_WARN_DAYS ? 'warn' : 'pass', days <= 0
115
+ ? 'The key has expired.'
116
+ : `The key expires ${company.token.expiresAt} (in ${Math.ceil(days)} day${Math.ceil(days) === 1 ? '' : 's'}).`);
117
+ }
118
+ add('rate-limits', 'pass', `${company.rateLimits.perKey.limit}/${company.rateLimits.perKey.windowSeconds}s per key, ${company.rateLimits.perCompany.limit}/${company.rateLimits.perCompany.windowSeconds}s per company.`);
119
+ }
120
+ return {
121
+ ok: !checks.some((c) => c.status === 'fail'),
122
+ profile: creds.profile,
123
+ apiUrl: creds.apiUrl,
124
+ checks,
125
+ };
126
+ }
127
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GAGpB,MAAM,iBAAiB,CAAC;AAiBzB;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAA2B,mBAAmB,CAAC,MAAM,CAE1E,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;IACpB,IAAI,CAAC,UAAU,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC;IAC5D,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;QAC3C,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,UAAU,EAAE;QACtD,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC,EAAE,EAAE,CAAC,CAAC;AAEP,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AACpC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AACrC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAA0B,EAC1B,OAKC;IAED,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,CAAC,EAAU,EAAE,MAAmB,EAAE,OAAe,EAAE,EAAE,CAC/D,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAEvC,iBAAiB;IACjB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,MAAM;QAChB,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,mBAAmB,MAAM,CAAC,IAAI,gCAAgC,CAAC,CAAC;SACxF,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/E,GAAG,CACD,aAAa,EACb,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAC1B,GAAG,MAAM,CAAC,IAAI,qCAAqC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,sBAAsB,MAAM,CAAC,IAAI,GAAG,CACtH,CAAC;IACJ,CAAC;;QACC,GAAG,CACD,aAAa,EACb,MAAM,EACN,GAAG,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAClF,CAAC;IAEJ,aAAa;IACb,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3C,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,UAAU,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,KAAK,GAAG,KAAK,CAAC;QACd,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC5F,CAAC;IAED,0BAA0B;IAC1B,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QAC5B,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,4DAA4D,CAAC,CAAC;IACvF,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC,SAAS,YAAY,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QAChG,GAAG,CACD,YAAY,EACZ,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAClD,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC;YAC9B,CAAC,CAAC,8CAA8C;YAChD,CAAC,CAAC,oFAAoF,CACzF,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ;QAAE,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAE3D,gCAAgC;IAChC,IAAI,OAAO,GAA0B,IAAI,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QAC5B,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,iCAAiC,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;YAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,MAAM;YACrB,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAA2B,UAAU,CAAC,CAAC;YAClF,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;YACpB,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,sBAAsB,IAAI,CAAC,SAAS,MAAM,CAAC,CAAC;YACzE,GAAG,CACD,WAAW,EACX,MAAM,EACN,QAAQ,OAAO,CAAC,KAAK,CAAC,IAAI,gBAAgB,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,CAC1F,CAAC;YAEF,sEAAsE;YACtE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrC,MAAM,KAAK,GAAG,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;gBAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;gBACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC3B,MAAM,MAAM,GACV,GAAG,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC/E,GAAG,CACD,YAAY,EACZ,MAAM,EACN,GAAG,IAAI,CAAC;oBACN,CAAC,CAAC,iCAAiC;oBACnC,CAAC,CAAC,kBAAkB,GAAG,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,cACzD,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,qCAC5B,EAAE,CACP,CAAC;YACJ,CAAC;;gBAAM,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,iCAAiC,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YACxD,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;gBAC9D,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,eAAe,EAAE,MAAM,EAAE,kBAAkB,CAAC,CAAC;gBACjD,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;YAC7F,CAAC;QACH,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,GAAG,CACD,QAAQ,EACR,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAChC,OAAO,CAAC,MAAM;YACZ,CAAC,CAAC,WAAW,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG;YACxH,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACzC,CAAC;QACF,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACvF,IAAI,SAAS,KAAK,IAAI;YAAE,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,0BAA0B,CAAC,CAAC;aACzE,CAAC;YACJ,MAAM,IAAI,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,UAAU,CAAC;YACtD,GAAG,CACD,YAAY,EACZ,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAC/D,IAAI,IAAI,CAAC;gBACP,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,mBAAmB,OAAO,CAAC,KAAK,CAAC,SAAS,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CACjH,CAAC;QACJ,CAAC;QACD,GAAG,CACD,aAAa,EACb,MAAM,EACN,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,cAAc,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,aAAa,gBAAgB,CAC9L,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC;QAC5C,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM;KACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Output contract of the `inboxy` CLI.
3
+ *
4
+ * --json prints exactly one JSON document on stdout (schemaVersion 1):
5
+ * success: { schemaVersion: 1, ok: true, command, data, meta? }
6
+ * failure: { schemaVersion: 1, ok: false, command, error: { kind, code, message, status, details, retryAfterSeconds } }
7
+ * Fields may be added within schemaVersion 1; none is renamed or removed.
8
+ *
9
+ * Exit codes are stable and documented in the README.
10
+ */
11
+ import { type InboxyErrorKind } from '@inboxychat/sdk';
12
+ export declare const JSON_SCHEMA_VERSION = 1;
13
+ export declare const EXIT: {
14
+ readonly OK: 0;
15
+ readonly UNEXPECTED: 1;
16
+ readonly USAGE: 2;
17
+ readonly CONFIG: 3;
18
+ readonly AUTH: 4;
19
+ readonly FORBIDDEN: 5;
20
+ readonly NOT_FOUND: 6;
21
+ readonly RATE_LIMITED: 7;
22
+ readonly NETWORK: 8;
23
+ readonly SERVER: 9;
24
+ readonly CHECKS_FAILED: 10;
25
+ };
26
+ export declare function exitCodeFor(kind: InboxyErrorKind): number;
27
+ export declare class UsageError extends Error {
28
+ constructor(message: string);
29
+ }
30
+ export interface CliIO {
31
+ stdout(text: string): void;
32
+ stderr(text: string): void;
33
+ /** Reads the API key for `auth login` (piped stdin, or a hidden prompt on a TTY). */
34
+ readSecret(prompt: string): Promise<string>;
35
+ env: NodeJS.ProcessEnv;
36
+ now(): number;
37
+ }
38
+ export declare class Output {
39
+ private readonly io;
40
+ private readonly json;
41
+ private readonly command;
42
+ /** Secrets that must never reach the terminal (the key in use). */
43
+ private readonly secrets;
44
+ constructor(io: CliIO, json: boolean, command: string,
45
+ /** Secrets that must never reach the terminal (the key in use). */
46
+ secrets: () => string[]);
47
+ private clean;
48
+ /** Success. `human` renders the non-JSON view. */
49
+ success(data: unknown, meta: unknown, human: () => string): number;
50
+ failure(error: unknown): number;
51
+ /** Progress or hints for humans; suppressed in --json mode. */
52
+ note(text: string): void;
53
+ }
54
+ /** A plain aligned table; cells are flattened to one line. */
55
+ export declare function table(headers: string[], rows: Array<Array<string | number | null | undefined>>): string;
56
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAA6C,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElG,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,eAAO,MAAM,IAAI;;;;;;;;;;;;CAYP,CAAC;AAEX,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAwBzD;AAED,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qFAAqF;IACrF,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,IAAI,MAAM,CAAC;CACf;AAED,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAJP,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,MAAM;IAChC,mEAAmE;IAClD,OAAO,EAAE,MAAM,MAAM,EAAE;IAG1C,OAAO,CAAC,KAAK;IAIb,kDAAkD;IAClD,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,GAAG,MAAM;IAiBlE,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAqD/B,+DAA+D;IAC/D,IAAI,CAAC,IAAI,EAAE,MAAM;CAGlB;AAED,8DAA8D;AAC9D,wBAAgB,KAAK,CACnB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACrD,MAAM,CAiBR"}
package/dist/output.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Output contract of the `inboxy` CLI.
3
+ *
4
+ * --json prints exactly one JSON document on stdout (schemaVersion 1):
5
+ * success: { schemaVersion: 1, ok: true, command, data, meta? }
6
+ * failure: { schemaVersion: 1, ok: false, command, error: { kind, code, message, status, details, retryAfterSeconds } }
7
+ * Fields may be added within schemaVersion 1; none is renamed or removed.
8
+ *
9
+ * Exit codes are stable and documented in the README.
10
+ */
11
+ import { InboxyError, describeError, redactSecrets } from '@inboxychat/sdk';
12
+ export const JSON_SCHEMA_VERSION = 1;
13
+ export const EXIT = {
14
+ OK: 0,
15
+ UNEXPECTED: 1,
16
+ USAGE: 2,
17
+ CONFIG: 3,
18
+ AUTH: 4,
19
+ FORBIDDEN: 5,
20
+ NOT_FOUND: 6,
21
+ RATE_LIMITED: 7,
22
+ NETWORK: 8,
23
+ SERVER: 9,
24
+ CHECKS_FAILED: 10,
25
+ };
26
+ export function exitCodeFor(kind) {
27
+ switch (kind) {
28
+ case 'config':
29
+ return EXIT.CONFIG;
30
+ case 'auth':
31
+ return EXIT.AUTH;
32
+ case 'forbidden':
33
+ return EXIT.FORBIDDEN;
34
+ case 'not_found':
35
+ return EXIT.NOT_FOUND;
36
+ case 'rate_limited':
37
+ return EXIT.RATE_LIMITED;
38
+ case 'timeout':
39
+ case 'cancelled':
40
+ case 'network':
41
+ return EXIT.NETWORK;
42
+ case 'server':
43
+ case 'invalid_response':
44
+ return EXIT.SERVER;
45
+ case 'invalid_request':
46
+ return EXIT.USAGE;
47
+ default:
48
+ return EXIT.UNEXPECTED;
49
+ }
50
+ }
51
+ export class UsageError extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = 'UsageError';
55
+ }
56
+ }
57
+ export class Output {
58
+ io;
59
+ json;
60
+ command;
61
+ secrets;
62
+ constructor(io, json, command,
63
+ /** Secrets that must never reach the terminal (the key in use). */
64
+ secrets) {
65
+ this.io = io;
66
+ this.json = json;
67
+ this.command = command;
68
+ this.secrets = secrets;
69
+ }
70
+ clean(text) {
71
+ return redactSecrets(text, this.secrets());
72
+ }
73
+ /** Success. `human` renders the non-JSON view. */
74
+ success(data, meta, human) {
75
+ if (this.json) {
76
+ const doc = {
77
+ schemaVersion: JSON_SCHEMA_VERSION,
78
+ ok: true,
79
+ command: this.command,
80
+ data,
81
+ };
82
+ if (meta !== undefined)
83
+ doc.meta = meta;
84
+ this.io.stdout(`${this.clean(JSON.stringify(doc, null, 2))}\n`);
85
+ }
86
+ else {
87
+ const text = human();
88
+ if (text)
89
+ this.io.stdout(`${this.clean(text)}\n`);
90
+ }
91
+ return EXIT.OK;
92
+ }
93
+ failure(error) {
94
+ let payload;
95
+ let code;
96
+ let human;
97
+ if (error instanceof InboxyError) {
98
+ payload = { ...error.toJSON(), message: describeError(error) };
99
+ code = exitCodeFor(error.kind);
100
+ human = `Error (${error.code}): ${describeError(error)}`;
101
+ }
102
+ else if (error instanceof UsageError) {
103
+ payload = {
104
+ kind: 'usage',
105
+ code: 'USAGE',
106
+ message: error.message,
107
+ status: 0,
108
+ details: {},
109
+ retryAfterSeconds: null,
110
+ };
111
+ code = EXIT.USAGE;
112
+ human = `Error: ${error.message}\nRun \`inboxy --help\` for usage.`;
113
+ }
114
+ else {
115
+ const message = error instanceof Error ? error.message : 'Unexpected error';
116
+ payload = {
117
+ kind: 'unexpected',
118
+ code: 'UNEXPECTED',
119
+ message,
120
+ status: 0,
121
+ details: {},
122
+ retryAfterSeconds: null,
123
+ };
124
+ code = EXIT.UNEXPECTED;
125
+ human = `Error: ${message}`;
126
+ }
127
+ if (this.json) {
128
+ const doc = {
129
+ schemaVersion: JSON_SCHEMA_VERSION,
130
+ ok: false,
131
+ command: this.command,
132
+ error: payload,
133
+ };
134
+ this.io.stdout(`${this.clean(JSON.stringify(doc, null, 2))}\n`);
135
+ }
136
+ else {
137
+ this.io.stderr(`${this.clean(human)}\n`);
138
+ }
139
+ return code;
140
+ }
141
+ /** Progress or hints for humans; suppressed in --json mode. */
142
+ note(text) {
143
+ if (!this.json)
144
+ this.io.stderr(`${this.clean(text)}\n`);
145
+ }
146
+ }
147
+ /** A plain aligned table; cells are flattened to one line. */
148
+ export function table(headers, rows) {
149
+ const cells = rows.map((r) => r.map((c) => (c === null || c === undefined ? '-' : String(c).replace(/\s+/g, ' '))));
150
+ const widths = headers.map((h, i) => Math.min(48, Math.max(h.length, ...cells.map((r) => (r[i] ?? '').length))));
151
+ const fmt = (r) => r
152
+ .map((c, i) => {
153
+ const w = widths[i];
154
+ const v = c.length > w ? `${c.slice(0, w - 1)}…` : c;
155
+ return v.padEnd(w);
156
+ })
157
+ .join(' ')
158
+ .trimEnd();
159
+ return [fmt(headers), fmt(widths.map((w) => '-'.repeat(w))), ...cells.map(fmt)].join('\n');
160
+ }
161
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,36 @@
1
1
  {
2
2
  "name": "@inboxychat/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Inboxy command line: read-only account, orders, contacts and usage, plus doctor diagnostics.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "bin": {
8
+ "inboxy": "./dist/bin.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "!dist/.tsbuildinfo",
13
+ "!dist/**/*.tsbuildinfo"
14
+ ],
15
+ "engines": {
16
+ "node": ">=20.3.0"
17
+ },
18
+ "dependencies": {
19
+ "@inboxychat/sdk": "0.1.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.10.0",
23
+ "typescript": "^5.3.0",
24
+ "vitest": "^4.0.16"
25
+ },
26
+ "homepage": "https://inboxy.chat",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -b tsconfig.build.json",
32
+ "type-check": "tsc -p tsconfig.json --noEmit",
33
+ "test": "vitest run",
34
+ "clean": "rm -rf dist .turbo"
35
+ }
6
36
  }