oathbound 0.14.0 → 0.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oathbound",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Install verified Claude Code skills and agents from the Oath Bound registry",
5
5
  "license": "MIT",
6
6
  "author": "Josh Anderson",
@@ -18,43 +18,21 @@
18
18
  "security",
19
19
  "ai-tools"
20
20
  ],
21
- "type": "module",
22
21
  "bin": {
23
- "oathbound": "./bin/cli.cjs"
22
+ "oathbound": "./dist/cli.cjs"
24
23
  },
25
24
  "files": [
26
- "cli.ts",
27
- "ui.ts",
28
- "config.ts",
29
- "update.ts",
30
- "verify.ts",
31
- "content-hash.ts",
32
- "auth.ts",
33
- "push.ts",
34
- "search.ts",
35
- "agent-push.ts",
36
- "agent-search.ts",
37
- "semver.ts",
38
- "bin/cli.cjs",
39
- "install.cjs"
25
+ "dist/cli.cjs"
40
26
  ],
41
27
  "publishConfig": {
42
28
  "access": "public",
43
29
  "provenance": true
44
30
  },
45
31
  "scripts": {
46
- "build": "bun build ./cli.ts --compile --outfile dist/oathbound",
47
- "build:macos-arm64": "bun build ./cli.ts --compile --target=bun-darwin-arm64 --outfile dist/oathbound-macos-arm64",
48
- "build:macos-x64": "bun build ./cli.ts --compile --target=bun-darwin-x64 --outfile dist/oathbound-macos-x64",
49
- "build:linux-x64": "bun build ./cli.ts --compile --target=bun-linux-x64 --outfile dist/oathbound-linux-x64",
50
- "build:linux-arm64": "bun build ./cli.ts --compile --target=bun-linux-arm64 --outfile dist/oathbound-linux-arm64",
51
- "build:windows-x64": "bun build ./cli.ts --compile --target=bun-windows-x64 --outfile dist/oathbound-windows-x64",
52
- "build:windows-arm64": "bun build ./cli.ts --compile --target=bun-windows-arm64 --outfile dist/oathbound-windows-arm64",
53
- "build:all": "bun run build:macos-arm64 && bun run build:macos-x64 && bun run build:linux-x64 && bun run build:linux-arm64 && bun run build:windows-x64 && bun run build:windows-arm64",
54
- "test": "bun test",
55
- "postinstall": "node install.cjs"
32
+ "build": "bun build cli.ts --outfile=dist/cli.cjs --target=node --format=cjs && node -e \"const f='dist/cli.cjs';let s=require('fs').readFileSync(f,'utf8');s=s.replace('require.main == require.module','require.main === module');require('fs').writeFileSync(f,s)\" && printf '%s\\n' '#!/usr/bin/env node' | cat - dist/cli.cjs > dist/tmp && mv dist/tmp dist/cli.cjs",
33
+ "test": "bun test"
56
34
  },
