datavessel-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/dist/errors.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Error types and backend-error mapping.
3
+ *
4
+ * The backend returns errors as `{ error: { code, message, statusCode,
5
+ * details } }` (see execute.controller.ts). We translate the codes the MCP
6
+ * server already special-cases into actionable, human-readable guidance so the
7
+ * CLI behaves consistently with the rest of the product.
8
+ */
9
+ /** Process exit codes. Kept small and stable so scripts can branch on them. */
10
+ export const ExitCode = {
11
+ OK: 0,
12
+ ERROR: 1,
13
+ USAGE: 2,
14
+ AUTH: 3,
15
+ QUOTA: 4,
16
+ NOT_CONNECTED: 5,
17
+ };
18
+ export class CliError extends Error {
19
+ exitCode;
20
+ hint;
21
+ constructor(message, exitCode = ExitCode.ERROR, hint) {
22
+ super(message);
23
+ this.name = 'CliError';
24
+ this.exitCode = exitCode;
25
+ this.hint = hint;
26
+ }
27
+ }
28
+ /**
29
+ * Map a backend error payload (and/or HTTP status) to a CliError with the
30
+ * right exit code and a helpful hint. Mirrors the handling in the MCP server's
31
+ * `execute_tool` so users get the same guidance everywhere.
32
+ */
33
+ export function mapBackendError(status, body) {
34
+ const err = body?.error;
35
+ const code = err?.code;
36
+ const message = err?.message ?? `Backend request failed (HTTP ${status})`;
37
+ switch (code) {
38
+ case 'oauth_token_not_found':
39
+ return new CliError('No connection found for this provider.', ExitCode.NOT_CONNECTED, 'Connect your account in the web app first: https://app.datavessel.io/settings');
40
+ case 'oauth_token_refresh_failed':
41
+ return new CliError('Failed to refresh the OAuth token for this provider.', ExitCode.NOT_CONNECTED, 'Reconnect your account in the web app: https://app.datavessel.io/settings');
42
+ case 'rate_limit_exceeded': {
43
+ const d = err?.details ?? {};
44
+ const current = d.current ?? '?';
45
+ const limit = d.limit ?? '?';
46
+ const tier = d.tier ?? 'your current plan';
47
+ const end = d.billingPeriodEnd ? String(d.billingPeriodEnd) : '';
48
+ const lines = [
49
+ `API quota exceeded: ${current}/${limit} calls used this month on ${tier}.`,
50
+ 'To continue you can:',
51
+ ' 1. Upgrade your plan at https://app.datavessel.io/settings',
52
+ ' 2. Contact sales at contact@datavessel.io for enterprise options',
53
+ ];
54
+ if (end)
55
+ lines.push(` 3. Wait until your quota resets on ${end}`);
56
+ return new CliError(lines.join('\n'), ExitCode.QUOTA);
57
+ }
58
+ case 'unauthorized':
59
+ return new CliError('Authentication failed or session expired.', ExitCode.AUTH, 'Run `datavessel login` to authenticate again.');
60
+ }
61
+ // Fall back to HTTP status when no recognised code is present.
62
+ if (status === 401) {
63
+ return new CliError('Authentication failed or session expired.', ExitCode.AUTH, 'Run `datavessel login` to authenticate again.');
64
+ }
65
+ if (status === 429) {
66
+ return new CliError('API quota exceeded.', ExitCode.QUOTA, 'Upgrade your plan at https://app.datavessel.io/settings');
67
+ }
68
+ return new CliError(code ? `Backend error (${code}): ${message}` : message, ExitCode.ERROR);
69
+ }
70
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point. Parses argv and maps thrown errors to clean messages and
4
+ * stable exit codes (see errors.ts).
5
+ */
6
+ import { buildProgram } from './cli.js';
7
+ import { CliError } from './errors.js';
8
+ import { errorLine, c } from './output.js';
9
+ async function main() {
10
+ const program = buildProgram();
11
+ await program.parseAsync(process.argv);
12
+ }
13
+ main().catch((err) => {
14
+ if (err instanceof CliError) {
15
+ errorLine(err.message);
16
+ if (err.hint)
17
+ process.stderr.write(c.dim(' → ' + err.hint) + '\n');
18
+ process.exit(err.exitCode);
19
+ }
20
+ // Commander raises CommanderError for its own flows (help/version/parse).
21
+ // It carries an exitCode and has already written output; just honour it.
22
+ const maybe = err;
23
+ if (maybe && typeof maybe.exitCode === 'number' && maybe.code?.startsWith('commander.')) {
24
+ process.exit(maybe.exitCode);
25
+ }
26
+ errorLine(err instanceof Error ? err.message : String(err));
27
+ process.exit(1);
28
+ });
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAC/B,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvB,IAAI,GAAG,CAAC,IAAI;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,0EAA0E;IAC1E,yEAAyE;IACzE,MAAM,KAAK,GAAG,GAA6D,CAAC;IAC5E,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IACD,SAAS,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/oauth.js ADDED
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Browser login via a localhost loopback handoff.
3
+ *
4
+ * Flow:
5
+ * 1. Start an HTTP server on a random 127.0.0.1 port.
6
+ * 2. Open the web app's `/cli-auth?port=…&state=…` page.
7
+ * 3. The user signs in with the normal Google flow; the page then redirects
8
+ * the browser to `http://127.0.0.1:<port>/callback#<tokens>` where the
9
+ * hash carries the Supabase session (access/refresh tokens + the public
10
+ * supabase URL & anon key the CLI needs to self-refresh).
11
+ * 4. The loopback page posts the hash back to `/store`; we resolve it here.
12
+ *
13
+ * Tokens travel in the URL *fragment* (never sent to a server) and are then
14
+ * posted same-origin to the loopback, keeping them out of any logs. The random
15
+ * `state` is echoed back and verified to bind the response to this request.
16
+ */
17
+ import { createServer } from 'node:http';
18
+ import { randomUUID } from 'node:crypto';
19
+ import { spawn } from 'node:child_process';
20
+ import { CliError, ExitCode } from './errors.js';
21
+ import { info, c } from './output.js';
22
+ const LOOPBACK_HOST = '127.0.0.1';
23
+ const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
24
+ export function buildAuthUrl(appUrl, port, state) {
25
+ const base = appUrl.replace(/\/+$/, '');
26
+ const u = new URL(`${base}/cli-auth`);
27
+ u.searchParams.set('port', String(port));
28
+ u.searchParams.set('state', state);
29
+ return u.toString();
30
+ }
31
+ /** Validate and shape the handoff payload posted back by the loopback page. */
32
+ export function parseHandoff(data, expectedState) {
33
+ if (typeof data.state !== 'string' || data.state !== expectedState) {
34
+ throw new CliError('Login response failed state validation (possible CSRF).', ExitCode.AUTH);
35
+ }
36
+ const accessToken = str(data.access_token);
37
+ const refreshToken = str(data.refresh_token);
38
+ const supabaseUrl = str(data.supabase_url);
39
+ const anonKey = str(data.anon_key);
40
+ if (!accessToken || !refreshToken || !supabaseUrl || !anonKey) {
41
+ throw new CliError('Login response was missing required fields.', ExitCode.AUTH);
42
+ }
43
+ const expiresAt = Number(data.expires_at);
44
+ return {
45
+ type: 'oauth',
46
+ accessToken,
47
+ refreshToken,
48
+ supabaseUrl,
49
+ anonKey,
50
+ expiresAt: Number.isFinite(expiresAt)
51
+ ? expiresAt
52
+ : Math.floor(Date.now() / 1000) + 3600,
53
+ };
54
+ }
55
+ function str(v) {
56
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
57
+ }
58
+ const SUCCESS_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>datavessel CLI</title>
59
+ <style>body{font-family:system-ui,sans-serif;max-width:32rem;margin:18vh auto;text-align:center;color:#111}
60
+ .muted{color:#666}</style></head><body>
61
+ <h2 id="msg">Finishing sign-in…</h2><p class="muted" id="sub">One moment.</p>
62
+ <script>
63
+ (async () => {
64
+ const params = new URLSearchParams(location.hash.slice(1));
65
+ const payload = Object.fromEntries(params.entries());
66
+ try {
67
+ const r = await fetch('/store', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(payload) });
68
+ if (!r.ok) throw new Error(await r.text());
69
+ document.getElementById('msg').textContent = 'You are signed in ✓';
70
+ document.getElementById('sub').textContent = 'You can close this tab and return to the terminal.';
71
+ } catch (e) {
72
+ document.getElementById('msg').textContent = 'Sign-in failed';
73
+ document.getElementById('sub').textContent = String(e);
74
+ }
75
+ })();
76
+ </script></body></html>`;
77
+ function readBody(stream) {
78
+ return new Promise((resolve) => {
79
+ let d = '';
80
+ stream.on('data', (c2) => (d += c2));
81
+ stream.on('end', () => resolve(d));
82
+ });
83
+ }
84
+ function openBrowser(url) {
85
+ const platform = process.platform;
86
+ const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
87
+ const args = platform === 'win32' ? ['/c', 'start', '', url] : [url];
88
+ try {
89
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
90
+ child.on('error', () => {
91
+ /* fall back to printed URL */
92
+ });
93
+ child.unref();
94
+ }
95
+ catch {
96
+ /* ignore; URL is printed regardless */
97
+ }
98
+ }
99
+ /** Run the full browser handoff and resolve with the captured OAuth session. */
100
+ export function loginViaBrowser(opts) {
101
+ const state = randomUUID();
102
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
103
+ return new Promise((resolve, reject) => {
104
+ let settled = false;
105
+ const finish = (fn) => {
106
+ if (settled)
107
+ return;
108
+ settled = true;
109
+ clearTimeout(timer);
110
+ server.close();
111
+ fn();
112
+ };
113
+ const server = createServer(async (req, res) => {
114
+ const url = req.url ?? '/';
115
+ if (req.method === 'GET' && url.startsWith('/callback')) {
116
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
117
+ res.end(SUCCESS_HTML);
118
+ return;
119
+ }
120
+ if (req.method === 'POST' && url.startsWith('/store')) {
121
+ try {
122
+ const body = await readBody(req);
123
+ const data = JSON.parse(body || '{}');
124
+ const credential = parseHandoff(data, state);
125
+ res.writeHead(200, { 'Content-Type': 'application/json' });
126
+ res.end('{"ok":true}');
127
+ finish(() => resolve(credential));
128
+ }
129
+ catch (err) {
130
+ res.writeHead(400, { 'Content-Type': 'application/json' });
131
+ res.end(JSON.stringify({ ok: false }));
132
+ finish(() => reject(err));
133
+ }
134
+ return;
135
+ }
136
+ res.writeHead(404);
137
+ res.end();
138
+ });
139
+ const timer = setTimeout(() => {
140
+ finish(() => reject(new CliError('Timed out waiting for browser sign-in.', ExitCode.AUTH)));
141
+ }, timeoutMs);
142
+ server.on('error', (err) => finish(() => reject(new CliError(`Could not start local login server: ${err.message}`))));
143
+ server.listen(0, LOOPBACK_HOST, () => {
144
+ const { port } = server.address();
145
+ const authUrl = buildAuthUrl(opts.appUrl, port, state);
146
+ info(`Opening your browser to sign in…`);
147
+ info(`If it doesn't open, visit:\n ${c.cyan(authUrl)}\n`);
148
+ if (opts.open !== false)
149
+ openBrowser(authUrl);
150
+ });
151
+ });
152
+ }
153
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
package/dist/output.js ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Terminal output helpers: colours, tables, and structured printing.
3
+ *
4
+ * No runtime dependencies — a tiny ANSI helper that disables itself when output
5
+ * is not a TTY or when NO_COLOR is set (https://no-color.org).
6
+ */
7
+ const colorEnabled = process.env.NO_COLOR === undefined &&
8
+ process.env.DATAVESSEL_NO_COLOR === undefined &&
9
+ process.stdout.isTTY === true;
10
+ function wrap(code, close) {
11
+ return (s) => (colorEnabled ? `\x1b[${code}m${s}\x1b[${close}m` : s);
12
+ }
13
+ export const c = {
14
+ bold: wrap(1, 22),
15
+ dim: wrap(2, 22),
16
+ red: wrap(31, 39),
17
+ green: wrap(32, 39),
18
+ yellow: wrap(33, 39),
19
+ blue: wrap(34, 39),
20
+ cyan: wrap(36, 39),
21
+ gray: wrap(90, 39),
22
+ };
23
+ /** Visible width of a string, ignoring ANSI escape sequences. */
24
+ function visibleWidth(s) {
25
+ // eslint-disable-next-line no-control-regex
26
+ return s.replace(/\x1b\[[0-9;]*m/g, '').length;
27
+ }
28
+ function pad(s, width) {
29
+ const diff = width - visibleWidth(s);
30
+ return diff > 0 ? s + ' '.repeat(diff) : s;
31
+ }
32
+ /** Render a simple left-aligned columnar table with a dim header row. */
33
+ export function table(headers, rows) {
34
+ const widths = headers.map((h, i) => Math.max(visibleWidth(h), ...rows.map((r) => visibleWidth(r[i] ?? ''))));
35
+ const headerLine = headers.map((h, i) => c.dim(pad(h, widths[i]))).join(' ');
36
+ const body = rows.map((r) => r.map((cell, i) => pad(cell ?? '', widths[i])).join(' '));
37
+ return [headerLine, ...body].join('\n');
38
+ }
39
+ export function printJson(value) {
40
+ process.stdout.write(JSON.stringify(value, null, 2) + '\n');
41
+ }
42
+ export function info(msg) {
43
+ process.stderr.write(msg + '\n');
44
+ }
45
+ export function success(msg) {
46
+ process.stderr.write(c.green('✓ ') + msg + '\n');
47
+ }
48
+ export function warn(msg) {
49
+ process.stderr.write(c.yellow('! ') + msg + '\n');
50
+ }
51
+ export function errorLine(msg) {
52
+ process.stderr.write(c.red('✗ ') + msg + '\n');
53
+ }
54
+ //# 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/dist/prompt.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Minimal interactive prompts over stdin/stdout. Used only when the CLI needs
3
+ * input it wasn't given on the command line (e.g. pasting a token at login,
4
+ * confirming a write tool).
5
+ */
6
+ import { createInterface } from 'node:readline';
7
+ export function isInteractive() {
8
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
9
+ }
10
+ export async function prompt(question) {
11
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
12
+ try {
13
+ return await new Promise((resolve) => rl.question(question, resolve));
14
+ }
15
+ finally {
16
+ rl.close();
17
+ }
18
+ }
19
+ /** Read a line without echoing it back (best-effort, for secrets). */
20
+ export async function promptSecret(question) {
21
+ process.stderr.write(question);
22
+ const stdin = process.stdin;
23
+ const wasRaw = stdin.isRaw ?? false;
24
+ if (stdin.isTTY)
25
+ stdin.setRawMode(true);
26
+ return await new Promise((resolve) => {
27
+ let value = '';
28
+ const cleanup = () => {
29
+ stdin.removeListener('data', onData);
30
+ if (stdin.isTTY)
31
+ stdin.setRawMode(wasRaw);
32
+ stdin.pause();
33
+ };
34
+ const onData = (chunk) => {
35
+ for (const code of chunk) {
36
+ if (code === 0x0a || code === 0x0d) {
37
+ // Enter (LF / CR)
38
+ cleanup();
39
+ process.stderr.write('\n');
40
+ resolve(value);
41
+ return;
42
+ }
43
+ else if (code === 0x03) {
44
+ // Ctrl-C
45
+ cleanup();
46
+ process.stderr.write('\n');
47
+ process.exit(130);
48
+ }
49
+ else if (code === 0x7f || code === 0x08) {
50
+ // Backspace / Delete
51
+ value = value.slice(0, -1);
52
+ }
53
+ else if (code >= 0x20) {
54
+ value += String.fromCharCode(code);
55
+ }
56
+ }
57
+ };
58
+ stdin.resume();
59
+ stdin.on('data', onData);
60
+ });
61
+ }
62
+ export async function confirm(question, defaultYes = false) {
63
+ const suffix = defaultYes ? ' [Y/n] ' : ' [y/N] ';
64
+ const answer = (await prompt(question + suffix)).trim().toLowerCase();
65
+ if (answer === '')
66
+ return defaultYes;
67
+ return answer === 'y' || answer === 'yes';
68
+ }
69
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
package/dist/schema.js ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * JSON Schema → CLI flags.
3
+ *
4
+ * This is what makes the CLI self-updating: each tool's `inputSchema` (a JSON
5
+ * Schema object served by the backend) is turned into command-line options,
6
+ * help text, value coercion, and required-field validation. Adding a tool on
7
+ * the backend makes it appear here with no CLI code change.
8
+ */
9
+ import { Option } from 'commander';
10
+ import { CliError, ExitCode } from './errors.js';
11
+ import { c } from './output.js';
12
+ export function toKebab(name) {
13
+ return name
14
+ .replace(/_/g, '-')
15
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') // HTTPServer -> HTTP-Server
16
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2') // propertyId -> property-Id
17
+ .replace(/[^a-zA-Z0-9-]/g, '-')
18
+ .toLowerCase()
19
+ .replace(/-+/g, '-')
20
+ .replace(/^-|-$/g, '');
21
+ }
22
+ function toCamel(flag) {
23
+ return flag.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase());
24
+ }
25
+ /** Pick a single primary type from a schema's `type` (which may be an array). */
26
+ function primaryType(t) {
27
+ if (Array.isArray(t)) {
28
+ const real = t.find((x) => x !== 'null');
29
+ return (real ?? 'string');
30
+ }
31
+ return (t ?? 'string');
32
+ }
33
+ /** Turn an object input schema into an ordered list of parameter descriptors. */
34
+ export function schemaToParams(input) {
35
+ const props = input?.properties ?? {};
36
+ const required = new Set(input?.required ?? []);
37
+ return Object.entries(props).map(([prop, schema]) => {
38
+ const flag = toKebab(prop);
39
+ const type = primaryType(schema.type);
40
+ return {
41
+ prop,
42
+ flag,
43
+ attr: toCamel(flag),
44
+ type,
45
+ itemType: type === 'array' ? primaryType(schema.items?.type) : 'string',
46
+ required: required.has(prop),
47
+ description: schema.description ?? '',
48
+ enum: schema.enum,
49
+ default: schema.default,
50
+ };
51
+ });
52
+ }
53
+ /** Register each descriptor as a commander option on the given command. */
54
+ export function registerOptions(cmd, params) {
55
+ for (const p of params) {
56
+ const label = p.required ? `${p.description} ${c.dim('(required)')}` : p.description;
57
+ if (p.type === 'boolean') {
58
+ // Boolean flags get a negatable form so `--no-foo` can send false.
59
+ cmd.addOption(new Option(`--${p.flag}`, label || `Set ${p.prop}`));
60
+ cmd.addOption(new Option(`--no-${p.flag}`, `Set ${p.prop} to false`).hideHelp());
61
+ continue;
62
+ }
63
+ const placeholder = p.type === 'array' ? '<value...>' : `<${p.type}>`;
64
+ const opt = new Option(`--${p.flag} ${placeholder}`, label || `Set ${p.prop}`);
65
+ if (p.type === 'array') {
66
+ opt.argParser((val, prev = []) => [...prev, val]);
67
+ }
68
+ if (p.enum && p.type !== 'array')
69
+ opt.choices(p.enum.map(String));
70
+ cmd.addOption(opt);
71
+ }
72
+ }
73
+ function coerceScalar(raw, type, prop) {
74
+ switch (type) {
75
+ case 'integer': {
76
+ const n = Number(raw);
77
+ if (!Number.isInteger(n)) {
78
+ throw new CliError(`--${toKebab(prop)} must be an integer, got "${raw}"`, ExitCode.USAGE);
79
+ }
80
+ return n;
81
+ }
82
+ case 'number': {
83
+ const n = Number(raw);
84
+ if (Number.isNaN(n)) {
85
+ throw new CliError(`--${toKebab(prop)} must be a number, got "${raw}"`, ExitCode.USAGE);
86
+ }
87
+ return n;
88
+ }
89
+ case 'boolean':
90
+ return raw === 'true' || raw === '1';
91
+ case 'object':
92
+ case 'array':
93
+ try {
94
+ return JSON.parse(raw);
95
+ }
96
+ catch {
97
+ throw new CliError(`--${toKebab(prop)} must be valid JSON, got "${raw}"`, ExitCode.USAGE);
98
+ }
99
+ default:
100
+ return raw;
101
+ }
102
+ }
103
+ /**
104
+ * Collect coerced parameters from parsed commander options. `extra` holds
105
+ * values supplied via the generic escape hatches (`--param`, `--params-json`)
106
+ * and takes precedence so power users can always override.
107
+ */
108
+ export function collectParams(params, opts, extra = {}) {
109
+ const out = { ...extra };
110
+ for (const p of params) {
111
+ if (p.prop in out)
112
+ continue; // escape-hatch override wins
113
+ const raw = opts[p.attr];
114
+ if (raw === undefined)
115
+ continue;
116
+ if (p.type === 'boolean') {
117
+ out[p.prop] = Boolean(raw);
118
+ }
119
+ else if (p.type === 'array') {
120
+ const arr = Array.isArray(raw) ? raw : [raw];
121
+ out[p.prop] = arr.map((v) => coerceScalar(String(v), p.itemType, p.prop));
122
+ }
123
+ else {
124
+ out[p.prop] = coerceScalar(String(raw), p.type, p.prop);
125
+ }
126
+ }
127
+ const missing = params.filter((p) => p.required && !(p.prop in out)).map((p) => `--${p.flag}`);
128
+ if (missing.length > 0) {
129
+ throw new CliError(`Missing required option(s): ${missing.join(', ')}`, ExitCode.USAGE);
130
+ }
131
+ return out;
132
+ }
133
+ /** Human-readable description of a tool's inputs, for `tools show`. */
134
+ export function describeSchema(params) {
135
+ if (params.length === 0)
136
+ return c.dim(' (no parameters)');
137
+ return params
138
+ .map((p) => {
139
+ const type = p.type === 'array' ? `${p.itemType}[]` : p.type;
140
+ const req = p.required ? c.red('required') : c.dim('optional');
141
+ const head = ` ${c.cyan('--' + p.flag)} ${c.dim(type)} ${req}`;
142
+ const lines = [head];
143
+ if (p.description)
144
+ lines.push(` ${p.description}`);
145
+ if (p.enum)
146
+ lines.push(` ${c.dim('choices:')} ${p.enum.map(String).join(', ')}`);
147
+ if (p.default !== undefined) {
148
+ lines.push(` ${c.dim('default:')} ${JSON.stringify(p.default)}`);
149
+ }
150
+ return lines.join('\n');
151
+ })
152
+ .join('\n');
153
+ }
154
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Supabase session refresh.
3
+ *
4
+ * The web app authenticates with Supabase (the backend's authorization
5
+ * server). Access tokens are short-lived (~1h), so the CLI refreshes them on
6
+ * its own using the stored refresh token. `supabaseUrl`/`anonKey` are captured
7
+ * during login (both are public values) so no secrets are baked into the CLI.
8
+ */
9
+ import { CliError, ExitCode } from './errors.js';
10
+ /** True when the access token is expired or within `skewSec` of expiring. */
11
+ export function needsRefresh(cred, skewSec = 60) {
12
+ return Date.now() / 1000 >= cred.expiresAt - skewSec;
13
+ }
14
+ /**
15
+ * Exchange the refresh token for a fresh access token via Supabase GoTrue.
16
+ * Supabase rotates the refresh token, so the returned credential supersedes the
17
+ * old one and must be persisted.
18
+ */
19
+ export async function refreshOAuth(cred) {
20
+ const url = `${cred.supabaseUrl.replace(/\/+$/, '')}/auth/v1/token?grant_type=refresh_token`;
21
+ let res;
22
+ try {
23
+ res = await fetch(url, {
24
+ method: 'POST',
25
+ headers: {
26
+ apikey: cred.anonKey,
27
+ Authorization: `Bearer ${cred.anonKey}`,
28
+ 'Content-Type': 'application/json',
29
+ },
30
+ body: JSON.stringify({ refresh_token: cred.refreshToken }),
31
+ });
32
+ }
33
+ catch (err) {
34
+ const reason = err instanceof Error ? err.message : String(err);
35
+ throw new CliError(`Could not reach the auth server to refresh the session: ${reason}`);
36
+ }
37
+ if (!res.ok) {
38
+ throw new CliError('Your session has expired and could not be refreshed.', ExitCode.AUTH, 'Run `datavessel login` to sign in again.');
39
+ }
40
+ const body = (await res.json());
41
+ if (!body.access_token) {
42
+ throw new CliError('Auth server returned no access token on refresh.', ExitCode.AUTH);
43
+ }
44
+ const expiresAt = body.expires_at ?? Math.floor(Date.now() / 1000) + (body.expires_in ?? 3600);
45
+ return {
46
+ ...cred,
47
+ accessToken: body.access_token,
48
+ refreshToken: body.refresh_token ?? cred.refreshToken,
49
+ expiresAt,
50
+ };
51
+ }
52
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGjD,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,IAAqB,EAAE,OAAO,GAAG,EAAE;IAC9D,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC;AACvD,CAAC;AASD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAqB;IACtD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,yCAAyC,CAAC;IAC7F,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YACrB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,MAAM,EAAE,IAAI,CAAC,OAAO;gBACpB,aAAa,EAAE,UAAU,IAAI,CAAC,OAAO,EAAE;gBACvC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,IAAI,QAAQ,CAAC,2DAA2D,MAAM,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,QAAQ,CAChB,sDAAsD,EACtD,QAAQ,CAAC,IAAI,EACb,0CAA0C,CAC3C,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwB,CAAC;IACvD,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QACvB,MAAM,IAAI,QAAQ,CAAC,kDAAkD,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,SAAS,GACb,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;IAC/E,OAAO;QACL,GAAG,IAAI;QACP,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,YAAY,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,YAAY;QACrD,SAAS;KACV,CAAC;AACJ,CAAC"}