@orth/cli 0.2.11 → 0.2.13

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/api.d.ts CHANGED
@@ -58,6 +58,15 @@ export interface DetailsResponse {
58
58
  description?: string;
59
59
  }>;
60
60
  };
61
+ action?: {
62
+ description?: string;
63
+ parameters?: Array<{
64
+ name: string;
65
+ type: string;
66
+ required: boolean;
67
+ description?: string;
68
+ }>;
69
+ };
61
70
  }
62
71
  export interface RunResponse {
63
72
  success: boolean;
@@ -53,12 +53,13 @@ async function apiCommand(slug, path, options) {
53
53
  const data = await (0, api_js_1.getDetails)(slug, path);
54
54
  spinner.stop();
55
55
  console.log(chalk_1.default.bold(`\n${chalk_1.default.cyan(slug)}${chalk_1.default.white(path)}\n`));
56
- // Get description from endpoint object if available
57
- const desc = data.description || data.endpoint?.description;
56
+ // Get description from endpoint or action object if available
57
+ const desc = data.description || data.endpoint?.description || data.action?.description;
58
58
  console.log(chalk_1.default.gray(desc || "No description"));
59
- // Get params from nested endpoint object if needed
59
+ // Get params from nested endpoint or action object if needed
60
+ const actionParams = data.action?.parameters ?? [];
60
61
  const queryParams = data.parameters?.query || data.endpoint?.queryParams || [];
61
- const bodyParams = data.parameters?.body || data.endpoint?.bodyParams || [];
62
+ const bodyParams = data.parameters?.body || data.endpoint?.bodyParams || actionParams;
62
63
  if (queryParams.length > 0) {
63
64
  console.log(chalk_1.default.bold("\nQuery Parameters:"));
64
65
  for (const param of queryParams) {
@@ -33,7 +33,7 @@ async function whoamiCommand() {
33
33
  const key = (0, config_js_1.getApiKey)();
34
34
  if (!key) {
35
35
  console.log(chalk_1.default.yellow("Not authenticated"));
36
- console.log(chalk_1.default.gray("Run 'ortho login' to authenticate"));
36
+ console.log(chalk_1.default.gray("Run 'orth login' to authenticate"));
37
37
  return;
38
38
  }
39
39
  console.log(chalk_1.default.green("✓ Authenticated"));
@@ -4,4 +4,5 @@ export declare function runCommand(api: string, path: string, options: {
4
4
  body?: string;
5
5
  data?: string;
6
6
  raw?: boolean;
7
+ output?: string;
7
8
  }): Promise<void>;
@@ -6,7 +6,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runCommand = runCommand;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const ora_1 = __importDefault(require("ora"));
9
+ const fs_1 = require("fs");
10
+ const path_1 = require("path");
9
11
  const api_js_1 = require("../api.js");
12
+ // Map content-type to file extension
13
+ const CONTENT_TYPE_EXT = {
14
+ "image/jpeg": "jpg",
15
+ "image/png": "png",
16
+ "image/gif": "gif",
17
+ "image/webp": "webp",
18
+ "image/svg+xml": "svg",
19
+ "application/pdf": "pdf",
20
+ "application/zip": "zip",
21
+ "application/octet-stream": "bin",
22
+ "audio/mpeg": "mp3",
23
+ "audio/wav": "wav",
24
+ "video/mp4": "mp4",
25
+ };
26
+ const VALID_ENCODINGS = new Set(["base64", "base64url", "hex", "utf8", "utf-8", "ascii", "latin1", "binary"]);
27
+ function writeExclusive(filePath, data) {
28
+ try {
29
+ (0, fs_1.writeFileSync)(filePath, data, { flag: "wx" });
30
+ }
31
+ catch (err) {
32
+ if (err?.code === "EEXIST") {
33
+ console.error(chalk_1.default.red(`\nError: File already exists: ${filePath}`));
34
+ console.error(chalk_1.default.gray("Remove it first or choose a different path."));
35
+ process.exit(1);
36
+ }
37
+ throw err;
38
+ }
39
+ }
40
+ function extFromContentType(contentType) {
41
+ // Try exact match first, then prefix match
42
+ if (CONTENT_TYPE_EXT[contentType])
43
+ return CONTENT_TYPE_EXT[contentType];
44
+ // Strip parameters (e.g., "image/jpeg; charset=utf-8")
45
+ const base = contentType.split(";")[0].trim();
46
+ if (CONTENT_TYPE_EXT[base])
47
+ return CONTENT_TYPE_EXT[base];
48
+ // Fallback: use subtype
49
+ const parts = base.split("/");
50
+ return parts[1] || "bin";
51
+ }
52
+ function isBinaryEnvelope(data) {
53
+ return (typeof data === "object" &&
54
+ data !== null &&
55
+ data._binary === true &&
56
+ typeof data.data === "string" &&
57
+ typeof data.encoding === "string" &&
58
+ typeof data.contentType === "string" &&
59
+ typeof data.size === "number");
60
+ }
10
61
  async function runCommand(api, path, options) {
11
62
  const spinner = (0, ora_1.default)(`Calling ${api}${path}...`).start();
12
63
  try {
@@ -64,6 +115,35 @@ async function runCommand(api, path, options) {
64
115
  body,
65
116
  });
66
117
  spinner.stop();
118
+ // Handle binary responses (base64-encoded by the server)
119
+ if (isBinaryEnvelope(result.data)) {
120
+ if (!options.output) {
121
+ const ext = extFromContentType(result.data.contentType);
122
+ const methodHint = options.method !== "GET" ? ` -X ${options.method}` : "";
123
+ const bodyHint = options.body || options.data ? " --body '...'" : "";
124
+ const queryHint = options.query?.length ? " -q '...'" : "";
125
+ console.log(chalk_1.default.yellow(`\nResponse contains binary ${ext.toUpperCase()} data (${result.data.size} bytes).` +
126
+ `\nUse -o to save it: orth api run ${api} ${path}${methodHint}${queryHint}${bodyHint} -o output.${ext}`));
127
+ return;
128
+ }
129
+ const ext = extFromContentType(result.data.contentType);
130
+ const outputPath = (0, path_1.resolve)(options.output);
131
+ if (!VALID_ENCODINGS.has(result.data.encoding)) {
132
+ console.error(chalk_1.default.red(`\nError: Server returned unsupported encoding "${result.data.encoding}".`));
133
+ process.exit(1);
134
+ }
135
+ const buffer = Buffer.from(result.data.data, result.data.encoding);
136
+ writeExclusive(outputPath, buffer);
137
+ console.log(chalk_1.default.green(`\n${ext.toUpperCase()} saved to: ${outputPath} (${buffer.length} bytes)`));
138
+ return;
139
+ }
140
+ // If --output specified for non-binary data, save JSON to file
141
+ if (options.output) {
142
+ const outputPath = (0, path_1.resolve)(options.output);
143
+ writeExclusive(outputPath, JSON.stringify(result.data, null, 2));
144
+ console.log(chalk_1.default.green(`\nResponse saved to: ${outputPath}`));
145
+ return;
146
+ }
67
147
  if (options.raw) {
68
148
  console.log(JSON.stringify(result.data, null, 2));
69
149
  }
package/dist/config.js CHANGED
@@ -29,7 +29,7 @@ function clearApiKey() {
29
29
  function requireApiKey() {
30
30
  const key = getApiKey();
31
31
  if (!key) {
32
- console.error("Error: Not authenticated. Run 'ortho login' first or set ORTHOGONAL_API_KEY.");
32
+ console.error("Error: Not authenticated. Run 'orth login' first or set ORTHOGONAL_API_KEY.");
33
33
  process.exit(1);
34
34
  }
35
35
  return key;
package/dist/index.js CHANGED
@@ -110,6 +110,7 @@ apiGroup
110
110
  .option("-b, --body <json>", "Request body JSON")
111
111
  .option("-d, --data <json>", "Alias for --body")
112
112
  .option("--raw", "Output raw JSON without formatting")
113
+ .option("-o, --output <file>", "Save response to file (auto-detects binary like images)")
113
114
  .action(asyncAction(async (slug, path, options) => {
114
115
  (0, analytics_js_1.trackEvent)("api.run", { slug, path });
115
116
  await (0, run_js_1.runCommand)(slug, path, options);
@@ -253,6 +254,7 @@ program
253
254
  .option("-b, --body <json>", "Request body JSON")
254
255
  .option("-d, --data <json>", "Alias for --body")
255
256
  .option("--raw", "Output raw JSON without formatting")
257
+ .option("-o, --output <file>", "Save response to file (auto-detects binary like images)")
256
258
  .action(asyncAction(async (api, path, options) => {
257
259
  (0, analytics_js_1.trackEvent)("run", { api, path });
258
260
  await (0, run_js_1.runCommand)(api, path, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orth/cli",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "CLI to access all APIs and skills on the Orthogonal platform",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -40,11 +40,11 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "chalk": "^5.3.0",
43
+ "chalk": "^4.1.2",
44
44
  "commander": "^12.0.0",
45
- "conf": "^12.0.0",
46
- "node-fetch": "^3.3.2",
47
- "ora": "^8.0.1"
45
+ "conf": "^10.2.0",
46
+ "node-fetch": "^2.7.0",
47
+ "ora": "^5.4.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^20.0.0",