57
- "dependencies": {
35
+ "devDependencies": {
58
36
  "@clack/prompts": "^1.1.0",
59
37
  "@supabase/supabase-js": "^2",
60
38
  "yaml": "^2.8.2"
package/agent-push.ts DELETED
@@ -1,170 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
- import { join, resolve } from 'node:path';
3
- import { intro, outro } from '@clack/prompts';
4
- import { parse as yamlParse } from 'yaml';
5
- import { BRAND, GREEN, DIM, BOLD, RESET, fail, spinner } from './ui';
6
- import { getAccessToken } from './auth';
7
- import { isValidSemver } from './semver';
8
-
9
- const API_BASE = process.env.OATHBOUND_API_URL ?? 'https://www.oathbound.ai';
10
-
11
- /** Parse YAML frontmatter from an agent .md file. */
12
- function parseAgentFrontmatter(content: string): {
13
- meta: Record<string, unknown>;
14
- body: string;
15
- } {
16
- const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
17
- if (!match) return { meta: {}, body: content };
18
- const parsed = yamlParse(match[1]);
19
- const meta: Record<string, unknown> =
20
- parsed && typeof parsed === 'object' ? parsed : {};
21
- return { meta, body: match[2] };
22
- }
23
-
24
- /** Resolve agent .md file path from user argument or auto-detect. */
25
- function resolveAgentFile(pathArg?: string): string {
26
- if (pathArg) {
27
- const resolved = resolve(pathArg);
28
- if (!existsSync(resolved)) {
29
- fail('File not found', resolved);
30
- }
31
- if (statSync(resolved).isDirectory()) {
32
- // Look for a single .md file with agent frontmatter in the directory
33
- return findAgentInDir(resolved);
34
- }
35
- if (!resolved.endsWith('.md')) {
36
- fail('Invalid file', 'Agent files must be .md files');
37
- }
38
- return resolved;
39
- }
40
-
41
- // No path — look in cwd for a single .md with agent frontmatter
42
- return findAgentInDir(process.cwd());
43
- }
44
-
45
- /** Find a single agent .md file in a directory. */
46
- function findAgentInDir(dir: string): string {
47
- const mdFiles = readdirSync(dir)
48
- .filter(f => f.endsWith('.md') && !f.startsWith('.'))
49
- .map(f => join(dir, f));
50
-
51
- const agents: string[] = [];
52
- for (const file of mdFiles) {
53
- try {
54
- const content = readFileSync(file, 'utf-8');
55
- const { meta, body } = parseAgentFrontmatter(content);
56
- if (meta.name && meta.description && body.trim()) {
57
- agents.push(file);
58
- }
59
- } catch {
60
- // Skip unreadable files
61
- }
62
- }
63
-
64
- if (agents.length === 0) {
65
- fail(
66
- 'No agent file found',
67
- 'Run from a directory with an agent .md file, or pass a path: oathbound agent push ./my-agent.md',
68
- );
69
- }
70
-
71
- if (agents.length > 1) {
72
- fail(
73
- 'Multiple agent files found',
74
- `Found ${agents.length} .md files with agent frontmatter. Specify which one: oathbound agent push ./file.md`,
75
- );
76
- }
77
-
78
- return agents[0];
79
- }
80
-
81
- export async function agentPush(pathArg?: string, options?: { private?: boolean }): Promise<void> {
82
- intro(BRAND);
83
-
84
- const agentFile = resolveAgentFile(pathArg);
85
- const content = readFileSync(agentFile, 'utf-8');
86
- const { meta, body } = parseAgentFrontmatter(content);
87
-
88
- // Validate required fields
89
- const name = String(meta.name ?? '');
90
- const description = String(meta.description ?? '');
91
- const license = String(meta.license ?? '');
92
- if (!name) fail('Frontmatter missing: name');
93
- if (!description) fail('Frontmatter missing: description');
94
- if (!license) fail('Frontmatter missing: license');
95
- if (!body.trim()) fail('No system prompt (markdown body) after frontmatter');
96
-
97
- const rawVersion = meta.version != null ? String(meta.version) : null;
98
- const version = rawVersion && isValidSemver(rawVersion) ? rawVersion : null;
99
-
100
- console.log(`${DIM} file: ${agentFile}${RESET}`);
101
- console.log(`${DIM} name: ${name}${RESET}`);
102
- console.log(`${DIM} version: ${version ?? 'auto (next)'}${RESET}`);
103
- console.log(`${DIM} license: ${license}${RESET}`);
104
- if (meta.model) console.log(`${DIM} model: ${meta.model}${RESET}`);
105
- if (meta.permissionMode) console.log(`${DIM} permissionMode: ${meta.permissionMode}${RESET}`);
106
-
107
- const visibility = options?.private ? 'private' : 'public';
108
- if (options?.private) {
109
- console.log(`${DIM} visibility: ${BOLD}private${RESET}`);
110
- }
111
-
112
- // Authenticate
113
- const token = await getAccessToken();
114
-
115
- // Build request body
116
- const requestBody: Record<string, unknown> = {
117
- name,
118
- description,
119
- license,
120
- version,
121
- systemPrompt: body,
122
- tools: meta.tools != null ? String(meta.tools) : null,
123
- disallowedTools: meta.disallowedTools != null ? String(meta.disallowedTools) : null,
124
- model: meta.model != null ? String(meta.model) : null,
125
- permissionMode: meta.permissionMode != null ? String(meta.permissionMode) : null,
126
- maxTurns: meta.maxTurns != null ? Number(meta.maxTurns) : null,
127
- memoryScope: meta.memory != null ? String(meta.memory) : null,
128
- background: meta.background != null ? Boolean(meta.background) : null,
129
- effort: meta.effort != null ? String(meta.effort) : null,
130
- isolation: meta.isolation != null ? String(meta.isolation) : null,
131
- config: {
132
- hooks: meta.hooks ?? null,
133
- mcpServers: meta.mcpServers ?? null,
134
- skillsRefs: Array.isArray(meta.skills) ? meta.skills : null,
135
- initialPrompt: meta.initialPrompt != null ? String(meta.initialPrompt) : null,
136
- },
137
- compatibility: meta.compatibility != null ? String(meta.compatibility) : null,
138
- originalAuthor: meta['original-author'] != null ? String(meta['original-author']) : null,
139
- visibility,
140
- };
141
-
142
- // Push to API
143
- const spin = spinner('Pushing...');
144
-
145
- const response = await fetch(`${API_BASE}/api/agents`, {
146
- method: 'POST',
147
- headers: {
148
- 'Content-Type': 'application/json',
149
- Authorization: `Bearer ${token}`,
150
- },
151
- body: JSON.stringify(requestBody),
152
- });
153
-
154
- spin.stop();
155
-
156
- if (!response.ok) {
157
- const resBody = await response.json().catch(() => ({ error: 'Unknown error' }));
158
- const details = Array.isArray(resBody.details)
159
- ? '\n' + resBody.details.map((d: string) => ` - ${d}`).join('\n')
160
- : '';
161
- fail(`Push failed (${response.status})`, `${resBody.error}${details}`);
162
- }
163
-
164
- const result = await response.json();
165
-
166
- outro(`${GREEN}✓ Published ${BOLD}${result.namespace}/${result.name}${RESET}${GREEN} v${result.version}${RESET}`);
167
- if (result.suiObjectId) {
168
- console.log(`${DIM} on-chain: ${result.suiObjectId}${RESET}`);
169
- }
170
- }
package/agent-search.ts DELETED
@@ -1,162 +0,0 @@
1
- import { BRAND, TEAL, GREEN, DIM, BOLD, RESET, fail, spinner } from './ui';
2
-
3
- const API_BASE = process.env.OATHBOUND_API_URL ?? 'https://www.oathbound.ai';
4
-
5
- export interface AgentSearchOptions {
6
- query?: string;
7
- namespace?: string;
8
- sparse?: boolean;
9
- sort?: 'downloads';
10
- limit?: number;
11
- offset?: number;
12
- }
13
-
14
- export function parseAgentSearchArgs(args: string[]): AgentSearchOptions {
15
- const opts: AgentSearchOptions = {};
16
- let i = 0;
17
-
18
- while (i < args.length) {
19
- const arg = args[i];
20
-
21
- if (arg === '--user' || arg === '-u') {
22
- opts.namespace = args[++i];
23
- } else if (arg === '--sparse' || arg === '-s') {
24
- opts.sparse = true;
25
- } else if (arg === '--sort') {
26
- const val = args[++i];
27
- if (val === 'downloads') opts.sort = 'downloads';
28
- } else if (arg === '--limit') {
29
- opts.limit = parseInt(args[++i], 10);
30
- } else if (arg === '--offset') {
31
- opts.offset = parseInt(args[++i], 10);
32
- } else if (!arg.startsWith('-')) {
33
- opts.query = arg;
34
- }
35
-
36
- i++;
37
- }
38
-
39
- return opts;
40
- }
41
-
42
- interface AgentAuthor {
43
- username: string;
44
- display_name: string | null;
45
- verified: boolean;
46
- }
47
-
48
- interface AgentResult {
49
- name: string;
50
- namespace: string;
51
- description: string;
52
- version: string;
53
- license?: string;
54
- visibility?: string;
55
- model?: string | null;
56
- tools?: string | null;
57
- permission_mode?: string | null;
58
- effort?: string | null;
59
- author?: AgentAuthor;
60
- download_count?: number;
61
- }
62
-
63
- interface AgentSearchResponse {
64
- ok: boolean;
65
- agents: AgentResult[];
66
- total: number;
67
- limit: number;
68
- offset: number;
69
- error?: string;
70
- }
71
-
72
- export async function agentSearch(opts: AgentSearchOptions): Promise<void> {
73
- const params = new URLSearchParams();
74
- if (opts.query) params.set('q', opts.query);
75
- if (opts.namespace) params.set('namespace', opts.namespace);
76
- if (opts.sparse) params.set('sparse', 'true');
77
- if (opts.sort) params.set('sort', opts.sort);
78
- if (opts.limit != null) params.set('limit', String(opts.limit));
79
- if (opts.offset != null) params.set('offset', String(opts.offset));
80
-
81
- const url = `${API_BASE}/api/agents?${params}`;
82
-
83
- const sp = spinner('Searching agents...');
84
-
85
- let res: Response;
86
- try {
87
- res = await fetch(url);
88
- } catch (err) {
89
- sp.stop();
90
- const msg = err instanceof Error ? err.message : 'Unknown error';
91
- fail('Search failed', msg);
92
- }
93
-
94
- sp.stop();
95
-
96
- if (!res.ok) {
97
- let detail = `HTTP ${res.status}`;
98
- try {
99
- const body = await res.json() as { error?: string };
100
- if (body.error) detail = body.error;
101
- } catch { /* ignore parse errors */ }
102
- fail('Search failed', detail);
103
- }
104
-
105
- const data = await res.json() as AgentSearchResponse;
106
-
107
- if (!data.ok || !data.agents) {
108
- fail('Search failed', data.error ?? 'Unexpected response');
109
- }
110
-
111
- const { agents, total, offset } = data;
112
-
113
- if (agents.length === 0) {
114
- console.log(`\n${BRAND} ${DIM}No agents found.${RESET}`);
115
- return;
116
- }
117
-
118
- const showing = offset > 0
119
- ? `Showing ${offset + 1}–${offset + agents.length} of ${total}`
120
- : `${total} agent${total === 1 ? '' : 's'} found`;
121
-
122
- console.log(`\n${BRAND} ${TEAL}${showing}${RESET}\n`);
123
-
124
- for (const agent of agents) {
125
- const id = `${agent.namespace}/${agent.name}`;
126
- const ver = `v${agent.version}`;
127
-
128
- // Line 1: name + version
129
- console.log(` ${BOLD}${id}${RESET} ${DIM}${ver}${RESET}`);
130
-
131
- // Line 2: description
132
- if (agent.description) {
133
- console.log(` ${DIM}${agent.description}${RESET}`);
134
- }
135
-
136
- // Line 3: agent-specific metadata (non-sparse only)
137
- if (!opts.sparse) {
138
- const parts: string[] = [];
139
- if (agent.author) {
140
- const name = agent.author.display_name || agent.author.username;
141
- parts.push(`by ${name}${agent.author.verified ? ' ✓' : ''}`);
142
- }
143
- if (agent.license) parts.push(agent.license);
144
- if (agent.download_count != null) parts.push(`↓ ${agent.download_count}`);
145
- if (agent.model) parts.push(`model: ${agent.model}`);
146
- if (agent.permission_mode) parts.push(`mode: ${agent.permission_mode}`);
147
- if (agent.effort) parts.push(`effort: ${agent.effort}`);
148
- if (agent.visibility === 'private') parts.push('private');
149
- if (parts.length > 0) {
150
- console.log(` ${DIM}${parts.join(' · ')}${RESET}`);
151
- }
152
- }
153
-
154
- console.log(); // blank line between agents
155
- }
156
-
157
- // Pagination hint
158
- if (offset + agents.length < total) {
159
- const nextOffset = offset + agents.length;
160
- console.log(`${DIM} Use --offset ${nextOffset} to see more${RESET}\n`);
161
- }
162
- }
package/auth.ts DELETED
@@ -1,219 +0,0 @@
1
- import { createClient } from '@supabase/supabase-js';
2
- import { spawn } from 'node:child_process';
3
- import {
4
- mkdirSync, writeFileSync, readFileSync, unlinkSync, existsSync,
5
- } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { homedir } from 'node:os';
8
- import { intro, outro } from '@clack/prompts';
9
- import { BRAND, GREEN, DIM, BOLD, RESET, fail, spinner } from './ui';
10
-
11
- const SUPABASE_URL = 'https://mjnfqagwuewhgwbtrdgs.supabase.co';
12
- const SUPABASE_ANON_KEY = 'sb_publishable_T-rk0azNRqAMLLGCyadyhQ_ulk9685n';
13
- const API_BASE = process.env.OATHBOUND_API_URL ?? 'https://www.oathbound.ai';
14
-
15
- const AUTH_DIR = join(homedir(), '.oathbound');
16
- const AUTH_FILE = join(AUTH_DIR, 'auth.json');
17
-
18
- interface StoredSession {
19
- access_token: string;
20
- refresh_token: string;
21
- expires_at: number;
22
- }
23
-
24
- function saveSession(session: StoredSession): void {
25
- mkdirSync(AUTH_DIR, { recursive: true, mode: 0o700 });
26
- writeFileSync(AUTH_FILE, JSON.stringify(session, null, 2), { mode: 0o600 });
27
- }
28
-
29
- function loadSession(): StoredSession | null {
30
- if (!existsSync(AUTH_FILE)) return null;
31
- try {
32
- return JSON.parse(readFileSync(AUTH_FILE, 'utf-8'));
33
- } catch {
34
- return null;
35
- }
36
- }
37
-
38
- function clearSession(): void {
39
- if (existsSync(AUTH_FILE)) unlinkSync(AUTH_FILE);
40
- }
41
-
42
- function openBrowser(url: string): void {
43
- const cmd = process.platform === 'darwin' ? 'open'
44
- : process.platform === 'win32' ? 'cmd'
45
- : 'xdg-open';
46
- const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
47
- try {
48
- spawn(cmd, args, { stdio: 'ignore', detached: true }).unref();
49
- } catch {
50
- // URL is already printed — user can open manually
51
- }
52
- }
53
-
54
- const SUCCESS_HTML = `<!DOCTYPE html>
55
- <html><head><title>Oathbound CLI</title></head>
56
- <body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5">
57
- <div style="text-align:center">
58
- <h1 style="color:#3fa8a4">&#10003; Logged in</h1>
59
- <p>You can close this tab and return to your terminal.</p>
60
- </div></body></html>`;
61
-
62
- const ERROR_HTML = `<!DOCTYPE html>
63
- <html><head><title>Oathbound CLI</title></head>
64
- <body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5">
65
- <div style="text-align:center">
66
- <h1 style="color:#ef4444">Login failed</h1>
67
- <p>Missing session tokens. Please try again.</p>
68
- </div></body></html>`;
69
-
70
- export async function login(): Promise<void> {
71
- intro(BRAND);
72
-
73
- let resolveSession: (s: StoredSession) => void;
74
- let rejectSession: (e: Error) => void;
75
- const sessionPromise = new Promise<StoredSession>((res, rej) => {
76
- resolveSession = res;
77
- rejectSession = rej;
78
- });
79
-
80
- const server = Bun.serve({
81
- port: 0,
82
- fetch(req) {
83
- const url = new URL(req.url);
84
- if (url.pathname !== '/callback') {
85
- return new Response('Not found', { status: 404 });
86
- }
87
-
88
- const accessToken = url.searchParams.get('access_token');
89
- const refreshToken = url.searchParams.get('refresh_token');
90
- const expiresAt = url.searchParams.get('expires_at');
91
-
92
- if (!accessToken || !refreshToken || !expiresAt) {
93
- rejectSession!(new Error('Missing session tokens from callback'));
94
- setTimeout(() => server.stop(), 500);
95
- return new Response(ERROR_HTML, { headers: { 'Content-Type': 'text/html' } });
96
- }
97
-
98
- resolveSession!({
99
- access_token: accessToken,
100
- refresh_token: refreshToken,
101
- expires_at: Number(expiresAt),
102
- });
103
-
104
- setTimeout(() => server.stop(), 500);
105
- return new Response(SUCCESS_HTML, { headers: { 'Content-Type': 'text/html' } });
106
- },
107
- });
108
-
109
- const port = server.port;
110
- const loginUrl = `${API_BASE}/cli-login?port=${port}`;
111
-
112
- console.log(`${DIM} Opening browser...${RESET}`);
113
- console.log(`${DIM} If it doesn't open, visit:${RESET}`);
114
- console.log(`${DIM} ${loginUrl}${RESET}\n`);
115
-
116
- openBrowser(loginUrl);
117
-
118
- const spin = spinner('Waiting for login...');
119
-
120
- const timeout = new Promise<never>((_, rej) =>
121
- setTimeout(() => rej(new Error('Login timed out (2 minutes). Please try again.')), 120_000),
122
- );
123
-
124
- let session: StoredSession;
125
- try {
126
- session = await Promise.race([sessionPromise, timeout]);
127
- } catch (err) {
128
- spin.stop();
129
- server.stop();
130
- fail('Login failed', err instanceof Error ? err.message : 'Unknown error');
131
- }
132
-
133
- spin.stop();
134
- saveSession(session);
135
-
136
- // Get username for display
137
- const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
138
- global: { headers: { Authorization: `Bearer ${session.access_token}` } },
139
- auth: { autoRefreshToken: false, persistSession: false },
140
- });
141
-
142
- const { data: { user } } = await supabase.auth.getUser();
143
- let displayName = user?.email ?? 'unknown';
144
- if (user) {
145
- const { data: userRecord } = await supabase
146
- .from('users')
147
- .select('username')
148
- .eq('user_id', user.id)
149
- .single();
150
- if (userRecord?.username) displayName = userRecord.username;
151
- }
152
-
153
- outro(`Logged in as ${BOLD}${displayName}${RESET}`);
154
- }
155
-
156
- export async function logout(): Promise<void> {
157
- clearSession();
158
- console.log(`\n${BRAND} ${GREEN}✓ Logged out${RESET}`);
159
- }
160
-
161
- export async function getAccessToken(): Promise<string> {
162
- const session = loadSession();
163
- if (!session) {
164
- fail('Not logged in', 'Run: oathbound login');
165
- }
166
-
167
- // Token still valid (with 60s buffer) — use it directly
168
- const now = Math.floor(Date.now() / 1000);
169
- if (session.expires_at > now + 60) {
170
- return session.access_token;
171
- }
172
-
173
- // Token expired or expiring soon — refresh
174
- const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
175
- auth: { autoRefreshToken: false, persistSession: false },
176
- });
177
-
178
- const { data, error } = await supabase.auth.setSession({
179
- access_token: session.access_token,
180
- refresh_token: session.refresh_token,
181
- });
182
-
183
- if (error || !data.session) {
184
- clearSession();
185
- fail('Session expired', 'Run: oathbound login');
186
- }
187
-
188
- saveSession({
189
- access_token: data.session.access_token,
190
- refresh_token: data.session.refresh_token,
191
- expires_at: data.session.expires_at!,
192
- });
193
-
194
- return data.session.access_token;
195
- }
196
-
197
- export async function whoami(): Promise<void> {
198
- const token = await getAccessToken();
199
-
200
- const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
201
- global: { headers: { Authorization: `Bearer ${token}` } },
202
- auth: { autoRefreshToken: false, persistSession: false },
203
- });
204
-
205
- const { data: { user }, error } = await supabase.auth.getUser();
206
- if (error || !user) {
207
- fail('Failed to get user', error?.message ?? 'Unknown error');
208
- }
209
-
210
- const { data: userRecord } = await supabase
211
- .from('users')
212
- .select('username')
213
- .eq('user_id', user.id)
214
- .single();
215
-
216
- console.log(`\n${BRAND}`);
217
- console.log(` ${BOLD}Username:${RESET} ${userRecord?.username ?? 'not set'}`);
218
- console.log(` ${DIM}Email:${RESET} ${user.email ?? 'unknown'}`);
219
- }
package/bin/cli.cjs DELETED
@@ -1,23 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // Thin wrapper that spawns the platform binary downloaded by postinstall.
4
-
5
- const { execFileSync } = require("child_process");
6
- const path = require("path");
7
- const fs = require("fs");
8
-
9
- const ext = process.platform === "win32" ? ".exe" : "";
10
- const binary = path.join(__dirname, `oathbound${ext}`);
11
-
12
- if (!fs.existsSync(binary)) {
13
- console.error(
14
- "oathbound binary not found. Run `npm rebuild oathbound` or reinstall the package."
15
- );
16
- process.exit(1);
17
- }
18
-
19
- try {
20
- execFileSync(binary, process.argv.slice(2), { stdio: "inherit" });
21
- } catch (err) {
22
- process.exit(err.status ?? 1);
23
- }