@orth/cli 0.2.2 → 0.2.4
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 +15 -3
- package/dist/commands/api.js +5 -11
- package/dist/commands/run.js +10 -7
- package/dist/commands/search.js +1 -4
- package/dist/commands/skills.d.ts +7 -0
- package/dist/commands/skills.js +168 -19
- package/dist/index.js +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,13 +53,17 @@ orth api show hunter /v2/domain-search
|
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
# GET request with query params
|
|
56
|
-
orth
|
|
56
|
+
orth run hunter /v2/domain-search -q domain=stripe.com
|
|
57
|
+
|
|
58
|
+
# Multiple query params (two ways)
|
|
59
|
+
orth run searchapi /api/v1/search -q 'engine=amazon_search&q=wireless earbuds'
|
|
60
|
+
orth run searchapi /api/v1/search -q engine=amazon_search -q q=wireless earbuds
|
|
57
61
|
|
|
58
62
|
# POST request with body
|
|
59
|
-
orth
|
|
63
|
+
orth run olostep /v1/scrapes -d '{"url": "https://stripe.com"}'
|
|
60
64
|
|
|
61
65
|
# Raw output for piping
|
|
62
|
-
orth
|
|
66
|
+
orth run hunter /v2/domain-search -q domain=stripe.com --raw | jq '.emails'
|
|
63
67
|
```
|
|
64
68
|
|
|
65
69
|
### Generate Code
|
|
@@ -134,6 +138,14 @@ orth skills create owner/repo --path skills/my-skill --ref main
|
|
|
134
138
|
orth skills submit ./my-skill
|
|
135
139
|
orth skills submit --name "My Skill" --tags "react,testing"
|
|
136
140
|
|
|
141
|
+
# Update local skill from platform (pull)
|
|
142
|
+
orth skills update owner/my-skill
|
|
143
|
+
orth skills update owner/my-skill ./local-dir --force
|
|
144
|
+
|
|
145
|
+
# Push local changes to platform
|
|
146
|
+
orth skills push owner/my-skill
|
|
147
|
+
orth skills push owner/my-skill ./local-dir
|
|
148
|
+
|
|
137
149
|
# Request verification (required before discoverability)
|
|
138
150
|
orth skills request-verification owner/my-skill
|
|
139
151
|
|
package/dist/commands/api.js
CHANGED
|
@@ -36,7 +36,7 @@ async function apiCommand(slug, path, options) {
|
|
|
36
36
|
chalk_1.default.white(api.name || "") +
|
|
37
37
|
chalk_1.default.gray(` (${api.endpoints?.length || 0} endpoints)`));
|
|
38
38
|
}
|
|
39
|
-
console.log(chalk_1.default.gray("\nRun '
|
|
39
|
+
console.log(chalk_1.default.gray("\nRun 'orth api show <slug>' to see endpoints for an API"));
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
if (path) {
|
|
@@ -47,11 +47,6 @@ async function apiCommand(slug, path, options) {
|
|
|
47
47
|
// Get description from endpoint object if available
|
|
48
48
|
const desc = data.description || data.endpoint?.description;
|
|
49
49
|
console.log(chalk_1.default.gray(desc || "No description"));
|
|
50
|
-
// Get price
|
|
51
|
-
const price = data.price || data.endpoint?.price;
|
|
52
|
-
if (price) {
|
|
53
|
-
console.log(chalk_1.default.green(`\nPrice: ${typeof price === 'number' ? '$' + price : price}`));
|
|
54
|
-
}
|
|
55
50
|
// Get params from nested endpoint object if needed
|
|
56
51
|
const queryParams = data.parameters?.query || data.endpoint?.queryParams || [];
|
|
57
52
|
const bodyParams = data.parameters?.body || data.endpoint?.bodyParams || [];
|
|
@@ -127,20 +122,19 @@ async function apiCommand(slug, path, options) {
|
|
|
127
122
|
const api = data.results.find((a) => a.slug === slug);
|
|
128
123
|
if (!api) {
|
|
129
124
|
console.log(chalk_1.default.yellow(`API '${slug}' not found.`));
|
|
130
|
-
console.log(chalk_1.default.gray("Run '
|
|
125
|
+
console.log(chalk_1.default.gray("Run 'orth api' to see available APIs"));
|
|
131
126
|
return;
|
|
132
127
|
}
|
|
133
128
|
console.log(chalk_1.default.bold(`\n${chalk_1.default.cyan(api.name)} (${api.slug})\n`));
|
|
134
129
|
for (const endpoint of api.endpoints) {
|
|
135
130
|
const method = chalk_1.default.yellow(endpoint.method.padEnd(6));
|
|
136
|
-
|
|
137
|
-
console.log(`${method} ${chalk_1.default.white(endpoint.path)} ${price}`);
|
|
131
|
+
console.log(`${method} ${chalk_1.default.white(endpoint.path)}`);
|
|
138
132
|
if (endpoint.description) {
|
|
139
133
|
console.log(chalk_1.default.gray(` ${endpoint.description.slice(0, 80)}${endpoint.description.length > 80 ? "..." : ""}`));
|
|
140
134
|
}
|
|
141
135
|
}
|
|
142
|
-
console.log(chalk_1.default.gray("\nRun '
|
|
143
|
-
console.log(chalk_1.default.gray("Run '
|
|
136
|
+
console.log(chalk_1.default.gray("\nRun 'orth api show " + slug + " <path>' for endpoint details"));
|
|
137
|
+
console.log(chalk_1.default.gray("Run 'orth run " + slug + " <path>' to call an endpoint"));
|
|
144
138
|
}
|
|
145
139
|
catch (error) {
|
|
146
140
|
spinner.stop();
|
package/dist/commands/run.js
CHANGED
|
@@ -11,12 +11,19 @@ async function runCommand(api, path, options) {
|
|
|
11
11
|
const spinner = (0, ora_1.default)(`Calling ${api}${path}...`).start();
|
|
12
12
|
try {
|
|
13
13
|
// Parse query params
|
|
14
|
+
// Supports both `-q key=value -q key2=value2` and `-q 'key=value&key2=value2'`
|
|
14
15
|
const query = {};
|
|
15
16
|
if (options.query) {
|
|
16
17
|
for (const param of options.query) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
// Split on & to handle URL-style query strings
|
|
19
|
+
const parts = param.split("&");
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
const eqIndex = part.indexOf("=");
|
|
22
|
+
if (eqIndex > 0) {
|
|
23
|
+
const key = part.slice(0, eqIndex);
|
|
24
|
+
const value = part.slice(eqIndex + 1);
|
|
25
|
+
query[key] = decodeURIComponent(value);
|
|
26
|
+
}
|
|
20
27
|
}
|
|
21
28
|
}
|
|
22
29
|
}
|
|
@@ -61,10 +68,6 @@ async function runCommand(api, path, options) {
|
|
|
61
68
|
console.log(JSON.stringify(result.data, null, 2));
|
|
62
69
|
}
|
|
63
70
|
else {
|
|
64
|
-
// Show cost
|
|
65
|
-
if (result.price) {
|
|
66
|
-
console.log(chalk_1.default.gray(`Cost: $${result.price}`));
|
|
67
|
-
}
|
|
68
71
|
// Pretty print the response
|
|
69
72
|
console.log(chalk_1.default.bold("\nResponse:\n"));
|
|
70
73
|
console.log(JSON.stringify(result.data, null, 2));
|
package/dist/commands/search.js
CHANGED
|
@@ -21,12 +21,9 @@ async function searchCommand(query, options) {
|
|
|
21
21
|
console.log(chalk_1.default.cyan.bold(`${api.name}`) + chalk_1.default.gray(` (${api.slug})`));
|
|
22
22
|
for (const endpoint of api.endpoints.slice(0, 3)) {
|
|
23
23
|
const method = endpoint.method.padEnd(6);
|
|
24
|
-
const price = endpoint.price ? chalk_1.default.green(`$${endpoint.price.toFixed(2)}`) : chalk_1.default.gray("free");
|
|
25
24
|
console.log(chalk_1.default.gray(" ") +
|
|
26
25
|
chalk_1.default.yellow(method) +
|
|
27
|
-
chalk_1.default.white(endpoint.path)
|
|
28
|
-
chalk_1.default.gray(" - ") +
|
|
29
|
-
price);
|
|
26
|
+
chalk_1.default.white(endpoint.path));
|
|
30
27
|
if (endpoint.description) {
|
|
31
28
|
console.log(chalk_1.default.gray(` ${endpoint.description.slice(0, 80)}${endpoint.description.length > 80 ? "..." : ""}`));
|
|
32
29
|
}
|
|
@@ -21,6 +21,13 @@ export declare function skillsSubmitCommand(inputPath: string | undefined, optio
|
|
|
21
21
|
name?: string;
|
|
22
22
|
tags?: string;
|
|
23
23
|
}): Promise<void>;
|
|
24
|
+
export declare function skillsPushCommand(slug: string, inputPath: string | undefined, options: {
|
|
25
|
+
name?: string;
|
|
26
|
+
tags?: string;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
export declare function skillsUpdateCommand(slug: string, inputPath: string | undefined, options: {
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
24
31
|
export declare function skillsRequestVerificationCommand(slug: string): Promise<void>;
|
|
25
32
|
export declare function skillsPublishCommand(slug: string, options: {
|
|
26
33
|
unpublish?: boolean;
|
package/dist/commands/skills.js
CHANGED
|
@@ -43,6 +43,8 @@ exports.skillsCreateCommand = skillsCreateCommand;
|
|
|
43
43
|
exports.skillsInstallCommand = skillsInstallCommand;
|
|
44
44
|
exports.skillsInitCommand = skillsInitCommand;
|
|
45
45
|
exports.skillsSubmitCommand = skillsSubmitCommand;
|
|
46
|
+
exports.skillsPushCommand = skillsPushCommand;
|
|
47
|
+
exports.skillsUpdateCommand = skillsUpdateCommand;
|
|
46
48
|
exports.skillsRequestVerificationCommand = skillsRequestVerificationCommand;
|
|
47
49
|
exports.skillsPublishCommand = skillsPublishCommand;
|
|
48
50
|
exports.skillsRequestCommand = skillsRequestCommand;
|
|
@@ -63,7 +65,7 @@ const AGENT_DIRS = {
|
|
|
63
65
|
openclaw: path.join(os.homedir(), ".openclaw", "skills"),
|
|
64
66
|
};
|
|
65
67
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
66
|
-
//
|
|
68
|
+
// orth skills list
|
|
67
69
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
68
70
|
async function skillsListCommand(options) {
|
|
69
71
|
const spinner = (0, ora_1.default)("Loading skills...").start();
|
|
@@ -93,8 +95,8 @@ async function skillsListCommand(options) {
|
|
|
93
95
|
console.log(chalk_1.default.gray(` Slug: ${skill.slug}`));
|
|
94
96
|
console.log();
|
|
95
97
|
}
|
|
96
|
-
console.log(chalk_1.default.gray("Run '
|
|
97
|
-
console.log(chalk_1.default.gray("Run '
|
|
98
|
+
console.log(chalk_1.default.gray("Run 'orth skills show <slug>' to see skill details"));
|
|
99
|
+
console.log(chalk_1.default.gray("Run 'orth skills add <slug>' to add a skill locally"));
|
|
98
100
|
}
|
|
99
101
|
catch (error) {
|
|
100
102
|
spinner.stop();
|
|
@@ -103,7 +105,7 @@ async function skillsListCommand(options) {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
106
|
-
//
|
|
108
|
+
// orth skills search <query>
|
|
107
109
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
108
110
|
async function skillsSearchCommand(query, options) {
|
|
109
111
|
const spinner = (0, ora_1.default)("Searching skills...").start();
|
|
@@ -128,7 +130,7 @@ async function skillsSearchCommand(query, options) {
|
|
|
128
130
|
console.log(chalk_1.default.gray(` Slug: ${skill.slug}`));
|
|
129
131
|
console.log();
|
|
130
132
|
}
|
|
131
|
-
console.log(chalk_1.default.gray("Run '
|
|
133
|
+
console.log(chalk_1.default.gray("Run 'orth skills show <slug>' for full details"));
|
|
132
134
|
}
|
|
133
135
|
catch (error) {
|
|
134
136
|
spinner.stop();
|
|
@@ -137,7 +139,7 @@ async function skillsSearchCommand(query, options) {
|
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
140
|
-
//
|
|
142
|
+
// orth skills show <slug>
|
|
141
143
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
142
144
|
async function skillsShowCommand(slug) {
|
|
143
145
|
const spinner = (0, ora_1.default)("Loading skill...").start();
|
|
@@ -191,7 +193,7 @@ async function skillsShowCommand(slug) {
|
|
|
191
193
|
console.log(chalk_1.default.bold("Install:"));
|
|
192
194
|
console.log(chalk_1.default.white(` ${skill.installCommand}`));
|
|
193
195
|
}
|
|
194
|
-
console.log(chalk_1.default.gray(`\nRun '
|
|
196
|
+
console.log(chalk_1.default.gray(`\nRun 'orth skills add ${skill.slug}' to add locally`));
|
|
195
197
|
}
|
|
196
198
|
catch (error) {
|
|
197
199
|
spinner.stop();
|
|
@@ -200,7 +202,7 @@ async function skillsShowCommand(slug) {
|
|
|
200
202
|
}
|
|
201
203
|
}
|
|
202
204
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
203
|
-
//
|
|
205
|
+
// orth skills create <githubRepo>
|
|
204
206
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
205
207
|
async function skillsCreateCommand(githubRepo, options) {
|
|
206
208
|
const spinner = (0, ora_1.default)("Creating skill from GitHub...").start();
|
|
@@ -242,7 +244,7 @@ async function skillsCreateCommand(githubRepo, options) {
|
|
|
242
244
|
console.log(chalk_1.default.gray(` ${data.skill.description}`));
|
|
243
245
|
}
|
|
244
246
|
console.log(chalk_1.default.bold("\nYour skill is on the platform but not yet verified."));
|
|
245
|
-
console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`
|
|
247
|
+
console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`orth skills request-verification ${data.skill.slug}`)}`));
|
|
246
248
|
console.log(chalk_1.default.gray("Once verified, you can toggle discoverability anytime."));
|
|
247
249
|
}
|
|
248
250
|
catch (error) {
|
|
@@ -252,7 +254,7 @@ async function skillsCreateCommand(githubRepo, options) {
|
|
|
252
254
|
}
|
|
253
255
|
}
|
|
254
256
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
255
|
-
//
|
|
257
|
+
// orth skills add <slug>
|
|
256
258
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
257
259
|
async function skillsInstallCommand(slug, options) {
|
|
258
260
|
const spinner = (0, ora_1.default)("Fetching skill...").start();
|
|
@@ -333,7 +335,7 @@ async function skillsInstallCommand(slug, options) {
|
|
|
333
335
|
}
|
|
334
336
|
}
|
|
335
337
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
336
|
-
//
|
|
338
|
+
// orth skills init [name]
|
|
337
339
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
338
340
|
// Known binary/large file extensions to skip when reading local files
|
|
339
341
|
const SKIP_EXTENSIONS = new Set([
|
|
@@ -419,7 +421,7 @@ Describe the next step.
|
|
|
419
421
|
console.log(chalk_1.default.bold("\nNext steps:"));
|
|
420
422
|
console.log(chalk_1.default.white(" 1. Edit SKILL.md with your skill's instructions"));
|
|
421
423
|
console.log(chalk_1.default.white(" 2. Add any supporting files to scripts/, references/, or assets/"));
|
|
422
|
-
console.log(chalk_1.default.white(` 3. Submit to Orthogonal: ${chalk_1.default.cyan(`
|
|
424
|
+
console.log(chalk_1.default.white(` 3. Submit to Orthogonal: ${chalk_1.default.cyan(`orth skills submit ${dirPath}`)}`));
|
|
423
425
|
}
|
|
424
426
|
catch (error) {
|
|
425
427
|
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
@@ -427,7 +429,7 @@ Describe the next step.
|
|
|
427
429
|
}
|
|
428
430
|
}
|
|
429
431
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
430
|
-
//
|
|
432
|
+
// orth skills submit [path]
|
|
431
433
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
432
434
|
function readFilesRecursive(dirPath, basePath = "") {
|
|
433
435
|
const files = [];
|
|
@@ -508,7 +510,7 @@ async function skillsSubmitCommand(inputPath, options) {
|
|
|
508
510
|
if (!primaryFile) {
|
|
509
511
|
spinner.stop();
|
|
510
512
|
console.error(chalk_1.default.red("Error: No SKILL.md found in the root of the directory"));
|
|
511
|
-
console.log(chalk_1.default.gray("Run '
|
|
513
|
+
console.log(chalk_1.default.gray("Run 'orth skills init' to create a skill template"));
|
|
512
514
|
process.exit(1);
|
|
513
515
|
}
|
|
514
516
|
// Parse frontmatter
|
|
@@ -565,7 +567,7 @@ async function skillsSubmitCommand(inputPath, options) {
|
|
|
565
567
|
console.log(chalk_1.default.gray(` ${data.skill.description.slice(0, 100)}`));
|
|
566
568
|
}
|
|
567
569
|
console.log(chalk_1.default.bold("\nYour skill is on the platform but not yet verified."));
|
|
568
|
-
console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`
|
|
570
|
+
console.log(chalk_1.default.white(`To request verification: ${chalk_1.default.cyan(`orth skills request-verification ${data.skill.slug}`)}`));
|
|
569
571
|
console.log(chalk_1.default.gray("Once verified, you can toggle discoverability anytime."));
|
|
570
572
|
console.log(chalk_1.default.gray(`Dashboard: https://orthogonal.com/dashboard/skills`));
|
|
571
573
|
}
|
|
@@ -576,7 +578,154 @@ async function skillsSubmitCommand(inputPath, options) {
|
|
|
576
578
|
}
|
|
577
579
|
}
|
|
578
580
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
579
|
-
//
|
|
581
|
+
// orth skills push <slug> [path]
|
|
582
|
+
// Push local skill files to remote (replaces remote with local)
|
|
583
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
584
|
+
async function skillsPushCommand(slug, inputPath, options) {
|
|
585
|
+
const dirPath = inputPath ? path.resolve(inputPath) : process.cwd();
|
|
586
|
+
const spinner = (0, ora_1.default)("Reading skill files...").start();
|
|
587
|
+
try {
|
|
588
|
+
// Validate directory exists
|
|
589
|
+
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) {
|
|
590
|
+
spinner.stop();
|
|
591
|
+
console.error(chalk_1.default.red(`Error: ${dirPath} is not a directory`));
|
|
592
|
+
process.exit(1);
|
|
593
|
+
}
|
|
594
|
+
// Read all files
|
|
595
|
+
const files = readFilesRecursive(dirPath);
|
|
596
|
+
if (files.length === 0) {
|
|
597
|
+
spinner.stop();
|
|
598
|
+
console.error(chalk_1.default.red("Error: No files found in the directory"));
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
// Validate SKILL.md exists
|
|
602
|
+
const primaryFile = files.find((f) => f.isPrimary);
|
|
603
|
+
if (!primaryFile) {
|
|
604
|
+
spinner.stop();
|
|
605
|
+
console.error(chalk_1.default.red("Error: No SKILL.md found in the root of the directory"));
|
|
606
|
+
process.exit(1);
|
|
607
|
+
}
|
|
608
|
+
// Parse frontmatter
|
|
609
|
+
// Note: Unlike submit, name/description are optional for updates
|
|
610
|
+
// We only send them if provided (to allow partial updates)
|
|
611
|
+
const frontmatter = parseFrontmatter(primaryFile.content);
|
|
612
|
+
const skillName = options.name || frontmatter.name;
|
|
613
|
+
const skillDescription = frontmatter.description;
|
|
614
|
+
// Check size limits
|
|
615
|
+
const totalSize = files.reduce((acc, f) => acc + f.content.length, 0);
|
|
616
|
+
if (files.length > 50) {
|
|
617
|
+
spinner.stop();
|
|
618
|
+
console.error(chalk_1.default.red("Error: Too many files (max 50)"));
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
if (totalSize > 1024 * 1024) {
|
|
622
|
+
spinner.stop();
|
|
623
|
+
console.error(chalk_1.default.red("Error: Total content too large (max 1MB)"));
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
spinner.text = "Pushing skill to Orthogonal...";
|
|
627
|
+
const tags = options.tags
|
|
628
|
+
? options.tags.split(",").map((t) => t.trim())
|
|
629
|
+
: undefined;
|
|
630
|
+
const updatePayload = {
|
|
631
|
+
files: files.map((f) => ({
|
|
632
|
+
filePath: f.filePath,
|
|
633
|
+
content: f.content,
|
|
634
|
+
isPrimary: f.isPrimary,
|
|
635
|
+
})),
|
|
636
|
+
};
|
|
637
|
+
if (skillName)
|
|
638
|
+
updatePayload.name = skillName;
|
|
639
|
+
if (skillDescription)
|
|
640
|
+
updatePayload.description = skillDescription;
|
|
641
|
+
if (tags)
|
|
642
|
+
updatePayload.tags = tags;
|
|
643
|
+
const data = await (0, api_js_1.apiRequest)(`/skills/${slug}`, {
|
|
644
|
+
method: "PUT",
|
|
645
|
+
body: updatePayload,
|
|
646
|
+
});
|
|
647
|
+
spinner.stop();
|
|
648
|
+
console.log(chalk_1.default.green(`\n✓ Skill pushed successfully`));
|
|
649
|
+
console.log(chalk_1.default.bold(`\n${data.skill.name}`));
|
|
650
|
+
console.log(chalk_1.default.gray(` Slug: ${data.skill.slug}`));
|
|
651
|
+
console.log(chalk_1.default.gray(` Files: ${files.length}`));
|
|
652
|
+
if (data.skill.description) {
|
|
653
|
+
console.log(chalk_1.default.gray(` ${data.skill.description.slice(0, 100)}`));
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
catch (error) {
|
|
657
|
+
spinner.stop();
|
|
658
|
+
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
663
|
+
// orth skills update <slug> [path]
|
|
664
|
+
// Pull latest version from Orthogonal and update local files
|
|
665
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
666
|
+
async function skillsUpdateCommand(slug, inputPath, options) {
|
|
667
|
+
const spinner = (0, ora_1.default)(`Fetching skill '${slug}' from Orthogonal...`).start();
|
|
668
|
+
try {
|
|
669
|
+
// Fetch skill from API
|
|
670
|
+
const data = await (0, api_js_1.apiRequest)(`/skills/${slug}`);
|
|
671
|
+
const skill = data.skill;
|
|
672
|
+
if (!skill.files || skill.files.length === 0) {
|
|
673
|
+
spinner.stop();
|
|
674
|
+
console.error(chalk_1.default.red("Error: Skill has no files to download"));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
// Determine target directory
|
|
678
|
+
const skillDirName = slug.replace(/\//g, "-");
|
|
679
|
+
const dirPath = inputPath
|
|
680
|
+
? path.resolve(inputPath)
|
|
681
|
+
: path.join(process.cwd(), skillDirName);
|
|
682
|
+
// Check if directory exists and has content
|
|
683
|
+
if (fs.existsSync(dirPath) && !options.force) {
|
|
684
|
+
const contents = fs.readdirSync(dirPath);
|
|
685
|
+
if (contents.length > 0) {
|
|
686
|
+
spinner.stop();
|
|
687
|
+
console.log(chalk_1.default.yellow(`Directory ${dirPath} already exists with content.`));
|
|
688
|
+
console.log(chalk_1.default.white(`Use ${chalk_1.default.cyan("--force")} to overwrite existing files.`));
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
spinner.text = "Writing files...";
|
|
693
|
+
// Create directory
|
|
694
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
695
|
+
// Write all files
|
|
696
|
+
for (const file of skill.files) {
|
|
697
|
+
// Sanitize file path to prevent path traversal
|
|
698
|
+
const sanitized = file.filePath.replace(/\.\.\//g, "").replace(/\.\.\\/g, "");
|
|
699
|
+
const filePath = path.resolve(dirPath, sanitized);
|
|
700
|
+
// Ensure resolved path is within dirPath
|
|
701
|
+
if (!filePath.startsWith(path.resolve(dirPath))) {
|
|
702
|
+
console.log(chalk_1.default.yellow(` Skipped unsafe file path: ${file.filePath}`));
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
const fileDir = path.dirname(filePath);
|
|
706
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
707
|
+
fs.writeFileSync(filePath, file.content, "utf-8");
|
|
708
|
+
}
|
|
709
|
+
spinner.stop();
|
|
710
|
+
console.log(chalk_1.default.green(`\n✓ Skill updated from Orthogonal`));
|
|
711
|
+
console.log(chalk_1.default.bold(`\n${skill.name}`));
|
|
712
|
+
console.log(chalk_1.default.gray(` Path: ${dirPath}`));
|
|
713
|
+
console.log(chalk_1.default.gray(` Files: ${skill.files.length}`));
|
|
714
|
+
console.log(chalk_1.default.bold("\nFiles written:"));
|
|
715
|
+
for (const file of skill.files) {
|
|
716
|
+
const primary = file.isPrimary ? chalk_1.default.green(" (primary)") : "";
|
|
717
|
+
console.log(chalk_1.default.gray(" ") + chalk_1.default.white(file.filePath) + primary);
|
|
718
|
+
}
|
|
719
|
+
console.log(chalk_1.default.gray(`\nTo push changes back: ${chalk_1.default.cyan(`orth skills push ${slug} ${dirPath}`)}`));
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
spinner.stop();
|
|
723
|
+
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
724
|
+
process.exit(1);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
728
|
+
// orth skills request-verification <slug>
|
|
580
729
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
581
730
|
async function skillsRequestVerificationCommand(slug) {
|
|
582
731
|
const spinner = (0, ora_1.default)("Submitting verification request...").start();
|
|
@@ -604,16 +753,16 @@ async function skillsRequestVerificationCommand(slug) {
|
|
|
604
753
|
}
|
|
605
754
|
}
|
|
606
755
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
607
|
-
//
|
|
756
|
+
// orth skills publish <slug> (deprecated - redirects to request-verification)
|
|
608
757
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
609
758
|
async function skillsPublishCommand(slug, options) {
|
|
610
759
|
console.log(chalk_1.default.yellow("Note: Direct publishing has been replaced with a verification workflow."));
|
|
611
|
-
console.log(chalk_1.default.white(`\nTo request your skill to be verified, run:\n ${chalk_1.default.cyan(`
|
|
760
|
+
console.log(chalk_1.default.white(`\nTo request your skill to be verified, run:\n ${chalk_1.default.cyan(`orth skills request-verification ${slug}`)}`));
|
|
612
761
|
console.log(chalk_1.default.white("Once verified, you can toggle discoverability from your dashboard."));
|
|
613
762
|
console.log(chalk_1.default.gray("\nOr manage from the dashboard: https://orthogonal.com/dashboard/skills"));
|
|
614
763
|
}
|
|
615
764
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
616
|
-
//
|
|
765
|
+
// orth skills request <input>
|
|
617
766
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
618
767
|
async function skillsRequestCommand(input) {
|
|
619
768
|
const spinner = (0, ora_1.default)("Submitting skill request...").start();
|
package/dist/index.js
CHANGED
|
@@ -175,6 +175,23 @@ skillsGroup
|
|
|
175
175
|
(0, analytics_js_1.trackEvent)("skills.submit", { path: inputPath });
|
|
176
176
|
await (0, skills_js_1.skillsSubmitCommand)(inputPath, options);
|
|
177
177
|
}));
|
|
178
|
+
skillsGroup
|
|
179
|
+
.command("update <slug> [path]")
|
|
180
|
+
.description("Pull latest skill version from Orthogonal to local directory")
|
|
181
|
+
.option("-f, --force", "Overwrite existing files")
|
|
182
|
+
.action(asyncAction(async (slug, inputPath, options) => {
|
|
183
|
+
(0, analytics_js_1.trackEvent)("skills.update", { slug, path: inputPath });
|
|
184
|
+
await (0, skills_js_1.skillsUpdateCommand)(slug, inputPath, options);
|
|
185
|
+
}));
|
|
186
|
+
skillsGroup
|
|
187
|
+
.command("push <slug> [path]")
|
|
188
|
+
.description("Push local skill files to Orthogonal (update remote)")
|
|
189
|
+
.option("-n, --name <name>", "Override skill name from frontmatter")
|
|
190
|
+
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
191
|
+
.action(asyncAction(async (slug, inputPath, options) => {
|
|
192
|
+
(0, analytics_js_1.trackEvent)("skills.push", { slug, path: inputPath });
|
|
193
|
+
await (0, skills_js_1.skillsPushCommand)(slug, inputPath, options);
|
|
194
|
+
}));
|
|
178
195
|
skillsGroup
|
|
179
196
|
.command("request-verification <slug>")
|
|
180
197
|
.description("Request verification for your skill (required before discoverability)")
|