@orth/cli 0.2.3 → 0.2.5
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 +3 -9
- package/dist/commands/run.js +10 -7
- package/dist/commands/search.js +1 -4
- package/dist/commands/skills.d.ts +4 -1
- package/dist/commands/skills.js +71 -4
- package/dist/index.js +11 -3
- 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 'orth api <slug>' to see endpoints for an API"));
|
|
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 || [];
|
|
@@ -133,13 +128,12 @@ async function apiCommand(slug, path, options) {
|
|
|
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 'orth api " + slug + " <path>' for endpoint details"));
|
|
136
|
+
console.log(chalk_1.default.gray("\nRun 'orth api show " + slug + " <path>' for endpoint details"));
|
|
143
137
|
console.log(chalk_1.default.gray("Run 'orth run " + slug + " <path>' to call an endpoint"));
|
|
144
138
|
}
|
|
145
139
|
catch (error) {
|
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,10 +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
|
|
24
|
+
export declare function skillsPushCommand(slug: string, inputPath: string | undefined, options: {
|
|
25
25
|
name?: string;
|
|
26
26
|
tags?: string;
|
|
27
27
|
}): Promise<void>;
|
|
28
|
+
export declare function skillsUpdateCommand(slug: string, inputPath: string | undefined, options: {
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
28
31
|
export declare function skillsRequestVerificationCommand(slug: string): Promise<void>;
|
|
29
32
|
export declare function skillsPublishCommand(slug: string, options: {
|
|
30
33
|
unpublish?: boolean;
|
package/dist/commands/skills.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.skillsCreateCommand = skillsCreateCommand;
|
|
|
43
43
|
exports.skillsInstallCommand = skillsInstallCommand;
|
|
44
44
|
exports.skillsInitCommand = skillsInitCommand;
|
|
45
45
|
exports.skillsSubmitCommand = skillsSubmitCommand;
|
|
46
|
+
exports.skillsPushCommand = skillsPushCommand;
|
|
46
47
|
exports.skillsUpdateCommand = skillsUpdateCommand;
|
|
47
48
|
exports.skillsRequestVerificationCommand = skillsRequestVerificationCommand;
|
|
48
49
|
exports.skillsPublishCommand = skillsPublishCommand;
|
|
@@ -577,9 +578,10 @@ async function skillsSubmitCommand(inputPath, options) {
|
|
|
577
578
|
}
|
|
578
579
|
}
|
|
579
580
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
580
|
-
// orth skills
|
|
581
|
+
// orth skills push <slug> [path]
|
|
582
|
+
// Push local skill files to remote (replaces remote with local)
|
|
581
583
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
582
|
-
async function
|
|
584
|
+
async function skillsPushCommand(slug, inputPath, options) {
|
|
583
585
|
const dirPath = inputPath ? path.resolve(inputPath) : process.cwd();
|
|
584
586
|
const spinner = (0, ora_1.default)("Reading skill files...").start();
|
|
585
587
|
try {
|
|
@@ -621,7 +623,7 @@ async function skillsUpdateCommand(slug, inputPath, options) {
|
|
|
621
623
|
console.error(chalk_1.default.red("Error: Total content too large (max 1MB)"));
|
|
622
624
|
process.exit(1);
|
|
623
625
|
}
|
|
624
|
-
spinner.text = "
|
|
626
|
+
spinner.text = "Pushing skill to Orthogonal...";
|
|
625
627
|
const tags = options.tags
|
|
626
628
|
? options.tags.split(",").map((t) => t.trim())
|
|
627
629
|
: undefined;
|
|
@@ -643,7 +645,7 @@ async function skillsUpdateCommand(slug, inputPath, options) {
|
|
|
643
645
|
body: updatePayload,
|
|
644
646
|
});
|
|
645
647
|
spinner.stop();
|
|
646
|
-
console.log(chalk_1.default.green(`\n✓ Skill
|
|
648
|
+
console.log(chalk_1.default.green(`\n✓ Skill pushed successfully`));
|
|
647
649
|
console.log(chalk_1.default.bold(`\n${data.skill.name}`));
|
|
648
650
|
console.log(chalk_1.default.gray(` Slug: ${data.skill.slug}`));
|
|
649
651
|
console.log(chalk_1.default.gray(` Files: ${files.length}`));
|
|
@@ -658,6 +660,71 @@ async function skillsUpdateCommand(slug, inputPath, options) {
|
|
|
658
660
|
}
|
|
659
661
|
}
|
|
660
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
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
661
728
|
// orth skills request-verification <slug>
|
|
662
729
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
663
730
|
async function skillsRequestVerificationCommand(slug) {
|
package/dist/index.js
CHANGED
|
@@ -177,13 +177,21 @@ skillsGroup
|
|
|
177
177
|
}));
|
|
178
178
|
skillsGroup
|
|
179
179
|
.command("update <slug> [path]")
|
|
180
|
-
.description("
|
|
181
|
-
.option("-
|
|
182
|
-
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
180
|
+
.description("Pull latest skill version from Orthogonal to local directory")
|
|
181
|
+
.option("-f, --force", "Overwrite existing files")
|
|
183
182
|
.action(asyncAction(async (slug, inputPath, options) => {
|
|
184
183
|
(0, analytics_js_1.trackEvent)("skills.update", { slug, path: inputPath });
|
|
185
184
|
await (0, skills_js_1.skillsUpdateCommand)(slug, inputPath, options);
|
|
186
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
|
+
}));
|
|
187
195
|
skillsGroup
|
|
188
196
|
.command("request-verification <slug>")
|
|
189
197
|
.description("Request verification for your skill (required before discoverability)")
|