@erdoai/cli 0.119.0 → 0.120.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/dist/index.js +74 -6
- 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({
|
|
@@ -6957,6 +7020,7 @@ refreshCmd.command("show <slug>").description(
|
|
|
6957
7020
|
}
|
|
6958
7021
|
const rows = [];
|
|
6959
7022
|
rows.push(["strategy", res.strategy || "(none recorded)"]);
|
|
7023
|
+
if (res.experiment_run_id) rows.push(["experiment", res.experiment_run_id]);
|
|
6960
7024
|
rows.push(["writes", describeRefreshWrites(res.strategy, res.refresh_mode, res.key_column, res.live_write_mode)]);
|
|
6961
7025
|
rows.push(["trigger", describeRefreshTrigger(res)]);
|
|
6962
7026
|
if (res.schedule) {
|
|
@@ -7063,6 +7127,9 @@ refreshCmd.command("set <slug>").description(
|
|
|
7063
7127
|
).option(
|
|
7064
7128
|
"--parameters <@file>",
|
|
7065
7129
|
"JSON file: all configuration values for this recipe; omitting clears previous PARAMETERS values"
|
|
7130
|
+
).option(
|
|
7131
|
+
"--experiment-run-id <uuid>",
|
|
7132
|
+
"bind this measurement refresh to its experiment so it stops when the experiment settles; must match parameters.experiment_run_id when present"
|
|
7066
7133
|
).action(
|
|
7067
7134
|
async (slug, opts) => {
|
|
7068
7135
|
try {
|
|
@@ -7083,6 +7150,7 @@ refreshCmd.command("set <slug>").description(
|
|
|
7083
7150
|
return;
|
|
7084
7151
|
}
|
|
7085
7152
|
const body = { strategy: opts.strategy };
|
|
7153
|
+
if (opts.experimentRunId !== void 0) body.experiment_run_id = opts.experimentRunId;
|
|
7086
7154
|
if (opts.mode) body.refresh_mode = opts.mode;
|
|
7087
7155
|
if (opts.keyColumn) body.key_column = opts.keyColumn;
|
|
7088
7156
|
if (opts.schedule) body.schedule = opts.schedule;
|