@chatlychat/cli 0.0.0-stage → 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chatly Engineering
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/args.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The argument parser.
3
+ *
4
+ * Hand-written rather than `commander` or `yargs`, because this repo does not
5
+ * add a dependency without approval and because the shape is small: a command
6
+ * path, then flags. 40 lines beats a 2MB tree.
7
+ */
8
+ export interface ParsedArgs {
9
+ /** Positional words before the first flag: `conversations send` → ['conversations','send']. */
10
+ readonly path: ReadonlyArray<string>;
11
+ /** `--key value`, `--key=value`, and `--flag` (true). Repeated keys collect into an array. */
12
+ readonly flags: Readonly<Record<string, string | boolean | string[]>>;
13
+ }
14
+ export declare function parseArgs(argv: ReadonlyArray<string>): ParsedArgs;
15
+ /** A flag as a string, or undefined. `--limit 5` → `'5'`. */
16
+ export declare function str(flags: ParsedArgs['flags'], name: string): string | undefined;
17
+ /** A flag as a number, or undefined. Rejects garbage rather than coercing it to NaN. */
18
+ export declare function num(flags: ParsedArgs['flags'], name: string): number | undefined;
19
+ /** A flag as a boolean. Present with no value, or `--flag=true`. */
20
+ export declare function bool(flags: ParsedArgs['flags'], name: string): boolean;
21
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,UAAU;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACrC,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;CACvE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,UAAU,CAoDjE;AAED,6DAA6D;AAC7D,wBAAgB,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAShF;AAED,wFAAwF;AACxF,wBAAgB,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUhF;AAED,oEAAoE;AACpE,wBAAgB,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAStE"}
package/dist/args.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The argument parser.
3
+ *
4
+ * Hand-written rather than `commander` or `yargs`, because this repo does not
5
+ * add a dependency without approval and because the shape is small: a command
6
+ * path, then flags. 40 lines beats a 2MB tree.
7
+ */
8
+ export function parseArgs(argv) {
9
+ const path = [];
10
+ const flags = {};
11
+ let i = 0;
12
+ for (; i < argv.length; i++) {
13
+ const token = argv[i];
14
+ if (token === undefined || token.startsWith('-')) {
15
+ break;
16
+ }
17
+ path.push(token);
18
+ }
19
+ for (; i < argv.length; i++) {
20
+ const token = argv[i];
21
+ if (token === undefined) {
22
+ continue;
23
+ }
24
+ if (!token.startsWith('-')) {
25
+ // A bare word after a flag: treat it as a positional, so
26
+ // `chatly search --limit 5 refunds` works as well as the other order.
27
+ path.push(token);
28
+ continue;
29
+ }
30
+ const bare = token.replace(/^-+/, '');
31
+ const eq = bare.indexOf('=');
32
+ let name;
33
+ let value;
34
+ if (eq > 0) {
35
+ name = bare.slice(0, eq);
36
+ value = bare.slice(eq + 1);
37
+ }
38
+ else {
39
+ name = bare;
40
+ const next = argv[i + 1];
41
+ if (next !== undefined && !next.startsWith('-')) {
42
+ value = next;
43
+ i += 1;
44
+ }
45
+ else {
46
+ value = true;
47
+ }
48
+ }
49
+ const existing = flags[name];
50
+ if (existing === undefined) {
51
+ flags[name] = value;
52
+ }
53
+ else if (Array.isArray(existing)) {
54
+ existing.push(String(value));
55
+ }
56
+ else {
57
+ flags[name] = [String(existing), String(value)];
58
+ }
59
+ }
60
+ return { path, flags };
61
+ }
62
+ /** A flag as a string, or undefined. `--limit 5` → `'5'`. */
63
+ export function str(flags, name) {
64
+ const v = flags[name];
65
+ if (typeof v === 'string') {
66
+ return v;
67
+ }
68
+ if (Array.isArray(v)) {
69
+ return v[v.length - 1];
70
+ }
71
+ return undefined;
72
+ }
73
+ /** A flag as a number, or undefined. Rejects garbage rather than coercing it to NaN. */
74
+ export function num(flags, name) {
75
+ const raw = str(flags, name);
76
+ if (raw === undefined) {
77
+ return undefined;
78
+ }
79
+ const n = Number(raw);
80
+ if (!Number.isFinite(n)) {
81
+ throw new Error(`--${name} expects a number, got "${raw}"`);
82
+ }
83
+ return n;
84
+ }
85
+ /** A flag as a boolean. Present with no value, or `--flag=true`. */
86
+ export function bool(flags, name) {
87
+ const v = flags[name];
88
+ if (v === true) {
89
+ return true;
90
+ }
91
+ if (typeof v === 'string') {
92
+ return v !== 'false' && v !== '0';
93
+ }
94
+ return false;
95
+ }
96
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.spec.d.ts","sourceRoot":"","sources":["../src/cli.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * The CLI, exercised end to end with only the socket faked.
3
+ *
4
+ * `run()` takes argv and a client factory, so these tests drive the real
5
+ * argument parser, the real `ChatlyClient`, the real transport, the real
6
+ * retry policy and the real error mapping. A test that stubbed the SDK would
7
+ * prove the CLI calls a mock, which is not the thing worth knowing.
8
+ */
9
+ import { mkdtempSync, readFileSync, statSync } from 'node:fs';
10
+ import { tmpdir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ import { ChatlyClient } from '@chatlychat/sdk-node';
13
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
14
+ import { bool, num, parseArgs, str } from './args.js';
15
+ import { resolveCredentials, writeConfig } from './config.js';
16
+ import { itemsOf, nextCursorOf } from './output.js';
17
+ import { explain, run } from './run.js';
18
+ describe('parseArgs', () => {
19
+ it('splits the command path from the flags', () => {
20
+ const a = parseArgs(['conversations', 'send', 'conv_1', '--json']);
21
+ expect(a.path).toEqual(['conversations', 'send', 'conv_1']);
22
+ expect(a.flags).toEqual({ json: true });
23
+ });
24
+ it('accepts --key=value and --key value alike', () => {
25
+ expect(str(parseArgs(['x', '--limit=5']).flags, 'limit')).toBe('5');
26
+ expect(str(parseArgs(['x', '--limit', '5']).flags, 'limit')).toBe('5');
27
+ });
28
+ it('collects a repeated flag into an array', () => {
29
+ const a = parseArgs(['events', 'track', '--prop', 'a=1', '--prop', 'b=2']);
30
+ expect(a.flags['prop']).toEqual(['a=1', 'b=2']);
31
+ });
32
+ it('keeps a positional that follows a flag', () => {
33
+ // `chatly search --limit 5 refunds` must work as well as the other order.
34
+ const a = parseArgs(['search', '--limit', '5', 'refunds']);
35
+ expect(a.path).toEqual(['search', 'refunds']);
36
+ expect(str(a.flags, 'limit')).toBe('5');
37
+ });
38
+ it('rejects a non-numeric --limit instead of silently passing NaN', () => {
39
+ expect(() => num(parseArgs(['x', '--limit', 'lots']).flags, 'limit')).toThrow(/expects a number/);
40
+ });
41
+ it('treats a trailing flag with no value as true', () => {
42
+ expect(bool(parseArgs(['x', '--all']).flags, 'all')).toBe(true);
43
+ });
44
+ });
45
+ describe('credential resolution', () => {
46
+ let dir;
47
+ let path;
48
+ beforeEach(() => {
49
+ dir = mkdtempSync(join(tmpdir(), 'chatly-cli-'));
50
+ path = join(dir, 'config.json');
51
+ });
52
+ const write = (c) => {
53
+ writeConfig(c, path);
54
+ };
55
+ it('prefers --key over the environment over the file', () => {
56
+ write({ apiKey: 'ck_file' });
57
+ const env = { CHATLY_API_KEY: 'ck_env' };
58
+ expect(resolveCredentials('ck_flag', undefined, env, path).source).toBe('flag');
59
+ expect(resolveCredentials(undefined, undefined, env, path).apiKey).toBe('ck_env');
60
+ expect(resolveCredentials(undefined, undefined, {}, path).apiKey).toBe('ck_file');
61
+ });
62
+ it('writes the config 0600, because the key is a workspace-wide bearer token', () => {
63
+ write({ apiKey: 'ck_secret' });
64
+ // eslint-disable-next-line no-bitwise -- reading the permission bits is the assertion
65
+ expect(statSync(path).mode & 0o777).toBe(0o600);
66
+ expect(JSON.parse(readFileSync(path, 'utf8')).apiKey).toBe('ck_secret');
67
+ });
68
+ it('re-chmods an existing file, since writeFileSync mode only applies on create', () => {
69
+ write({ apiKey: 'ck_one' });
70
+ write({ apiKey: 'ck_two' });
71
+ // eslint-disable-next-line no-bitwise -- as above
72
+ expect(statSync(path).mode & 0o777).toBe(0o600);
73
+ });
74
+ it('says what to do when there is no key at all', () => {
75
+ expect(() => resolveCredentials(undefined, undefined, {}, path)).toThrow(/chatly login/);
76
+ });
77
+ it('survives a corrupt config file rather than refusing to run', () => {
78
+ writeConfig({}, path);
79
+ const env = { CHATLY_API_KEY: 'ck_env' };
80
+ expect(resolveCredentials(undefined, undefined, env, path).apiKey).toBe('ck_env');
81
+ });
82
+ });
83
+ describe('output helpers', () => {
84
+ it('finds items under either envelope and nowhere else', () => {
85
+ expect(itemsOf({ items: [1, 2] })).toEqual([1, 2]);
86
+ expect(itemsOf({ data: [3] })).toEqual([3]);
87
+ expect(itemsOf([4])).toEqual([4]);
88
+ expect(itemsOf({ nope: 1 })).toEqual([]);
89
+ expect(itemsOf(null)).toEqual([]);
90
+ });
91
+ it('treats an empty cursor as no cursor', () => {
92
+ expect(nextCursorOf({ nextCursor: 'c' })).toBe('c');
93
+ expect(nextCursorOf({ nextCursor: '' })).toBeNull();
94
+ expect(nextCursorOf({ nextCursor: null })).toBeNull();
95
+ });
96
+ });
97
+ describe('run() against a faked socket', () => {
98
+ let out;
99
+ let err;
100
+ beforeEach(() => {
101
+ out = [];
102
+ err = [];
103
+ vi.spyOn(process.stdout, 'write').mockImplementation((chunk) => {
104
+ out.push(String(chunk));
105
+ return true;
106
+ });
107
+ vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => {
108
+ err.push(String(chunk));
109
+ return true;
110
+ });
111
+ });
112
+ afterEach(() => {
113
+ vi.restoreAllMocks();
114
+ });
115
+ /** A factory whose client talks to a scripted fetch, not the network. */
116
+ function factoryFor(responses, seen = []) {
117
+ let i = 0;
118
+ const fetchFn = (async (url, init) => {
119
+ seen.push({ url: String(url), method: init?.method ?? 'GET' });
120
+ const step = responses[Math.min(i, responses.length - 1)] ?? { status: 200, body: '{}' };
121
+ i += 1;
122
+ return new Response(step.body, {
123
+ status: step.status,
124
+ headers: { 'content-type': 'application/json' },
125
+ });
126
+ });
127
+ return (opts) => new ChatlyClient({ ...opts, fetchFn, sleepFn: async () => undefined, retry: { attempts: 1 } });
128
+ }
129
+ it('prints the version, not the help, for --version', async () => {
130
+ expect(await run(['--version'], factoryFor([]))).toBe(0);
131
+ expect(out.join('')).toBe('0.1.0\n');
132
+ });
133
+ it('whoami prints the workspace and scopes', async () => {
134
+ const code = await run(['whoami', '--key', 'ck_test'], factoryFor([{ status: 200, body: '{"workspaceId":"ws_1","scopes":["contacts:read"]}' }]));
135
+ expect(code).toBe(0);
136
+ expect(out.join('')).toContain('ws_1');
137
+ expect(out.join('')).toContain('contacts:read');
138
+ });
139
+ it('renders a list as a table, and the raw envelope with --json', async () => {
140
+ const body = '{"items":[{"id":"c_1","name":"Ada","email":"ada@x.test"}],"nextCursor":null}';
141
+ await run(['contacts', 'list', '--key', 'ck_test'], factoryFor([{ status: 200, body }]));
142
+ expect(out.join('')).toContain('ID');
143
+ expect(out.join('')).toContain('Ada');
144
+ out.length = 0;
145
+ await run(['contacts', 'list', '--key', 'ck_test', '--json'], factoryFor([{ status: 200, body }]));
146
+ expect(JSON.parse(out.join(''))).toEqual({
147
+ items: [{ id: 'c_1', name: 'Ada', email: 'ada@x.test' }],
148
+ nextCursor: null,
149
+ });
150
+ });
151
+ it('--all walks every page', async () => {
152
+ const seen = [];
153
+ await run(['contacts', 'list', '--key', 'ck_test', '--all', '--json'], factoryFor([
154
+ { status: 200, body: '{"items":[{"id":"a"}],"nextCursor":"p2"}' },
155
+ { status: 200, body: '{"items":[{"id":"b"}],"nextCursor":null}' },
156
+ ], seen));
157
+ expect(seen).toHaveLength(2);
158
+ expect(JSON.parse(out.join(''))).toEqual([{ id: 'a' }, { id: 'b' }]);
159
+ });
160
+ it('api passes an arbitrary route through, with the method it was given', async () => {
161
+ const seen = [];
162
+ await run(['api', 'POST', '/v1/feature-flags', '--key', 'ck_test', '--body', '{"key":"x"}'], factoryFor([{ status: 200, body: '{"ok":true}' }], seen));
163
+ expect(seen[0]?.method).toBe('POST');
164
+ expect(seen[0]?.url).toContain('/v1/feature-flags');
165
+ });
166
+ it('api refuses a body that is not JSON instead of sending it', async () => {
167
+ await expect(run(['api', 'POST', '/v1/x', '--key', 'ck_test', '--body', 'not json'], factoryFor([]))).rejects.toThrow(/not valid JSON/);
168
+ });
169
+ it('api refuses a path that is not a path', async () => {
170
+ await expect(run(['api', 'GET', 'v1/contacts', '--key', 'ck_test'], factoryFor([]))).rejects.toThrow(/Give a path/);
171
+ });
172
+ it('exits 2 and names the command for an unknown one', async () => {
173
+ expect(await run(['nonsense', '--key', 'ck_test'], factoryFor([]))).toBe(2);
174
+ expect(err.join('')).toContain('Unknown command "nonsense"');
175
+ });
176
+ it('refuses to send an empty reply', async () => {
177
+ await expect(run(['conversations', 'send', 'conv_1', '--key', 'ck_test'], factoryFor([]))).rejects.toThrow(/Nothing to send/);
178
+ });
179
+ });
180
+ describe('explain()', () => {
181
+ it('turns a 401 into advice about the key, not a stack trace', async () => {
182
+ const { ChatlyAuthenticationError } = await import('@chatlychat/sdk-node');
183
+ const e = new ChatlyAuthenticationError({
184
+ status: 401,
185
+ code: 'unauthorized',
186
+ message: 'bad key',
187
+ requestId: 'req_9',
188
+ body: '{}',
189
+ retryAfter: null,
190
+ });
191
+ const { code, message } = explain(e);
192
+ expect(code).toBe(1);
193
+ expect(message).toContain('req_9');
194
+ expect(message).toContain('chatly login');
195
+ });
196
+ it('distinguishes a missing scope (403) from a bad key (401)', async () => {
197
+ const { ChatlyPermissionError } = await import('@chatlychat/sdk-node');
198
+ const { message } = explain(new ChatlyPermissionError({
199
+ status: 403,
200
+ code: 'forbidden',
201
+ message: 'no',
202
+ requestId: null,
203
+ body: '{}',
204
+ retryAfter: null,
205
+ }));
206
+ expect(message).toContain('lacks a scope');
207
+ expect(message).not.toContain('chatly login');
208
+ });
209
+ it('says a transport failure never reached Chatly', async () => {
210
+ const { ChatlyTransportError } = await import('@chatlychat/sdk-node');
211
+ const { message } = explain(new ChatlyTransportError('request failed: GET /v1/x'));
212
+ expect(message).toContain('never reached Chatly');
213
+ });
214
+ });
215
+ //# sourceMappingURL=cli.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.spec.js","sourceRoot":"","sources":["../src/cli.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { ParsedArgs } from '../args.js';
2
+ import type { ChatlyClient } from '@chatlychat/sdk-node';
3
+ /**
4
+ * Store a key.
5
+ *
6
+ * Takes it from `--key` when given, otherwise prompts. The prompt reads from
7
+ * stdin with no echo suppression available in `readline/promises`, so it
8
+ * tells the reader that the key will be visible and offers the flag — better
9
+ * than pretending to hide it.
10
+ */
11
+ export declare function login(args: ParsedArgs): Promise<number>;
12
+ export declare function logout(): number;
13
+ /**
14
+ * The first call anyone should make.
15
+ *
16
+ * `GET /v1/api-keys/whoami` is the only api-keys route a key may call, and it
17
+ * turns a mis-scoped key into an obvious error here rather than a 403 from an
18
+ * unrelated endpoint three days into an integration.
19
+ */
20
+ export declare function whoami(chatly: ChatlyClient, json: boolean): Promise<number>;
21
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7D;AAED,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CASjF"}
@@ -0,0 +1,56 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import { CONFIG_PATH, readConfig, writeConfig } from '../config.js';
3
+ import { str } from '../args.js';
4
+ import { printJson } from '../output.js';
5
+ /**
6
+ * Store a key.
7
+ *
8
+ * Takes it from `--key` when given, otherwise prompts. The prompt reads from
9
+ * stdin with no echo suppression available in `readline/promises`, so it
10
+ * tells the reader that the key will be visible and offers the flag — better
11
+ * than pretending to hide it.
12
+ */
13
+ export async function login(args) {
14
+ let key = str(args.flags, 'key');
15
+ if (key === undefined) {
16
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
17
+ process.stdout.write('Paste a workspace API key (Settings → API keys). It will be visible as you type;\n' +
18
+ 'use `chatly login --key ck_…` from a shell with history disabled to avoid that.\n');
19
+ key = (await rl.question('API key: ')).trim();
20
+ rl.close();
21
+ }
22
+ if (!key.startsWith('ck_')) {
23
+ process.stderr.write(`That does not look like a workspace API key (expected a ck_ prefix, got "${key.slice(0, 6)}…").\n` +
24
+ 'A dashboard session token is not an API key — create one under Settings → API keys.\n');
25
+ return 1;
26
+ }
27
+ const existing = readConfig();
28
+ writeConfig({ ...existing, apiKey: key });
29
+ process.stdout.write(`Saved to ${CONFIG_PATH} (0600). Run \`chatly whoami\` to check it.\n`);
30
+ return 0;
31
+ }
32
+ export function logout() {
33
+ const existing = readConfig();
34
+ delete existing.apiKey;
35
+ writeConfig(existing);
36
+ process.stdout.write(`Removed the stored key from ${CONFIG_PATH}.\n`);
37
+ return 0;
38
+ }
39
+ /**
40
+ * The first call anyone should make.
41
+ *
42
+ * `GET /v1/api-keys/whoami` is the only api-keys route a key may call, and it
43
+ * turns a mis-scoped key into an obvious error here rather than a 403 from an
44
+ * unrelated endpoint three days into an integration.
45
+ */
46
+ export async function whoami(chatly, json) {
47
+ const me = await chatly.whoami();
48
+ if (json) {
49
+ printJson(me);
50
+ return 0;
51
+ }
52
+ process.stdout.write(`workspace ${me.workspaceId}\n`);
53
+ process.stdout.write(`scopes ${me.scopes.length === 0 ? '(none)' : me.scopes.join(', ')}\n`);
54
+ return 0;
55
+ }
56
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAgB;IAC1C,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oFAAoF;YAClF,mFAAmF,CACtF,CAAC;QACF,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4EAA4E,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ;YACjG,uFAAuF,CAC1F,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,WAAW,+CAA+C,CAAC,CAAC;IAC7F,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,MAAM;IACpB,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,OAAO,QAAQ,CAAC,MAAM,CAAC;IACvB,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,WAAW,KAAK,CAAC,CAAC;IACtE,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAoB,EAAE,IAAa;IAC9D,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACjC,IAAI,IAAI,EAAE,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjG,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { ChatlyClient } from '@chatlychat/sdk-node';
2
+ import { type ParsedArgs } from '../args.js';
3
+ export declare function conversations(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
4
+ export declare function contacts(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
5
+ export declare function tickets(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
6
+ export declare function search(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
7
+ export declare function kb(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
8
+ export declare function events(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
9
+ export declare function webhooks(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
10
+ export declare function reports(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
11
+ export declare function workspace(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
12
+ export declare function channels(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
13
+ export declare function billing(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
14
+ /**
15
+ * The raw passthrough — `gh api` for Chatly.
16
+ *
17
+ * 85 of the 140 key-accessible routes have no named command here, and adding
18
+ * one for each would be a worse CLI. This keeps every route reachable with
19
+ * the same auth, idempotency key, retry policy and typed errors.
20
+ */
21
+ export declare function api(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
22
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/commands/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA4E7D,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAkD3F;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDtF;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BrF;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAYpF;AAED,wBAAsB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBhF;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BpF;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBtF;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrF;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCvF;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAatF;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAQrF;AAED;;;;;;GAMG;AACH,wBAAsB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BjF"}