oathbound 0.13.1 → 0.14.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/agent-search.ts +7 -0
- package/cli.ts +37 -6
- package/package.json +1 -1
- package/search.ts +7 -0
package/agent-search.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface AgentSearchOptions {
|
|
|
6
6
|
query?: string;
|
|
7
7
|
namespace?: string;
|
|
8
8
|
sparse?: boolean;
|
|
9
|
+
sort?: 'downloads';
|
|
9
10
|
limit?: number;
|
|
10
11
|
offset?: number;
|
|
11
12
|
}
|
|
@@ -21,6 +22,9 @@ export function parseAgentSearchArgs(args: string[]): AgentSearchOptions {
|
|
|
21
22
|
opts.namespace = args[++i];
|
|
22
23
|
} else if (arg === '--sparse' || arg === '-s') {
|
|
23
24
|
opts.sparse = true;
|
|
25
|
+
} else if (arg === '--sort') {
|
|
26
|
+
const val = args[++i];
|
|
27
|
+
if (val === 'downloads') opts.sort = 'downloads';
|
|
24
28
|
} else if (arg === '--limit') {
|
|
25
29
|
opts.limit = parseInt(args[++i], 10);
|
|
26
30
|
} else if (arg === '--offset') {
|
|
@@ -53,6 +57,7 @@ interface AgentResult {
|
|
|
53
57
|
permission_mode?: string | null;
|
|
54
58
|
effort?: string | null;
|
|
55
59
|
author?: AgentAuthor;
|
|
60
|
+
download_count?: number;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
interface AgentSearchResponse {
|
|
@@ -69,6 +74,7 @@ export async function agentSearch(opts: AgentSearchOptions): Promise<void> {
|
|
|
69
74
|
if (opts.query) params.set('q', opts.query);
|
|
70
75
|
if (opts.namespace) params.set('namespace', opts.namespace);
|
|
71
76
|
if (opts.sparse) params.set('sparse', 'true');
|
|
77
|
+
if (opts.sort) params.set('sort', opts.sort);
|
|
72
78
|
if (opts.limit != null) params.set('limit', String(opts.limit));
|
|
73
79
|
if (opts.offset != null) params.set('offset', String(opts.offset));
|
|
74
80
|
|
|
@@ -135,6 +141,7 @@ export async function agentSearch(opts: AgentSearchOptions): Promise<void> {
|
|
|
135
141
|
parts.push(`by ${name}${agent.author.verified ? ' ✓' : ''}`);
|
|
136
142
|
}
|
|
137
143
|
if (agent.license) parts.push(agent.license);
|
|
144
|
+
if (agent.download_count != null) parts.push(`↓ ${agent.download_count}`);
|
|
138
145
|
if (agent.model) parts.push(`model: ${agent.model}`);
|
|
139
146
|
if (agent.permission_mode) parts.push(`mode: ${agent.permission_mode}`);
|
|
140
147
|
if (agent.effort) parts.push(`effort: ${agent.effort}`);
|
package/cli.ts
CHANGED
|
@@ -29,14 +29,16 @@ export { stripJsoncComments, writeOathboundConfig, mergeClaudeSettings, type Mer
|
|
|
29
29
|
export { isNewer } from './update';
|
|
30
30
|
export { installDevDependency, type InstallResult, setup, addPrepareScript, type PrepareResult, addTrustedDependency, type TrustedDepResult };
|
|
31
31
|
|
|
32
|
-
const VERSION = '0.
|
|
32
|
+
const VERSION = '0.14.0';
|
|
33
33
|
|
|
34
34
|
// --- Supabase ---
|
|
35
35
|
const SUPABASE_URL = 'https://mjnfqagwuewhgwbtrdgs.supabase.co';
|
|
36
36
|
const SUPABASE_ANON_KEY = 'sb_publishable_T-rk0azNRqAMLLGCyadyhQ_ulk9685n';
|
|
37
|
+
const API_BASE = process.env.OATHBOUND_API_URL ?? 'https://www.oathbound.ai';
|
|
37
38
|
|
|
38
39
|
// --- Types ---
|
|
39
40
|
interface SkillRow {
|
|
41
|
+
id: string;
|
|
40
42
|
name: string;
|
|
41
43
|
namespace: string;
|
|
42
44
|
version: string;
|
|
@@ -279,7 +281,7 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
279
281
|
if (version !== null) {
|
|
280
282
|
const { data, error } = await supabase
|
|
281
283
|
.from('skills')
|
|
282
|
-
.select('name, namespace, version, tar_hash, storage_path')
|
|
284
|
+
.select('id, name, namespace, version, tar_hash, storage_path')
|
|
283
285
|
.eq('namespace', namespace)
|
|
284
286
|
.eq('name', name)
|
|
285
287
|
.eq('version', version)
|
|
@@ -293,7 +295,7 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
293
295
|
// Fetch all versions, pick highest via semver comparison
|
|
294
296
|
const { data, error } = await supabase
|
|
295
297
|
.from('skills')
|
|
296
|
-
.select('name, namespace, version, tar_hash, storage_path')
|
|
298
|
+
.select('id, name, namespace, version, tar_hash, storage_path')
|
|
297
299
|
.eq('namespace', namespace)
|
|
298
300
|
.eq('name', name);
|
|
299
301
|
|
|
@@ -346,7 +348,21 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
346
348
|
}
|
|
347
349
|
unlinkSync(tarFile);
|
|
348
350
|
|
|
349
|
-
// 5.
|
|
351
|
+
// 5. Record download (non-fatal)
|
|
352
|
+
try {
|
|
353
|
+
const trackRes = await fetch(`${API_BASE}/api/downloads`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: { 'Content-Type': 'application/json' },
|
|
356
|
+
body: JSON.stringify({ skill_id: skill.id, version: skill.version }),
|
|
357
|
+
});
|
|
358
|
+
if (!trackRes.ok) {
|
|
359
|
+
process.stderr.write(`${DIM} [warn] download tracking failed (${trackRes.status})${RESET}\n`);
|
|
360
|
+
}
|
|
361
|
+
} catch {
|
|
362
|
+
// Network error — non-fatal
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 6. Success
|
|
350
366
|
console.log(`${BOLD}${GREEN} ✓ Skill verified${RESET}`);
|
|
351
367
|
console.log(`${DIM} ${fullName} v${skill.version}${RESET}`);
|
|
352
368
|
console.log(`${DIM} → ${join(skillsDir, name)}${RESET}`);
|
|
@@ -354,6 +370,7 @@ async function pull(skillArg: string): Promise<void> {
|
|
|
354
370
|
|
|
355
371
|
// --- Agent types ---
|
|
356
372
|
interface AgentRow {
|
|
373
|
+
id: string;
|
|
357
374
|
name: string;
|
|
358
375
|
namespace: string;
|
|
359
376
|
version: string;
|
|
@@ -379,7 +396,7 @@ async function agentPull(agentArg: string): Promise<void> {
|
|
|
379
396
|
if (version !== null) {
|
|
380
397
|
const { data, error } = await supabase
|
|
381
398
|
.from('agents')
|
|
382
|
-
.select('name, namespace, version, content_hash, storage_path, config')
|
|
399
|
+
.select('id, name, namespace, version, content_hash, storage_path, config')
|
|
383
400
|
.eq('namespace', namespace)
|
|
384
401
|
.eq('name', name)
|
|
385
402
|
.eq('version', version)
|
|
@@ -392,7 +409,7 @@ async function agentPull(agentArg: string): Promise<void> {
|
|
|
392
409
|
} else {
|
|
393
410
|
const { data, error } = await supabase
|
|
394
411
|
.from('agents')
|
|
395
|
-
.select('name, namespace, version, content_hash, storage_path, config')
|
|
412
|
+
.select('id, name, namespace, version, content_hash, storage_path, config')
|
|
396
413
|
.eq('namespace', namespace)
|
|
397
414
|
.eq('name', name);
|
|
398
415
|
|
|
@@ -466,6 +483,20 @@ async function agentPull(agentArg: string): Promise<void> {
|
|
|
466
483
|
// Write agent file
|
|
467
484
|
writeFileSync(targetPath, content);
|
|
468
485
|
|
|
486
|
+
// Record download (non-fatal)
|
|
487
|
+
try {
|
|
488
|
+
const trackRes = await fetch(`${API_BASE}/api/downloads`, {
|
|
489
|
+
method: 'POST',
|
|
490
|
+
headers: { 'Content-Type': 'application/json' },
|
|
491
|
+
body: JSON.stringify({ agent_id: agent.id, version: agent.version }),
|
|
492
|
+
});
|
|
493
|
+
if (!trackRes.ok) {
|
|
494
|
+
process.stderr.write(`${DIM} [warn] download tracking failed (${trackRes.status})${RESET}\n`);
|
|
495
|
+
}
|
|
496
|
+
} catch {
|
|
497
|
+
// Network error — non-fatal
|
|
498
|
+
}
|
|
499
|
+
|
|
469
500
|
console.log(`${BOLD}${GREEN} ✓ Agent verified${RESET}`);
|
|
470
501
|
console.log(`${DIM} ${fullName} v${agent.version}${RESET}`);
|
|
471
502
|
console.log(`${DIM} → ${targetPath}${RESET}`);
|
package/package.json
CHANGED
package/search.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface SearchOptions {
|
|
|
6
6
|
query?: string;
|
|
7
7
|
namespace?: string;
|
|
8
8
|
sparse?: boolean;
|
|
9
|
+
sort?: 'downloads';
|
|
9
10
|
limit?: number;
|
|
10
11
|
offset?: number;
|
|
11
12
|
}
|
|
@@ -21,6 +22,9 @@ export function parseSearchArgs(args: string[]): SearchOptions {
|
|
|
21
22
|
opts.namespace = args[++i];
|
|
22
23
|
} else if (arg === '--sparse' || arg === '-s') {
|
|
23
24
|
opts.sparse = true;
|
|
25
|
+
} else if (arg === '--sort') {
|
|
26
|
+
const val = args[++i];
|
|
27
|
+
if (val === 'downloads') opts.sort = 'downloads';
|
|
24
28
|
} else if (arg === '--limit') {
|
|
25
29
|
opts.limit = parseInt(args[++i], 10);
|
|
26
30
|
} else if (arg === '--offset') {
|
|
@@ -50,6 +54,7 @@ interface SkillResult {
|
|
|
50
54
|
visibility?: string;
|
|
51
55
|
author?: SkillAuthor;
|
|
52
56
|
audit_status?: 'passed' | 'failed' | 'none';
|
|
57
|
+
download_count?: number;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
interface SearchResponse {
|
|
@@ -66,6 +71,7 @@ export async function search(opts: SearchOptions): Promise<void> {
|
|
|
66
71
|
if (opts.query) params.set('q', opts.query);
|
|
67
72
|
if (opts.namespace) params.set('namespace', opts.namespace);
|
|
68
73
|
if (opts.sparse) params.set('sparse', 'true');
|
|
74
|
+
if (opts.sort) params.set('sort', opts.sort);
|
|
69
75
|
if (opts.limit != null) params.set('limit', String(opts.limit));
|
|
70
76
|
if (opts.offset != null) params.set('offset', String(opts.offset));
|
|
71
77
|
|
|
@@ -132,6 +138,7 @@ export async function search(opts: SearchOptions): Promise<void> {
|
|
|
132
138
|
parts.push(`by ${name}${skill.author.verified ? ' ✓' : ''}`);
|
|
133
139
|
}
|
|
134
140
|
if (skill.license) parts.push(skill.license);
|
|
141
|
+
if (skill.download_count != null) parts.push(`↓ ${skill.download_count}`);
|
|
135
142
|
if (skill.audit_status && skill.audit_status !== 'none') {
|
|
136
143
|
parts.push(skill.audit_status === 'passed' ? `${GREEN}audited${RESET}` : 'audit failed');
|
|
137
144
|
}
|