@erdoai/cli 0.119.0 → 0.119.1

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.
Files changed (2) hide show
  1. package/dist/index.js +69 -6
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -135,7 +135,7 @@ function formatOrg(name, slug) {
135
135
  if (name && slug) return `${name} (${slug})`;
136
136
  return name || slug || "";
137
137
  }
138
- var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "error"]);
138
+ var TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled", "declined", "error"]);
139
139
  var POLL_STOP_STATUSES = /* @__PURE__ */ new Set([...TERMINAL_RUN_STATUSES, "awaiting_approval"]);
140
140
  var ErdoApiError = class extends Error {
141
141
  constructor(status, message) {
@@ -162,13 +162,13 @@ function networkErrorFor(method, path, err) {
162
162
  return new ErdoNetworkError(`${method} ${path} failed: ${detail}`, cause);
163
163
  }
164
164
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
165
- async function rawRequest(baseURL, token, orgId, projectId, method, path, body) {
165
+ async function rawRequest(baseURL, token, orgId, projectId, method, path, body, retryNetworkFailures = true) {
166
166
  const headers = { Authorization: `Bearer ${token}` };
167
167
  if (orgId) headers["X-Organization-ID"] = orgId;
168
168
  if (projectId) headers["X-Project-ID"] = projectId;
169
169
  if (body !== void 0) headers["Content-Type"] = "application/json";
170
170
  const upper = method.toUpperCase();
171
- const maxRetries = upper === "GET" || upper === "HEAD" ? 2 : 1;
171
+ const maxRetries = retryNetworkFailures ? upper === "GET" || upper === "HEAD" ? 2 : 1 : 0;
172
172
  let attempt = 0;
173
173
  while (true) {
174
174
  let res;
@@ -185,6 +185,9 @@ async function rawRequest(baseURL, token, orgId, projectId, method, path, body)
185
185
  attempt++;
186
186
  continue;
187
187
  }
188
+ if (!retryNetworkFailures) {
189
+ throw new ErdoNetworkError(`${netErr.message}. The launch may have started; inspect agent runs before retrying.`, netErr);
190
+ }
188
191
  throw netErr;
189
192
  }
190
193
  const text = await res.text();
@@ -260,7 +263,7 @@ var ErdoClient = class {
260
263
  process.stderr.write("org: token default (no org resolved \u2014 pass --org to pin)\n");
261
264
  }
262
265
  }
263
- request(method, path, body) {
266
+ request(method, path, body, retryNetworkFailures = true) {
264
267
  this.announceOrgOnce(method);
265
268
  return rawRequest(
266
269
  this.baseURL,
@@ -269,7 +272,8 @@ var ErdoClient = class {
269
272
  this.projectId,
270
273
  method,
271
274
  path,
272
- body
275
+ body,
276
+ retryNetworkFailures
273
277
  );
274
278
  }
275
279
  me() {
@@ -1058,7 +1062,7 @@ var ErdoClient = class {
1058
1062
  }
1059
1063
  // --- agents ---
1060
1064
  ask(input) {
1061
- return this.request("POST", "/v1/ask", input);
1065
+ return this.request("POST", "/v1/ask", input, !input.saved_ask_id);
1062
1066
  }
1063
1067
  createThread(input) {
1064
1068
  return this.request(
@@ -4044,6 +4048,65 @@ agentCmd.command("wait <threadId>").description("Re-attach to a thread's latest
4044
4048
  fail(e);
4045
4049
  }
4046
4050
  });
4051
+ var shortcutCmd = program.command("shortcut").description("Inspect and run saved Shortcuts with their stored agent and context");
4052
+ shortcutCmd.command("get <id>").description("Read the complete saved Ask, including its config and linked Knowledge").action(async (id) => {
4053
+ try {
4054
+ const response = await new ErdoClient().getKnowledge(id);
4055
+ if (response.object.type !== "ask" || ["archived", "deleted"].includes(response.object.status)) {
4056
+ throw new Error("Shortcut not found or inactive");
4057
+ }
4058
+ print(response);
4059
+ } catch (e) {
4060
+ fail(e);
4061
+ }
4062
+ });
4063
+ shortcutCmd.command("run <id>").description("Run the current saved prompt, agent, datasets and linked skills").option("--parameters <json>", "flat invocation data as JSON or @path; does not override saved configuration").option("--sync", "hold one HTTP request until completion; long runs can exceed proxy limits").option("--no-wait", "print the startup acknowledgement without waiting for the terminal run").action(async (id, opts) => {
4064
+ try {
4065
+ if (opts.sync && opts.wait === false) throw new Error("--sync cannot be combined with --no-wait");
4066
+ let parameters;
4067
+ if (opts.parameters !== void 0) {
4068
+ const value = JSON.parse(readMaybeFile(opts.parameters) ?? "");
4069
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
4070
+ throw new Error("Invocation parameters must be a plain object");
4071
+ }
4072
+ for (const [key, field] of Object.entries(value)) {
4073
+ if (!/^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(key)) throw new Error(`Invalid invocation parameter key: ${key}`);
4074
+ if (field !== null && typeof field !== "string" && typeof field !== "boolean" && (typeof field !== "number" || !Number.isFinite(field))) {
4075
+ throw new Error(`Invalid invocation parameter value for "${key}"`);
4076
+ }
4077
+ }
4078
+ if (JSON.stringify(value).length > 4096) throw new Error("Invocation parameters exceed the 4096-character limit");
4079
+ parameters = value;
4080
+ }
4081
+ const client = new ErdoClient();
4082
+ const response = await client.ask({ saved_ask_id: id, parameters, async: !opts.sync });
4083
+ if (typeof response.thread_id !== "string" || !response.thread_id.trim() || !opts.sync && response.status !== "running") throw new Error("Saved Shortcut did not start");
4084
+ if (opts.wait === false) {
4085
+ print({ thread_id: response.thread_id, message_id: response.message_id, status: response.status });
4086
+ return;
4087
+ }
4088
+ if (opts.sync) {
4089
+ if (response.status !== "completed" && response.status !== "success") throw new Error(`Saved Shortcut did not complete (status: ${response.status}; thread: ${response.thread_id})`);
4090
+ console.log(response.answer || `(status: ${response.status})`);
4091
+ console.log(`
4092
+ thread: ${response.thread_id}`);
4093
+ return;
4094
+ }
4095
+ process.stderr.write(`thread: ${response.thread_id}
4096
+ running\u2026`);
4097
+ const run = await client.waitForThreadRun(response.thread_id, { onTick: () => process.stderr.write(".") });
4098
+ process.stderr.write("\n");
4099
+ if (!run) throw new Error(timedOutMessage(response.thread_id));
4100
+ if (run.status === "awaiting_approval") throw new Error(awaitingApprovalMessage(response.thread_id));
4101
+ if (run.error) throw new Error(run.error);
4102
+ if (run.status !== "completed") throw new Error(`Saved Shortcut did not complete (status: ${run.status})`);
4103
+ console.log(run.output || `(status: ${run.status})`);
4104
+ console.log(`
4105
+ thread: ${response.thread_id}`);
4106
+ } catch (e) {
4107
+ fail(e);
4108
+ }
4109
+ });
4047
4110
  agentCmd.command("thread").description("Create a thread; prints its id").option("-n, --name <name>", "thread name").option("-d, --datasets <csv>", "dataset UUIDs to attach").option("--hidden", "keep the thread out of the organization's thread list (for diagnostic or scripted threads)").action(async (opts) => {
4048
4111
  try {
4049
4112
  const res = await new ErdoClient().createThread({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.119.0",
3
+ "version": "0.119.1",
4
4
  "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
5
5
  "type": "module",
6
6
  "bin": {