@elnora-ai/cli 1.1.1 → 1.2.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/LICENSE +201 -201
- package/README.md +155 -137
- package/dist/cli.js +996 -447
- package/package.json +66 -67
- package/dist/cli.cjs +0 -442
package/dist/cli.js
CHANGED
|
@@ -10,18 +10,6 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
|
-
import { readFileSync } from "node:fs";
|
|
14
|
-
import { dirname, join } from "node:path";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
function getVersion() {
|
|
17
|
-
try {
|
|
18
|
-
const dir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
const pkg = JSON.parse(readFileSync(join(dir, "../../package.json"), "utf-8"));
|
|
20
|
-
return pkg.version ?? "0.0.0-dev";
|
|
21
|
-
} catch {
|
|
22
|
-
return "0.0.0-dev";
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
13
|
function buildUrl(endpointKey, params, baseUrl) {
|
|
26
14
|
const path = ENDPOINTS[endpointKey];
|
|
27
15
|
if (!path) throw new Error(`Unknown endpoint: ${endpointKey}`);
|
|
@@ -33,12 +21,13 @@ function buildUrl(endpointKey, params, baseUrl) {
|
|
|
33
21
|
}
|
|
34
22
|
return `${baseUrl ?? BASE_URL}${resolved}`;
|
|
35
23
|
}
|
|
36
|
-
var VERSION, BASE_URL, AI_SERVER_URL, ENDPOINTS, DEFAULT_HEADERS;
|
|
24
|
+
var VERSION, PRODUCTION_BASE_URL, BASE_URL, AI_SERVER_URL, ENDPOINTS, DEFAULT_HEADERS;
|
|
37
25
|
var init_config = __esm({
|
|
38
26
|
"src/lib/config.ts"() {
|
|
39
27
|
"use strict";
|
|
40
|
-
VERSION =
|
|
41
|
-
|
|
28
|
+
VERSION = typeof __APP_VERSION__ !== "undefined" ? __APP_VERSION__ : process.env.npm_package_version ?? "0.0.0-dev";
|
|
29
|
+
PRODUCTION_BASE_URL = "https://platform.elnora.ai/api/v1";
|
|
30
|
+
BASE_URL = process.env.ELNORA_API_URL ?? PRODUCTION_BASE_URL;
|
|
42
31
|
AI_SERVER_URL = process.env.ELNORA_AI_SERVER_URL ?? "https://platform.elnora.ai/ai-server";
|
|
43
32
|
ENDPOINTS = {
|
|
44
33
|
// Projects
|
|
@@ -87,6 +76,7 @@ var init_config = __esm({
|
|
|
87
76
|
// Organization invitations
|
|
88
77
|
org_invitations: "/organizations/{orgId}/invitations",
|
|
89
78
|
org_invitation: "/organizations/{orgId}/invitations/{invId}",
|
|
79
|
+
org_invitation_resend: "/organizations/{orgId}/invitations/{invId}/resend",
|
|
90
80
|
// Public invitations
|
|
91
81
|
invitation_info: "/invitations/{token}",
|
|
92
82
|
invitation_accept: "/invitations/{token}/accept",
|
|
@@ -175,6 +165,23 @@ function formatErrorPayload(err) {
|
|
|
175
165
|
}
|
|
176
166
|
return payload;
|
|
177
167
|
}
|
|
168
|
+
function formatErrorForHuman(err) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
const message = scrub(err.message);
|
|
171
|
+
lines.push(`Error: ${message}`);
|
|
172
|
+
if (err instanceof ElnoraError && err.suggestion) {
|
|
173
|
+
const steps = err.suggestion.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
174
|
+
if (steps.length === 1) {
|
|
175
|
+
lines.push("", ` ${steps[0]}`);
|
|
176
|
+
} else {
|
|
177
|
+
lines.push("", "Next steps:");
|
|
178
|
+
for (const step of steps) {
|
|
179
|
+
lines.push(` ${step}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return lines.join("\n");
|
|
184
|
+
}
|
|
178
185
|
var ElnoraError, AuthError, NotFoundError, RateLimitError, ValidationError, ServerError, EXIT_CODES, SCRUB_KEY_VALUE_RE, SCRUB_LONG_TOKEN_RE;
|
|
179
186
|
var init_errors = __esm({
|
|
180
187
|
"src/lib/errors.ts"() {
|
|
@@ -244,9 +251,9 @@ var init_errors = __esm({
|
|
|
244
251
|
});
|
|
245
252
|
|
|
246
253
|
// src/lib/profiles.ts
|
|
247
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync
|
|
254
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
248
255
|
import { homedir } from "node:os";
|
|
249
|
-
import { join
|
|
256
|
+
import { join } from "node:path";
|
|
250
257
|
function validateProfileName(name) {
|
|
251
258
|
if (!PROFILE_NAME_RE.test(name)) {
|
|
252
259
|
throw new ValidationError(
|
|
@@ -305,7 +312,7 @@ function serializeProfiles(profiles) {
|
|
|
305
312
|
return parts.join("\n");
|
|
306
313
|
}
|
|
307
314
|
function writeSecureFile(filePath, content) {
|
|
308
|
-
const dir =
|
|
315
|
+
const dir = join(filePath, "..");
|
|
309
316
|
mkdirSync(dir, { recursive: true });
|
|
310
317
|
if (process.platform !== "win32") {
|
|
311
318
|
try {
|
|
@@ -318,7 +325,7 @@ function writeSecureFile(filePath, content) {
|
|
|
318
325
|
function loadProfiles() {
|
|
319
326
|
if (!existsSync(PROFILES_FILE)) return {};
|
|
320
327
|
try {
|
|
321
|
-
const text =
|
|
328
|
+
const text = readFileSync(PROFILES_FILE, "utf-8");
|
|
322
329
|
return parseProfilesToml(text);
|
|
323
330
|
} catch {
|
|
324
331
|
return {};
|
|
@@ -368,7 +375,7 @@ function migrateConfigIfNeeded() {
|
|
|
368
375
|
if (!existsSync(LEGACY_CONFIG_FILE)) return false;
|
|
369
376
|
let apiKey = "";
|
|
370
377
|
try {
|
|
371
|
-
const text =
|
|
378
|
+
const text = readFileSync(LEGACY_CONFIG_FILE, "utf-8");
|
|
372
379
|
for (const line of text.split("\n")) {
|
|
373
380
|
const trimmed = line.trim();
|
|
374
381
|
if (trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -396,9 +403,9 @@ var init_profiles = __esm({
|
|
|
396
403
|
"src/lib/profiles.ts"() {
|
|
397
404
|
"use strict";
|
|
398
405
|
init_errors();
|
|
399
|
-
CONFIG_DIR =
|
|
400
|
-
PROFILES_FILE =
|
|
401
|
-
LEGACY_CONFIG_FILE =
|
|
406
|
+
CONFIG_DIR = join(homedir(), ".elnora");
|
|
407
|
+
PROFILES_FILE = join(CONFIG_DIR, "profiles.toml");
|
|
408
|
+
LEGACY_CONFIG_FILE = join(CONFIG_DIR, "config.toml");
|
|
402
409
|
PROFILE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
403
410
|
}
|
|
404
411
|
});
|
|
@@ -492,7 +499,7 @@ var init_client = __esm({
|
|
|
492
499
|
}
|
|
493
500
|
url = `${url}?${qs.toString()}`;
|
|
494
501
|
}
|
|
495
|
-
if (this.baseUrl ===
|
|
502
|
+
if (this.baseUrl === PRODUCTION_BASE_URL) {
|
|
496
503
|
validateApiUrl(url);
|
|
497
504
|
}
|
|
498
505
|
const now = Date.now();
|
|
@@ -544,14 +551,30 @@ var init_client = __esm({
|
|
|
544
551
|
bodyText = scrub(await response.text());
|
|
545
552
|
} catch {
|
|
546
553
|
}
|
|
547
|
-
|
|
548
|
-
|
|
554
|
+
let errorMessage = bodyText;
|
|
555
|
+
try {
|
|
556
|
+
const parsed = JSON.parse(bodyText);
|
|
557
|
+
if (parsed.messages?.length) {
|
|
558
|
+
errorMessage = parsed.messages.join(" ");
|
|
559
|
+
} else if (parsed.message) {
|
|
560
|
+
errorMessage = parsed.message;
|
|
561
|
+
}
|
|
562
|
+
} catch {
|
|
563
|
+
}
|
|
564
|
+
if (response.status === 401) {
|
|
565
|
+
throw new AuthError(errorMessage.slice(0, 200) || "Authentication failed");
|
|
566
|
+
}
|
|
567
|
+
if (response.status === 403) {
|
|
568
|
+
throw new ElnoraError(errorMessage.slice(0, 200) || "Access denied", {
|
|
569
|
+
code: "FORBIDDEN",
|
|
570
|
+
suggestion: "You don't have permission to access this resource. Check the resource ID or your organization membership."
|
|
571
|
+
});
|
|
549
572
|
}
|
|
550
573
|
if (response.status === 404) {
|
|
551
|
-
throw new NotFoundError("resource",
|
|
574
|
+
throw new NotFoundError("resource", errorMessage.slice(0, 200) || "not found");
|
|
552
575
|
}
|
|
553
576
|
if (response.status === 422) {
|
|
554
|
-
throw new ValidationError(
|
|
577
|
+
throw new ValidationError(errorMessage.slice(0, 500) || "Validation error");
|
|
555
578
|
}
|
|
556
579
|
if (response.status === 429) {
|
|
557
580
|
if (attempt < MAX_RETRIES) {
|
|
@@ -824,6 +847,7 @@ var init_http_server = __esm({
|
|
|
824
847
|
|
|
825
848
|
// src/adapters/cli.ts
|
|
826
849
|
init_client();
|
|
850
|
+
init_config();
|
|
827
851
|
init_errors();
|
|
828
852
|
import { Command } from "commander";
|
|
829
853
|
|
|
@@ -979,12 +1003,12 @@ function kebabToCamel(str) {
|
|
|
979
1003
|
return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
980
1004
|
}
|
|
981
1005
|
function buildProgram(registry2) {
|
|
982
|
-
const program2 = new Command().name("elnora").description("Elnora AI Platform CLI").version(
|
|
1006
|
+
const program2 = new Command().name("elnora").description("Elnora AI Platform CLI").version(VERSION).option("--compact", "Token-efficient minimal output", false).option("--output <format>", "Output format (json, csv)", "json").option("--fields <fields>", "Comma-separated fields to include").option("--profile <name>", "Named profile to use").option("--json", "Force JSON output", false);
|
|
983
1007
|
for (const group of registry2.groups()) {
|
|
984
1008
|
const groupCmd = new Command(group).description(`Manage ${group}`);
|
|
985
1009
|
const commands = registry2.byGroup(group);
|
|
986
1010
|
for (const cmd of commands) {
|
|
987
|
-
const subName = cmd.name.split(".").slice(1).join("-");
|
|
1011
|
+
const subName = camelToKebab(cmd.name.split(".").slice(1).join("-"));
|
|
988
1012
|
const sub = new Command(subName).description(cmd.description);
|
|
989
1013
|
const opts = zodToCommanderOptions(cmd.inputSchema);
|
|
990
1014
|
for (const opt of opts) {
|
|
@@ -1024,7 +1048,11 @@ function buildProgram(registry2) {
|
|
|
1024
1048
|
}
|
|
1025
1049
|
}
|
|
1026
1050
|
const profileName = parentOpts.profile ?? "default";
|
|
1027
|
-
|
|
1051
|
+
if (parentOpts.profile && !("profile" in input && input.profile !== "default")) {
|
|
1052
|
+
input.profile = parentOpts.profile;
|
|
1053
|
+
}
|
|
1054
|
+
const skipAuth = cmd.name.startsWith("auth.login") || cmd.name === "auth.logout" || cmd.name === "auth.profiles";
|
|
1055
|
+
const client = skipAuth ? new ElnoraApiClient("placeholder") : ElnoraApiClient.fromEnv(profileName);
|
|
1028
1056
|
const ctx = {
|
|
1029
1057
|
client,
|
|
1030
1058
|
profileName,
|
|
@@ -1037,19 +1065,37 @@ function buildProgram(registry2) {
|
|
|
1037
1065
|
};
|
|
1038
1066
|
const parsed = cmd.inputSchema.parse(input);
|
|
1039
1067
|
const result = await cmd.execute(parsed, ctx);
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1068
|
+
if (result != null) {
|
|
1069
|
+
const isStreamed = typeof result === "object" && result.streamed === true;
|
|
1070
|
+
if (!isStreamed) {
|
|
1071
|
+
const hasGlobalFlags = ctx.output.compact || ctx.output.format !== "json" || ctx.output.fields;
|
|
1072
|
+
let formatted;
|
|
1073
|
+
if (cmd.formatOutput && !hasGlobalFlags) {
|
|
1074
|
+
formatted = cmd.formatOutput(result, ctx.output.format);
|
|
1075
|
+
} else {
|
|
1076
|
+
formatted = formatOutput(result, {
|
|
1077
|
+
format: ctx.output.format,
|
|
1078
|
+
compact: ctx.output.compact,
|
|
1079
|
+
fields: ctx.output.fields
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
if (formatted) {
|
|
1083
|
+
process.stdout.write(`${formatted}
|
|
1046
1084
|
`);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1047
1088
|
} catch (err) {
|
|
1048
1089
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1049
|
-
|
|
1050
|
-
|
|
1090
|
+
if (process.stderr.isTTY) {
|
|
1091
|
+
process.stderr.write(`${formatErrorForHuman(error)}
|
|
1051
1092
|
`);
|
|
1052
|
-
|
|
1093
|
+
} else {
|
|
1094
|
+
const payload = formatErrorPayload(error);
|
|
1095
|
+
process.stderr.write(`${JSON.stringify(payload, null, 2)}
|
|
1096
|
+
`);
|
|
1097
|
+
}
|
|
1098
|
+
process.exitCode = getExitCode(error);
|
|
1053
1099
|
}
|
|
1054
1100
|
});
|
|
1055
1101
|
groupCmd.addCommand(sub);
|
|
@@ -1454,7 +1500,7 @@ var paginationInput = {
|
|
|
1454
1500
|
|
|
1455
1501
|
// src/commands/audit/list.ts
|
|
1456
1502
|
var inputSchema15 = z16.object({
|
|
1457
|
-
|
|
1503
|
+
orgId: z16.string().uuid().describe("Organization ID"),
|
|
1458
1504
|
...paginationInput,
|
|
1459
1505
|
action: z16.string().optional().describe("Filter by action type"),
|
|
1460
1506
|
userId: z16.string().optional().describe("Filter by user ID")
|
|
@@ -1474,7 +1520,7 @@ var auditList = {
|
|
|
1474
1520
|
if (input.action) queryParams.action = input.action;
|
|
1475
1521
|
if (input.userId) queryParams.userId = input.userId;
|
|
1476
1522
|
return ctx.client.get("audit_log", {
|
|
1477
|
-
pathParams: { orgId: input.
|
|
1523
|
+
pathParams: { orgId: input.orgId },
|
|
1478
1524
|
queryParams
|
|
1479
1525
|
});
|
|
1480
1526
|
},
|
|
@@ -1494,6 +1540,78 @@ init_client();
|
|
|
1494
1540
|
init_errors();
|
|
1495
1541
|
init_profiles();
|
|
1496
1542
|
import { z as z17 } from "zod";
|
|
1543
|
+
|
|
1544
|
+
// src/lib/prompt.ts
|
|
1545
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
1546
|
+
function promptSecret(message) {
|
|
1547
|
+
return new Promise((resolve, reject) => {
|
|
1548
|
+
const { stdin, stdout } = process;
|
|
1549
|
+
if (!stdin.isTTY) {
|
|
1550
|
+
const rl = createInterface2({ input: stdin, output: stdout });
|
|
1551
|
+
rl.question(message, (answer) => {
|
|
1552
|
+
rl.close();
|
|
1553
|
+
resolve(answer.trim());
|
|
1554
|
+
});
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
stdout.write(message);
|
|
1558
|
+
let input = "";
|
|
1559
|
+
const wasRaw = stdin.isRaw;
|
|
1560
|
+
stdin.setRawMode(true);
|
|
1561
|
+
stdin.resume();
|
|
1562
|
+
stdin.setEncoding("utf8");
|
|
1563
|
+
const onData = (char) => {
|
|
1564
|
+
switch (char) {
|
|
1565
|
+
case "\n":
|
|
1566
|
+
case "\r":
|
|
1567
|
+
case "": {
|
|
1568
|
+
stdin.setRawMode(wasRaw);
|
|
1569
|
+
stdin.pause();
|
|
1570
|
+
stdin.removeListener("data", onData);
|
|
1571
|
+
stdout.write("\n");
|
|
1572
|
+
resolve(input.trim());
|
|
1573
|
+
break;
|
|
1574
|
+
}
|
|
1575
|
+
case "":
|
|
1576
|
+
// Ctrl+C
|
|
1577
|
+
case "\x1B": {
|
|
1578
|
+
stdin.setRawMode(wasRaw);
|
|
1579
|
+
stdin.pause();
|
|
1580
|
+
stdin.removeListener("data", onData);
|
|
1581
|
+
stdout.write("\n");
|
|
1582
|
+
reject(new Error("Cancelled"));
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
case "\x7F":
|
|
1586
|
+
case "\b": {
|
|
1587
|
+
if (input.length > 0) {
|
|
1588
|
+
input = input.slice(0, -1);
|
|
1589
|
+
stdout.write("\b \b");
|
|
1590
|
+
}
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
default: {
|
|
1594
|
+
input += char;
|
|
1595
|
+
stdout.write("*");
|
|
1596
|
+
break;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
};
|
|
1600
|
+
stdin.on("data", onData);
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// src/lib/tty.ts
|
|
1605
|
+
function isTTY() {
|
|
1606
|
+
return Boolean(process.stdout.isTTY);
|
|
1607
|
+
}
|
|
1608
|
+
function isColorEnabled() {
|
|
1609
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
1610
|
+
if (process.env.FORCE_COLOR !== void 0) return true;
|
|
1611
|
+
return isTTY();
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/commands/auth/login.ts
|
|
1497
1615
|
var inputSchema16 = z17.object({
|
|
1498
1616
|
apiKey: z17.string().optional().describe("Elnora API key (elnora_live_...)"),
|
|
1499
1617
|
profile: z17.string().default("default").describe("Profile name to save key under")
|
|
@@ -1505,30 +1623,118 @@ var authLogin = {
|
|
|
1505
1623
|
inputSchema: inputSchema16,
|
|
1506
1624
|
outputSchema: z17.any(),
|
|
1507
1625
|
async execute(input, _ctx) {
|
|
1508
|
-
|
|
1626
|
+
let apiKey = input.apiKey;
|
|
1627
|
+
const profileName = input.profile;
|
|
1628
|
+
validateProfileName(profileName);
|
|
1629
|
+
const existing = listProfileNames();
|
|
1630
|
+
const profileExists = existing.includes(profileName);
|
|
1631
|
+
if (!apiKey) {
|
|
1632
|
+
if (profileExists) {
|
|
1633
|
+
let savedKey;
|
|
1634
|
+
try {
|
|
1635
|
+
savedKey = getApiKey(profileName);
|
|
1636
|
+
} catch {
|
|
1637
|
+
}
|
|
1638
|
+
if (savedKey && isTTY()) {
|
|
1639
|
+
process.stderr.write(`
|
|
1640
|
+
Checking profile "${profileName}"...`);
|
|
1641
|
+
try {
|
|
1642
|
+
const client = new ElnoraApiClient(savedKey);
|
|
1643
|
+
const result = await client.get("projects", {
|
|
1644
|
+
queryParams: { page: 1, pageSize: 1 }
|
|
1645
|
+
});
|
|
1646
|
+
const projectCount = result?.totalCount ?? result?.items?.length ?? 0;
|
|
1647
|
+
process.stderr.write(` authenticated (${projectCount} project${projectCount !== 1 ? "s" : ""}).
|
|
1648
|
+
|
|
1649
|
+
`);
|
|
1650
|
+
process.stderr.write(" To update your API key, run:\n");
|
|
1651
|
+
process.stderr.write(
|
|
1652
|
+
` elnora auth login --api-key <new-key>${profileName !== "default" ? ` --profile ${profileName}` : ""}
|
|
1653
|
+
|
|
1654
|
+
`
|
|
1655
|
+
);
|
|
1656
|
+
return null;
|
|
1657
|
+
} catch {
|
|
1658
|
+
process.stderr.write(" key is invalid or expired.\n\n");
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
if (isTTY()) {
|
|
1663
|
+
if (profileExists) {
|
|
1664
|
+
process.stderr.write(` Enter a new API key for profile "${profileName}"
|
|
1665
|
+
`);
|
|
1666
|
+
} else {
|
|
1667
|
+
process.stderr.write("\n Set up Elnora CLI authentication\n");
|
|
1668
|
+
}
|
|
1669
|
+
process.stderr.write(" Get your API key at: https://platform.elnora.ai > Settings > API Keys\n\n");
|
|
1670
|
+
try {
|
|
1671
|
+
apiKey = await promptSecret(" API key (Ctrl+C to cancel): ");
|
|
1672
|
+
} catch {
|
|
1673
|
+
process.stderr.write("\n Login cancelled.\n\n");
|
|
1674
|
+
process.exit(0);
|
|
1675
|
+
}
|
|
1676
|
+
process.stderr.write("\n");
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1509
1679
|
if (!apiKey) {
|
|
1510
1680
|
throw new ValidationError(
|
|
1511
|
-
"API key is required. Use --api-key flag
|
|
1681
|
+
"API key is required. Use --api-key flag or run interactively in a terminal.",
|
|
1512
1682
|
"Get your API key from: https://platform.elnora.ai > Settings > API Keys"
|
|
1513
1683
|
);
|
|
1514
1684
|
}
|
|
1515
1685
|
const key = apiKey.trim();
|
|
1516
1686
|
if (!key.startsWith("elnora_live_")) {
|
|
1517
|
-
throw new AuthError("API key must start with 'elnora_live_'."
|
|
1687
|
+
throw new AuthError("API key must start with 'elnora_live_'. Check that you copied the full key.", {
|
|
1688
|
+
suggestion: "Get your API key from: https://platform.elnora.ai > Settings > API Keys"
|
|
1689
|
+
});
|
|
1518
1690
|
}
|
|
1519
1691
|
if (key.length < 20) {
|
|
1520
|
-
throw new AuthError("API key looks too short.
|
|
1692
|
+
throw new AuthError("API key looks too short. Make sure you copied the full key.", {
|
|
1693
|
+
suggestion: "Get your API key from: https://platform.elnora.ai > Settings > API Keys"
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
if (isTTY()) {
|
|
1697
|
+
process.stderr.write(" Verifying...");
|
|
1698
|
+
}
|
|
1699
|
+
try {
|
|
1700
|
+
const tempClient = new ElnoraApiClient(key);
|
|
1701
|
+
const result = await tempClient.get("projects", {
|
|
1702
|
+
queryParams: { page: 1, pageSize: 1 }
|
|
1703
|
+
});
|
|
1704
|
+
const projectCount = result?.totalCount ?? result?.items?.length ?? 0;
|
|
1705
|
+
const configPath = saveProfile(profileName, key);
|
|
1706
|
+
if (isTTY()) {
|
|
1707
|
+
process.stderr.write(` Done! ${projectCount} project${projectCount !== 1 ? "s" : ""} accessible.
|
|
1708
|
+
`);
|
|
1709
|
+
process.stderr.write(` Saved to profile "${profileName}" at ${configPath}
|
|
1710
|
+
|
|
1711
|
+
`);
|
|
1712
|
+
}
|
|
1713
|
+
return { profile: profileName, verified: true, configPath };
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
if (isTTY()) {
|
|
1716
|
+
process.stderr.write("\n");
|
|
1717
|
+
}
|
|
1718
|
+
if (err instanceof AuthError) {
|
|
1719
|
+
throw new AuthError("API key is invalid or has been revoked. Please check your key and try again.", {
|
|
1720
|
+
suggestion: "Get a new key from: https://platform.elnora.ai > Settings > API Keys"
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
throw err;
|
|
1521
1724
|
}
|
|
1522
|
-
const profileName = input.profile;
|
|
1523
|
-
validateProfileName(profileName);
|
|
1524
|
-
const tempClient = new ElnoraApiClient(key);
|
|
1525
|
-
await tempClient.get("projects", { queryParams: { page: 1, pageSize: 1 } });
|
|
1526
|
-
const configPath = saveProfile(profileName, key);
|
|
1527
|
-
return { profile: profileName, verified: true, configPath };
|
|
1528
1725
|
},
|
|
1529
1726
|
formatOutput(output, format) {
|
|
1530
|
-
|
|
1531
|
-
return JSON.stringify(output
|
|
1727
|
+
const data = output;
|
|
1728
|
+
if (format === "compact") return data.profile ?? JSON.stringify(output);
|
|
1729
|
+
const lines = [
|
|
1730
|
+
`\u2713 Authenticated! API key saved to profile "${data.profile}".`,
|
|
1731
|
+
"",
|
|
1732
|
+
"Next steps:",
|
|
1733
|
+
" elnora projects list See your projects",
|
|
1734
|
+
" elnora tasks create --project <ID> --stream Start a conversation",
|
|
1735
|
+
" elnora doctor Verify your setup"
|
|
1736
|
+
];
|
|
1737
|
+
return lines.join("\n");
|
|
1532
1738
|
}
|
|
1533
1739
|
};
|
|
1534
1740
|
|
|
@@ -1772,26 +1978,102 @@ function addCompletionCommand(program2) {
|
|
|
1772
1978
|
init_client();
|
|
1773
1979
|
init_config();
|
|
1774
1980
|
init_profiles();
|
|
1775
|
-
import { existsSync as
|
|
1776
|
-
import
|
|
1981
|
+
import { existsSync as existsSync3, statSync } from "node:fs";
|
|
1982
|
+
import pc2 from "picocolors";
|
|
1777
1983
|
|
|
1778
|
-
// src/lib/
|
|
1779
|
-
|
|
1780
|
-
|
|
1984
|
+
// src/lib/update-check.ts
|
|
1985
|
+
init_config();
|
|
1986
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1987
|
+
import { homedir as homedir2 } from "node:os";
|
|
1988
|
+
import { join as join2 } from "node:path";
|
|
1989
|
+
import pc from "picocolors";
|
|
1990
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@elnora-ai/cli/latest";
|
|
1991
|
+
var CACHE_DIR = join2(homedir2(), ".elnora");
|
|
1992
|
+
var CACHE_FILE = join2(CACHE_DIR, ".update-check");
|
|
1993
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1994
|
+
var FETCH_TIMEOUT_MS = 3e3;
|
|
1995
|
+
function shouldSkip() {
|
|
1996
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS) return true;
|
|
1997
|
+
if (process.env.ELNORA_NO_UPDATE_CHECK) return true;
|
|
1998
|
+
if (!process.stderr.isTTY) return true;
|
|
1999
|
+
return false;
|
|
1781
2000
|
}
|
|
1782
|
-
function
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2001
|
+
function readCache() {
|
|
2002
|
+
try {
|
|
2003
|
+
if (!existsSync2(CACHE_FILE)) return null;
|
|
2004
|
+
const raw = readFileSync2(CACHE_FILE, "utf-8");
|
|
2005
|
+
return JSON.parse(raw);
|
|
2006
|
+
} catch {
|
|
2007
|
+
return null;
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
function writeCache(entry) {
|
|
2011
|
+
try {
|
|
2012
|
+
mkdirSync2(CACHE_DIR, { recursive: true });
|
|
2013
|
+
writeFileSync2(CACHE_FILE, JSON.stringify(entry), "utf-8");
|
|
2014
|
+
} catch {
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
function showUpdateNotice(latest) {
|
|
2018
|
+
const color = isColorEnabled();
|
|
2019
|
+
const msg = color ? `
|
|
2020
|
+
${pc.yellow(`Update available: v${VERSION} \u2192 v${latest}`)}
|
|
2021
|
+
Run: curl -fsSL https://cli.elnora.ai/install.sh | bash
|
|
2022
|
+
` : `
|
|
2023
|
+
Update available: v${VERSION} \u2192 v${latest}
|
|
2024
|
+
Run: curl -fsSL https://cli.elnora.ai/install.sh | bash
|
|
2025
|
+
`;
|
|
2026
|
+
process.stderr.write(msg);
|
|
2027
|
+
}
|
|
2028
|
+
function isNewerVersion(a, b) {
|
|
2029
|
+
const pa = a.split(".").map(Number);
|
|
2030
|
+
const pb = b.split(".").map(Number);
|
|
2031
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
2032
|
+
const na = pa[i] ?? 0;
|
|
2033
|
+
const nb = pb[i] ?? 0;
|
|
2034
|
+
if (na > nb) return true;
|
|
2035
|
+
if (na < nb) return false;
|
|
2036
|
+
}
|
|
2037
|
+
return false;
|
|
2038
|
+
}
|
|
2039
|
+
function registerUpdateCheck() {
|
|
2040
|
+
if (shouldSkip()) return;
|
|
2041
|
+
const cached = readCache();
|
|
2042
|
+
if (cached) {
|
|
2043
|
+
const elapsed = Date.now() - new Date(cached.checkedAt).getTime();
|
|
2044
|
+
if (elapsed < CHECK_INTERVAL_MS) {
|
|
2045
|
+
if (cached.latest && cached.latest !== VERSION && cached.latest !== "unknown") {
|
|
2046
|
+
if (isNewerVersion(cached.latest, VERSION)) {
|
|
2047
|
+
showUpdateNotice(cached.latest);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
setTimeout(async () => {
|
|
2054
|
+
try {
|
|
2055
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
2056
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
2057
|
+
});
|
|
2058
|
+
if (!response.ok) return;
|
|
2059
|
+
const data = await response.json();
|
|
2060
|
+
const latest = data.version ?? "unknown";
|
|
2061
|
+
writeCache({ checkedAt: (/* @__PURE__ */ new Date()).toISOString(), latest });
|
|
2062
|
+
if (latest !== VERSION && latest !== "unknown" && isNewerVersion(latest, VERSION)) {
|
|
2063
|
+
showUpdateNotice(latest);
|
|
2064
|
+
}
|
|
2065
|
+
} catch {
|
|
2066
|
+
}
|
|
2067
|
+
}, 0);
|
|
1786
2068
|
}
|
|
1787
2069
|
|
|
1788
2070
|
// src/commands/doctor.ts
|
|
1789
2071
|
function addDoctorCommand(program2) {
|
|
1790
2072
|
program2.command("doctor").description("Check Elnora CLI configuration and connectivity").action(async () => {
|
|
1791
2073
|
const color = isColorEnabled();
|
|
1792
|
-
const ok = (msg) => color ? ` ${
|
|
1793
|
-
const fail = (msg) => color ? ` ${
|
|
1794
|
-
const warn = (msg) => color ? ` ${
|
|
2074
|
+
const ok = (msg) => color ? ` ${pc2.green("\u2713")} ${msg}` : ` \u2713 ${msg}`;
|
|
2075
|
+
const fail = (msg) => color ? ` ${pc2.red("\u2717")} ${msg}` : ` \u2717 ${msg}`;
|
|
2076
|
+
const warn = (msg) => color ? ` ${pc2.yellow("!")} ${msg}` : ` ! ${msg}`;
|
|
1795
2077
|
console.error(`
|
|
1796
2078
|
Elnora CLI v${VERSION}
|
|
1797
2079
|
`);
|
|
@@ -1832,7 +2114,7 @@ Elnora CLI v${VERSION}
|
|
|
1832
2114
|
if (res.ok) {
|
|
1833
2115
|
const data = await res.json();
|
|
1834
2116
|
const latest = data.version ?? "unknown";
|
|
1835
|
-
if (latest === VERSION || latest === "unknown") {
|
|
2117
|
+
if (latest === VERSION || latest === "unknown" || !isNewerVersion(latest, VERSION)) {
|
|
1836
2118
|
console.error(ok(`Version current v${VERSION} (latest: v${latest})`));
|
|
1837
2119
|
} else {
|
|
1838
2120
|
console.error(warn(`Update available v${VERSION} \u2192 v${latest}`));
|
|
@@ -1850,7 +2132,7 @@ Elnora CLI v${VERSION}
|
|
|
1850
2132
|
if (process.platform === "win32") {
|
|
1851
2133
|
console.error(ok("Config permissions (skipped on Windows)"));
|
|
1852
2134
|
passed++;
|
|
1853
|
-
} else if (!
|
|
2135
|
+
} else if (!existsSync3(PROFILES_FILE)) {
|
|
1854
2136
|
console.error(warn("Config permissions ~/.elnora/profiles.toml not found"));
|
|
1855
2137
|
} else {
|
|
1856
2138
|
const mode = statSync(PROFILES_FILE).mode & 511;
|
|
@@ -1878,7 +2160,7 @@ Elnora CLI v${VERSION}
|
|
|
1878
2160
|
console.error("All checks passed.");
|
|
1879
2161
|
} else {
|
|
1880
2162
|
console.error(`${passed}/${total} checks passed.`);
|
|
1881
|
-
if (!
|
|
2163
|
+
if (!existsSync3(PROFILES_FILE)) console.error("\nRun: elnora auth login");
|
|
1882
2164
|
}
|
|
1883
2165
|
});
|
|
1884
2166
|
}
|
|
@@ -2752,7 +3034,7 @@ var healthCheck = {
|
|
|
2752
3034
|
// src/commands/library/create-folder.ts
|
|
2753
3035
|
import { z as z51 } from "zod";
|
|
2754
3036
|
var inputSchema49 = z51.object({
|
|
2755
|
-
|
|
3037
|
+
orgId: z51.string().uuid().describe("Organization ID"),
|
|
2756
3038
|
name: z51.string().min(1).describe("Folder name"),
|
|
2757
3039
|
parent: z51.string().uuid().optional().describe("Parent folder ID")
|
|
2758
3040
|
});
|
|
@@ -2766,7 +3048,7 @@ var libraryCreateFolder = {
|
|
|
2766
3048
|
const body = { name: input.name };
|
|
2767
3049
|
if (input.parent) body.parentId = input.parent;
|
|
2768
3050
|
return ctx.client.post("library_folders", body, {
|
|
2769
|
-
pathParams: { orgId: input.
|
|
3051
|
+
pathParams: { orgId: input.orgId }
|
|
2770
3052
|
});
|
|
2771
3053
|
},
|
|
2772
3054
|
formatOutput(output, format) {
|
|
@@ -2778,7 +3060,7 @@ var libraryCreateFolder = {
|
|
|
2778
3060
|
// src/commands/library/delete-folder.ts
|
|
2779
3061
|
import { z as z52 } from "zod";
|
|
2780
3062
|
var inputSchema50 = z52.object({
|
|
2781
|
-
|
|
3063
|
+
orgId: z52.string().uuid().describe("Organization ID"),
|
|
2782
3064
|
folderId: z52.string().uuid().describe("Folder ID to delete")
|
|
2783
3065
|
});
|
|
2784
3066
|
var libraryDeleteFolder = {
|
|
@@ -2790,7 +3072,7 @@ var libraryDeleteFolder = {
|
|
|
2790
3072
|
annotations: { destructiveHint: true },
|
|
2791
3073
|
async execute(input, ctx) {
|
|
2792
3074
|
await ctx.client.del("library_folder", {
|
|
2793
|
-
pathParams: { orgId: input.
|
|
3075
|
+
pathParams: { orgId: input.orgId, id: input.folderId }
|
|
2794
3076
|
});
|
|
2795
3077
|
return { deleted: true, folderId: input.folderId };
|
|
2796
3078
|
},
|
|
@@ -2803,7 +3085,7 @@ var libraryDeleteFolder = {
|
|
|
2803
3085
|
// src/commands/library/files.ts
|
|
2804
3086
|
import { z as z53 } from "zod";
|
|
2805
3087
|
var inputSchema51 = z53.object({
|
|
2806
|
-
|
|
3088
|
+
orgId: z53.string().uuid().describe("Organization ID"),
|
|
2807
3089
|
...paginationInput
|
|
2808
3090
|
});
|
|
2809
3091
|
var libraryFiles = {
|
|
@@ -2815,7 +3097,7 @@ var libraryFiles = {
|
|
|
2815
3097
|
annotations: { readOnlyHint: true },
|
|
2816
3098
|
async execute(input, ctx) {
|
|
2817
3099
|
return ctx.client.get("library_files", {
|
|
2818
|
-
pathParams: { orgId: input.
|
|
3100
|
+
pathParams: { orgId: input.orgId },
|
|
2819
3101
|
queryParams: { page: input.page, pageSize: input.pageSize }
|
|
2820
3102
|
});
|
|
2821
3103
|
},
|
|
@@ -2828,7 +3110,7 @@ var libraryFiles = {
|
|
|
2828
3110
|
// src/commands/library/folders.ts
|
|
2829
3111
|
import { z as z54 } from "zod";
|
|
2830
3112
|
var inputSchema52 = z54.object({
|
|
2831
|
-
|
|
3113
|
+
orgId: z54.string().uuid().describe("Organization ID")
|
|
2832
3114
|
});
|
|
2833
3115
|
var libraryFolders = {
|
|
2834
3116
|
name: "library.folders",
|
|
@@ -2839,7 +3121,7 @@ var libraryFolders = {
|
|
|
2839
3121
|
annotations: { readOnlyHint: true },
|
|
2840
3122
|
async execute(input, ctx) {
|
|
2841
3123
|
return ctx.client.get("library_folders", {
|
|
2842
|
-
pathParams: { orgId: input.
|
|
3124
|
+
pathParams: { orgId: input.orgId }
|
|
2843
3125
|
});
|
|
2844
3126
|
},
|
|
2845
3127
|
formatOutput(output, format) {
|
|
@@ -2851,7 +3133,7 @@ var libraryFolders = {
|
|
|
2851
3133
|
// src/commands/library/rename-folder.ts
|
|
2852
3134
|
import { z as z55 } from "zod";
|
|
2853
3135
|
var inputSchema53 = z55.object({
|
|
2854
|
-
|
|
3136
|
+
orgId: z55.string().uuid().describe("Organization ID"),
|
|
2855
3137
|
folderId: z55.string().uuid().describe("Folder ID to rename"),
|
|
2856
3138
|
name: z55.string().min(1).describe("New folder name")
|
|
2857
3139
|
});
|
|
@@ -2866,7 +3148,7 @@ var libraryRenameFolder = {
|
|
|
2866
3148
|
"library_folder",
|
|
2867
3149
|
{ name: input.name },
|
|
2868
3150
|
{
|
|
2869
|
-
pathParams: { orgId: input.
|
|
3151
|
+
pathParams: { orgId: input.orgId, id: input.folderId }
|
|
2870
3152
|
}
|
|
2871
3153
|
);
|
|
2872
3154
|
},
|
|
@@ -3174,10 +3456,14 @@ var orgsInvite = {
|
|
|
3174
3456
|
pathParams: { orgId: input.orgId }
|
|
3175
3457
|
});
|
|
3176
3458
|
const items = Array.isArray(existing) ? existing : existing?.items ?? [];
|
|
3177
|
-
const match = items.find(
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3459
|
+
const match = items.find((inv) => {
|
|
3460
|
+
const i = inv;
|
|
3461
|
+
return i.email?.toLowerCase() === input.email.toLowerCase();
|
|
3462
|
+
});
|
|
3463
|
+
if (match) {
|
|
3464
|
+
const matchId = match.id;
|
|
3465
|
+
return ctx.client.post("org_invitation_resend", {}, { pathParams: { orgId: input.orgId, invId: matchId } });
|
|
3466
|
+
}
|
|
3181
3467
|
return ctx.client.post(
|
|
3182
3468
|
"org_invitations",
|
|
3183
3469
|
{ email: input.email, role: input.role ?? "Member" },
|
|
@@ -3276,17 +3562,43 @@ var orgsRemoveMember = {
|
|
|
3276
3562
|
}
|
|
3277
3563
|
};
|
|
3278
3564
|
|
|
3279
|
-
// src/commands/orgs/
|
|
3565
|
+
// src/commands/orgs/resend-invite.ts
|
|
3280
3566
|
import { z as z70 } from "zod";
|
|
3281
3567
|
var inputSchema68 = z70.object({
|
|
3282
|
-
orgId: z70.string().uuid().describe("Organization ID
|
|
3568
|
+
orgId: z70.string().uuid().describe("Organization ID"),
|
|
3569
|
+
invitationId: z70.string().uuid().describe("Invitation ID to resend")
|
|
3570
|
+
});
|
|
3571
|
+
var orgsResendInvite = {
|
|
3572
|
+
name: "orgs.resendInvite",
|
|
3573
|
+
group: "orgs",
|
|
3574
|
+
description: "Resend an organization invitation email. Regenerates the token and extends the expiry by 7 days. Works on both pending and expired invitations, preserves the invitation ID.",
|
|
3575
|
+
inputSchema: inputSchema68,
|
|
3576
|
+
outputSchema: z70.any(),
|
|
3577
|
+
annotations: { idempotentHint: true },
|
|
3578
|
+
async execute(input, ctx) {
|
|
3579
|
+
return ctx.client.post(
|
|
3580
|
+
"org_invitation_resend",
|
|
3581
|
+
{},
|
|
3582
|
+
{ pathParams: { orgId: input.orgId, invId: input.invitationId } }
|
|
3583
|
+
);
|
|
3584
|
+
},
|
|
3585
|
+
formatOutput(output, format) {
|
|
3586
|
+
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
3587
|
+
return JSON.stringify(output, null, 2);
|
|
3588
|
+
}
|
|
3589
|
+
};
|
|
3590
|
+
|
|
3591
|
+
// src/commands/orgs/set-default.ts
|
|
3592
|
+
import { z as z71 } from "zod";
|
|
3593
|
+
var inputSchema69 = z71.object({
|
|
3594
|
+
orgId: z71.string().uuid().describe("Organization ID to set as default")
|
|
3283
3595
|
});
|
|
3284
3596
|
var orgsSetDefault = {
|
|
3285
3597
|
name: "orgs.setDefault",
|
|
3286
3598
|
group: "orgs",
|
|
3287
3599
|
description: "Set an organization as the default",
|
|
3288
|
-
inputSchema:
|
|
3289
|
-
outputSchema:
|
|
3600
|
+
inputSchema: inputSchema69,
|
|
3601
|
+
outputSchema: z71.any(),
|
|
3290
3602
|
annotations: { idempotentHint: true },
|
|
3291
3603
|
async execute(input, ctx) {
|
|
3292
3604
|
return ctx.client.put("organization_set_default", void 0, {
|
|
@@ -3300,17 +3612,17 @@ var orgsSetDefault = {
|
|
|
3300
3612
|
};
|
|
3301
3613
|
|
|
3302
3614
|
// src/commands/orgs/set-stripe.ts
|
|
3303
|
-
import { z as
|
|
3304
|
-
var
|
|
3305
|
-
orgId:
|
|
3306
|
-
customerId:
|
|
3615
|
+
import { z as z72 } from "zod";
|
|
3616
|
+
var inputSchema70 = z72.object({
|
|
3617
|
+
orgId: z72.string().uuid().describe("Organization ID"),
|
|
3618
|
+
customerId: z72.string().min(1).describe("Stripe customer ID (e.g. cus_xxx)")
|
|
3307
3619
|
});
|
|
3308
3620
|
var orgsSetStripe = {
|
|
3309
3621
|
name: "orgs.setStripe",
|
|
3310
3622
|
group: "orgs",
|
|
3311
3623
|
description: "Set the Stripe customer ID for an organization",
|
|
3312
|
-
inputSchema:
|
|
3313
|
-
outputSchema:
|
|
3624
|
+
inputSchema: inputSchema70,
|
|
3625
|
+
outputSchema: z72.any(),
|
|
3314
3626
|
annotations: { idempotentHint: true },
|
|
3315
3627
|
async execute(input, ctx) {
|
|
3316
3628
|
return ctx.client.put(
|
|
@@ -3327,18 +3639,18 @@ var orgsSetStripe = {
|
|
|
3327
3639
|
|
|
3328
3640
|
// src/commands/orgs/update.ts
|
|
3329
3641
|
init_errors();
|
|
3330
|
-
import { z as
|
|
3331
|
-
var
|
|
3332
|
-
orgId:
|
|
3333
|
-
name:
|
|
3334
|
-
description:
|
|
3642
|
+
import { z as z73 } from "zod";
|
|
3643
|
+
var inputSchema71 = z73.object({
|
|
3644
|
+
orgId: z73.string().uuid().describe("Organization ID"),
|
|
3645
|
+
name: z73.string().min(1).optional().describe("New organization name"),
|
|
3646
|
+
description: z73.string().optional().describe("New organization description")
|
|
3335
3647
|
});
|
|
3336
3648
|
var orgsUpdate = {
|
|
3337
3649
|
name: "orgs.update",
|
|
3338
3650
|
group: "orgs",
|
|
3339
3651
|
description: "Update an existing organization",
|
|
3340
|
-
inputSchema:
|
|
3341
|
-
outputSchema:
|
|
3652
|
+
inputSchema: inputSchema71,
|
|
3653
|
+
outputSchema: z73.any(),
|
|
3342
3654
|
annotations: { idempotentHint: true },
|
|
3343
3655
|
async execute(input, ctx) {
|
|
3344
3656
|
const { orgId, ...fields } = input;
|
|
@@ -3359,18 +3671,18 @@ var orgsUpdate = {
|
|
|
3359
3671
|
};
|
|
3360
3672
|
|
|
3361
3673
|
// src/commands/orgs/update-role.ts
|
|
3362
|
-
import { z as
|
|
3363
|
-
var
|
|
3364
|
-
orgId:
|
|
3365
|
-
membershipId:
|
|
3366
|
-
role:
|
|
3674
|
+
import { z as z74 } from "zod";
|
|
3675
|
+
var inputSchema72 = z74.object({
|
|
3676
|
+
orgId: z74.string().uuid().describe("Organization ID"),
|
|
3677
|
+
membershipId: z74.string().uuid().describe("Membership ID of the member"),
|
|
3678
|
+
role: z74.string().min(1).describe("New role to assign")
|
|
3367
3679
|
});
|
|
3368
3680
|
var orgsUpdateRole = {
|
|
3369
3681
|
name: "orgs.updateRole",
|
|
3370
3682
|
group: "orgs",
|
|
3371
3683
|
description: "Update an organization member's role",
|
|
3372
|
-
inputSchema:
|
|
3373
|
-
outputSchema:
|
|
3684
|
+
inputSchema: inputSchema72,
|
|
3685
|
+
outputSchema: z74.any(),
|
|
3374
3686
|
annotations: { idempotentHint: true },
|
|
3375
3687
|
async execute(input, ctx) {
|
|
3376
3688
|
return ctx.client.put(
|
|
@@ -3402,6 +3714,7 @@ function registerOrgCommands() {
|
|
|
3402
3714
|
orgsInvite,
|
|
3403
3715
|
orgsInvitations,
|
|
3404
3716
|
orgsCancelInvite,
|
|
3717
|
+
orgsResendInvite,
|
|
3405
3718
|
orgsInvitationInfo,
|
|
3406
3719
|
orgsAcceptInvite,
|
|
3407
3720
|
orgsFiles,
|
|
@@ -3410,18 +3723,18 @@ function registerOrgCommands() {
|
|
|
3410
3723
|
}
|
|
3411
3724
|
|
|
3412
3725
|
// src/commands/projects/add-member.ts
|
|
3413
|
-
import { z as
|
|
3414
|
-
var
|
|
3415
|
-
projectId:
|
|
3416
|
-
userId:
|
|
3417
|
-
role:
|
|
3726
|
+
import { z as z75 } from "zod";
|
|
3727
|
+
var inputSchema73 = z75.object({
|
|
3728
|
+
projectId: z75.string().uuid().describe("Project ID"),
|
|
3729
|
+
userId: z75.string().uuid().describe("User ID to add"),
|
|
3730
|
+
role: z75.string().default("Member").describe("Role to assign (default: Member)")
|
|
3418
3731
|
});
|
|
3419
3732
|
var projectsAddMember = {
|
|
3420
3733
|
name: "projects.addMember",
|
|
3421
3734
|
group: "projects",
|
|
3422
3735
|
description: "Add a member to a project",
|
|
3423
|
-
inputSchema:
|
|
3424
|
-
outputSchema:
|
|
3736
|
+
inputSchema: inputSchema73,
|
|
3737
|
+
outputSchema: z75.any(),
|
|
3425
3738
|
async execute(input, ctx) {
|
|
3426
3739
|
return ctx.client.post(
|
|
3427
3740
|
"project_members",
|
|
@@ -3436,16 +3749,16 @@ var projectsAddMember = {
|
|
|
3436
3749
|
};
|
|
3437
3750
|
|
|
3438
3751
|
// src/commands/projects/archive.ts
|
|
3439
|
-
import { z as
|
|
3440
|
-
var
|
|
3441
|
-
projectId:
|
|
3752
|
+
import { z as z76 } from "zod";
|
|
3753
|
+
var inputSchema74 = z76.object({
|
|
3754
|
+
projectId: z76.string().uuid().describe("Project ID to archive")
|
|
3442
3755
|
});
|
|
3443
3756
|
var projectsArchive = {
|
|
3444
3757
|
name: "projects.archive",
|
|
3445
3758
|
group: "projects",
|
|
3446
3759
|
description: "Archive (delete) a project",
|
|
3447
|
-
inputSchema:
|
|
3448
|
-
outputSchema:
|
|
3760
|
+
inputSchema: inputSchema74,
|
|
3761
|
+
outputSchema: z76.any(),
|
|
3449
3762
|
annotations: { destructiveHint: true },
|
|
3450
3763
|
async execute(input, ctx) {
|
|
3451
3764
|
await ctx.client.del("project", {
|
|
@@ -3460,18 +3773,18 @@ var projectsArchive = {
|
|
|
3460
3773
|
};
|
|
3461
3774
|
|
|
3462
3775
|
// src/commands/projects/create.ts
|
|
3463
|
-
import { z as
|
|
3464
|
-
var
|
|
3465
|
-
name:
|
|
3466
|
-
description:
|
|
3467
|
-
icon:
|
|
3776
|
+
import { z as z77 } from "zod";
|
|
3777
|
+
var inputSchema75 = z77.object({
|
|
3778
|
+
name: z77.string().min(1).describe("Project name"),
|
|
3779
|
+
description: z77.string().optional().describe("Project description"),
|
|
3780
|
+
icon: z77.string().optional().describe("Project icon")
|
|
3468
3781
|
});
|
|
3469
3782
|
var projectsCreate = {
|
|
3470
3783
|
name: "projects.create",
|
|
3471
3784
|
group: "projects",
|
|
3472
3785
|
description: "Create a new project",
|
|
3473
|
-
inputSchema:
|
|
3474
|
-
outputSchema:
|
|
3786
|
+
inputSchema: inputSchema75,
|
|
3787
|
+
outputSchema: z77.any(),
|
|
3475
3788
|
async execute(input, ctx) {
|
|
3476
3789
|
return ctx.client.post("projects", input);
|
|
3477
3790
|
},
|
|
@@ -3482,17 +3795,17 @@ var projectsCreate = {
|
|
|
3482
3795
|
};
|
|
3483
3796
|
|
|
3484
3797
|
// src/commands/projects/get.ts
|
|
3485
|
-
import { z as
|
|
3486
|
-
var
|
|
3487
|
-
projectId:
|
|
3798
|
+
import { z as z78 } from "zod";
|
|
3799
|
+
var inputSchema76 = z78.object({
|
|
3800
|
+
projectId: z78.string().uuid().describe("Project ID")
|
|
3488
3801
|
});
|
|
3489
3802
|
var projectsGet = {
|
|
3490
3803
|
name: "projects.get",
|
|
3491
3804
|
group: "projects",
|
|
3492
3805
|
description: "Get details of a specific project",
|
|
3493
|
-
inputSchema:
|
|
3494
|
-
outputSchema:
|
|
3495
|
-
annotations: { readOnlyHint: true },
|
|
3806
|
+
inputSchema: inputSchema76,
|
|
3807
|
+
outputSchema: z78.any(),
|
|
3808
|
+
annotations: { readOnlyHint: true },
|
|
3496
3809
|
async execute(input, ctx) {
|
|
3497
3810
|
return ctx.client.get("project", {
|
|
3498
3811
|
pathParams: { id: input.projectId }
|
|
@@ -3505,16 +3818,16 @@ var projectsGet = {
|
|
|
3505
3818
|
};
|
|
3506
3819
|
|
|
3507
3820
|
// src/commands/projects/leave.ts
|
|
3508
|
-
import { z as
|
|
3509
|
-
var
|
|
3510
|
-
projectId:
|
|
3821
|
+
import { z as z79 } from "zod";
|
|
3822
|
+
var inputSchema77 = z79.object({
|
|
3823
|
+
projectId: z79.string().uuid().describe("Project ID to leave")
|
|
3511
3824
|
});
|
|
3512
3825
|
var projectsLeave = {
|
|
3513
3826
|
name: "projects.leave",
|
|
3514
3827
|
group: "projects",
|
|
3515
3828
|
description: "Leave a project",
|
|
3516
|
-
inputSchema:
|
|
3517
|
-
outputSchema:
|
|
3829
|
+
inputSchema: inputSchema77,
|
|
3830
|
+
outputSchema: z79.any(),
|
|
3518
3831
|
annotations: { destructiveHint: true },
|
|
3519
3832
|
async execute(input, ctx) {
|
|
3520
3833
|
return ctx.client.post("project_leave", void 0, {
|
|
@@ -3528,16 +3841,16 @@ var projectsLeave = {
|
|
|
3528
3841
|
};
|
|
3529
3842
|
|
|
3530
3843
|
// src/commands/projects/list.ts
|
|
3531
|
-
import { z as
|
|
3532
|
-
var
|
|
3844
|
+
import { z as z80 } from "zod";
|
|
3845
|
+
var inputSchema78 = z80.object({
|
|
3533
3846
|
...paginationInput
|
|
3534
3847
|
});
|
|
3535
3848
|
var projectsList = {
|
|
3536
3849
|
name: "projects.list",
|
|
3537
3850
|
group: "projects",
|
|
3538
3851
|
description: "List all projects accessible to the current user",
|
|
3539
|
-
inputSchema:
|
|
3540
|
-
outputSchema:
|
|
3852
|
+
inputSchema: inputSchema78,
|
|
3853
|
+
outputSchema: z80.any(),
|
|
3541
3854
|
annotations: { readOnlyHint: true },
|
|
3542
3855
|
async execute(input, ctx) {
|
|
3543
3856
|
return ctx.client.get("projects", {
|
|
@@ -3551,16 +3864,16 @@ var projectsList = {
|
|
|
3551
3864
|
};
|
|
3552
3865
|
|
|
3553
3866
|
// src/commands/projects/members.ts
|
|
3554
|
-
import { z as
|
|
3555
|
-
var
|
|
3556
|
-
projectId:
|
|
3867
|
+
import { z as z81 } from "zod";
|
|
3868
|
+
var inputSchema79 = z81.object({
|
|
3869
|
+
projectId: z81.string().uuid().describe("Project ID")
|
|
3557
3870
|
});
|
|
3558
3871
|
var projectsMembers = {
|
|
3559
3872
|
name: "projects.members",
|
|
3560
3873
|
group: "projects",
|
|
3561
3874
|
description: "List members of a project",
|
|
3562
|
-
inputSchema:
|
|
3563
|
-
outputSchema:
|
|
3875
|
+
inputSchema: inputSchema79,
|
|
3876
|
+
outputSchema: z81.any(),
|
|
3564
3877
|
annotations: { readOnlyHint: true },
|
|
3565
3878
|
async execute(input, ctx) {
|
|
3566
3879
|
return ctx.client.get("project_members", {
|
|
@@ -3574,17 +3887,17 @@ var projectsMembers = {
|
|
|
3574
3887
|
};
|
|
3575
3888
|
|
|
3576
3889
|
// src/commands/projects/remove-member.ts
|
|
3577
|
-
import { z as
|
|
3578
|
-
var
|
|
3579
|
-
projectId:
|
|
3580
|
-
userId:
|
|
3890
|
+
import { z as z82 } from "zod";
|
|
3891
|
+
var inputSchema80 = z82.object({
|
|
3892
|
+
projectId: z82.string().uuid().describe("Project ID"),
|
|
3893
|
+
userId: z82.string().uuid().describe("User ID to remove")
|
|
3581
3894
|
});
|
|
3582
3895
|
var projectsRemoveMember = {
|
|
3583
3896
|
name: "projects.removeMember",
|
|
3584
3897
|
group: "projects",
|
|
3585
3898
|
description: "Remove a member from a project",
|
|
3586
|
-
inputSchema:
|
|
3587
|
-
outputSchema:
|
|
3899
|
+
inputSchema: inputSchema80,
|
|
3900
|
+
outputSchema: z82.any(),
|
|
3588
3901
|
annotations: { destructiveHint: true },
|
|
3589
3902
|
async execute(input, ctx) {
|
|
3590
3903
|
await ctx.client.del("project_member", {
|
|
@@ -3600,19 +3913,19 @@ var projectsRemoveMember = {
|
|
|
3600
3913
|
|
|
3601
3914
|
// src/commands/projects/update.ts
|
|
3602
3915
|
init_errors();
|
|
3603
|
-
import { z as
|
|
3604
|
-
var
|
|
3605
|
-
projectId:
|
|
3606
|
-
name:
|
|
3607
|
-
description:
|
|
3608
|
-
icon:
|
|
3916
|
+
import { z as z83 } from "zod";
|
|
3917
|
+
var inputSchema81 = z83.object({
|
|
3918
|
+
projectId: z83.string().uuid().describe("Project ID"),
|
|
3919
|
+
name: z83.string().min(1).optional().describe("New project name"),
|
|
3920
|
+
description: z83.string().optional().describe("New project description"),
|
|
3921
|
+
icon: z83.string().optional().describe("New project icon")
|
|
3609
3922
|
});
|
|
3610
3923
|
var projectsUpdate = {
|
|
3611
3924
|
name: "projects.update",
|
|
3612
3925
|
group: "projects",
|
|
3613
3926
|
description: "Update an existing project",
|
|
3614
|
-
inputSchema:
|
|
3615
|
-
outputSchema:
|
|
3927
|
+
inputSchema: inputSchema81,
|
|
3928
|
+
outputSchema: z83.any(),
|
|
3616
3929
|
annotations: { idempotentHint: true },
|
|
3617
3930
|
async execute(input, ctx) {
|
|
3618
3931
|
const { projectId, ...fields } = input;
|
|
@@ -3633,18 +3946,18 @@ var projectsUpdate = {
|
|
|
3633
3946
|
};
|
|
3634
3947
|
|
|
3635
3948
|
// src/commands/projects/update-role.ts
|
|
3636
|
-
import { z as
|
|
3637
|
-
var
|
|
3638
|
-
projectId:
|
|
3639
|
-
userId:
|
|
3640
|
-
role:
|
|
3949
|
+
import { z as z84 } from "zod";
|
|
3950
|
+
var inputSchema82 = z84.object({
|
|
3951
|
+
projectId: z84.string().uuid().describe("Project ID"),
|
|
3952
|
+
userId: z84.string().uuid().describe("User ID of the member"),
|
|
3953
|
+
role: z84.string().min(1).describe("New role to assign")
|
|
3641
3954
|
});
|
|
3642
3955
|
var projectsUpdateRole = {
|
|
3643
3956
|
name: "projects.updateRole",
|
|
3644
3957
|
group: "projects",
|
|
3645
3958
|
description: "Update a project member's role",
|
|
3646
|
-
inputSchema:
|
|
3647
|
-
outputSchema:
|
|
3959
|
+
inputSchema: inputSchema82,
|
|
3960
|
+
outputSchema: z84.any(),
|
|
3648
3961
|
annotations: { idempotentHint: true },
|
|
3649
3962
|
async execute(input, ctx) {
|
|
3650
3963
|
return ctx.client.put(
|
|
@@ -3676,17 +3989,17 @@ function registerProjectCommands() {
|
|
|
3676
3989
|
}
|
|
3677
3990
|
|
|
3678
3991
|
// src/commands/search/all.ts
|
|
3679
|
-
import { z as
|
|
3680
|
-
var
|
|
3681
|
-
query:
|
|
3992
|
+
import { z as z85 } from "zod";
|
|
3993
|
+
var inputSchema83 = z85.object({
|
|
3994
|
+
query: z85.string().min(1).describe("Search query"),
|
|
3682
3995
|
...paginationInput
|
|
3683
3996
|
});
|
|
3684
3997
|
var searchAll = {
|
|
3685
3998
|
name: "search.all",
|
|
3686
3999
|
group: "search",
|
|
3687
4000
|
description: "Search across all entities",
|
|
3688
|
-
inputSchema:
|
|
3689
|
-
outputSchema:
|
|
4001
|
+
inputSchema: inputSchema83,
|
|
4002
|
+
outputSchema: z85.any(),
|
|
3690
4003
|
annotations: { readOnlyHint: true },
|
|
3691
4004
|
async execute(input, ctx) {
|
|
3692
4005
|
return ctx.client.get("search_all", {
|
|
@@ -3700,18 +4013,18 @@ var searchAll = {
|
|
|
3700
4013
|
};
|
|
3701
4014
|
|
|
3702
4015
|
// src/commands/search/file-content.ts
|
|
3703
|
-
import { z as
|
|
3704
|
-
var
|
|
3705
|
-
query:
|
|
3706
|
-
projectId:
|
|
4016
|
+
import { z as z86 } from "zod";
|
|
4017
|
+
var inputSchema84 = z86.object({
|
|
4018
|
+
query: z86.string().min(1).describe("Search query"),
|
|
4019
|
+
projectId: z86.string().uuid().optional().describe("Filter by project ID"),
|
|
3707
4020
|
...paginationInput
|
|
3708
4021
|
});
|
|
3709
4022
|
var searchFileContent = {
|
|
3710
4023
|
name: "search.fileContent",
|
|
3711
4024
|
group: "search",
|
|
3712
4025
|
description: "Search within file contents",
|
|
3713
|
-
inputSchema:
|
|
3714
|
-
outputSchema:
|
|
4026
|
+
inputSchema: inputSchema84,
|
|
4027
|
+
outputSchema: z86.any(),
|
|
3715
4028
|
annotations: { readOnlyHint: true },
|
|
3716
4029
|
async execute(input, ctx) {
|
|
3717
4030
|
const queryParams = {
|
|
@@ -3729,17 +4042,17 @@ var searchFileContent = {
|
|
|
3729
4042
|
};
|
|
3730
4043
|
|
|
3731
4044
|
// src/commands/search/files.ts
|
|
3732
|
-
import { z as
|
|
3733
|
-
var
|
|
3734
|
-
query:
|
|
4045
|
+
import { z as z87 } from "zod";
|
|
4046
|
+
var inputSchema85 = z87.object({
|
|
4047
|
+
query: z87.string().min(1).describe("Search query"),
|
|
3735
4048
|
...paginationInput
|
|
3736
4049
|
});
|
|
3737
4050
|
var searchFiles = {
|
|
3738
4051
|
name: "search.files",
|
|
3739
4052
|
group: "search",
|
|
3740
4053
|
description: "Search files by query",
|
|
3741
|
-
inputSchema:
|
|
3742
|
-
outputSchema:
|
|
4054
|
+
inputSchema: inputSchema85,
|
|
4055
|
+
outputSchema: z87.any(),
|
|
3743
4056
|
annotations: { readOnlyHint: true },
|
|
3744
4057
|
async execute(input, ctx) {
|
|
3745
4058
|
return ctx.client.get("search_files", {
|
|
@@ -3753,17 +4066,17 @@ var searchFiles = {
|
|
|
3753
4066
|
};
|
|
3754
4067
|
|
|
3755
4068
|
// src/commands/search/tasks.ts
|
|
3756
|
-
import { z as
|
|
3757
|
-
var
|
|
3758
|
-
query:
|
|
4069
|
+
import { z as z88 } from "zod";
|
|
4070
|
+
var inputSchema86 = z88.object({
|
|
4071
|
+
query: z88.string().min(1).describe("Search query"),
|
|
3759
4072
|
...paginationInput
|
|
3760
4073
|
});
|
|
3761
4074
|
var searchTasks = {
|
|
3762
4075
|
name: "search.tasks",
|
|
3763
4076
|
group: "search",
|
|
3764
4077
|
description: "Search tasks by query",
|
|
3765
|
-
inputSchema:
|
|
3766
|
-
outputSchema:
|
|
4078
|
+
inputSchema: inputSchema86,
|
|
4079
|
+
outputSchema: z88.any(),
|
|
3767
4080
|
annotations: { readOnlyHint: true },
|
|
3768
4081
|
async execute(input, ctx) {
|
|
3769
4082
|
return ctx.client.get("search_tasks", {
|
|
@@ -3781,17 +4094,125 @@ function registerSearchCommands() {
|
|
|
3781
4094
|
return [searchTasks, searchFiles, searchAll, searchFileContent];
|
|
3782
4095
|
}
|
|
3783
4096
|
|
|
4097
|
+
// src/commands/setup-claude.ts
|
|
4098
|
+
init_config();
|
|
4099
|
+
import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4100
|
+
import { homedir as homedir3 } from "node:os";
|
|
4101
|
+
import { dirname, join as join3 } from "node:path";
|
|
4102
|
+
import { fileURLToPath } from "node:url";
|
|
4103
|
+
import pc3 from "picocolors";
|
|
4104
|
+
var __script_dir;
|
|
4105
|
+
try {
|
|
4106
|
+
__script_dir = dirname(fileURLToPath(import.meta.url));
|
|
4107
|
+
} catch {
|
|
4108
|
+
__script_dir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
4109
|
+
}
|
|
4110
|
+
var ELNORA_DIR = join3(homedir3(), ".elnora");
|
|
4111
|
+
var PLUGIN_DIR = join3(ELNORA_DIR, "plugin", "elnora");
|
|
4112
|
+
var CLAUDE_DIR = join3(homedir3(), ".claude");
|
|
4113
|
+
var MARKETPLACES_FILE = join3(CLAUDE_DIR, "plugins", "known_marketplaces.json");
|
|
4114
|
+
var SETTINGS_FILE = join3(CLAUDE_DIR, "settings.json");
|
|
4115
|
+
var MARKETPLACE_NAME = "elnora-local";
|
|
4116
|
+
var PLUGIN_ID = `elnora@${MARKETPLACE_NAME}`;
|
|
4117
|
+
function findSkillsDir() {
|
|
4118
|
+
const candidates = [
|
|
4119
|
+
join3(__script_dir, "../../skills"),
|
|
4120
|
+
join3(__script_dir, "../skills"),
|
|
4121
|
+
// When running from source via tsx
|
|
4122
|
+
join3(__script_dir, "../../../skills"),
|
|
4123
|
+
// Standalone binary: skills extracted to ~/.elnora/skills/
|
|
4124
|
+
join3(ELNORA_DIR, "skills")
|
|
4125
|
+
];
|
|
4126
|
+
for (const dir of candidates) {
|
|
4127
|
+
if (existsSync4(join3(dir, "elnora-platform", "SKILL.md"))) {
|
|
4128
|
+
return dir;
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
return null;
|
|
4132
|
+
}
|
|
4133
|
+
function readJsonFile(path) {
|
|
4134
|
+
try {
|
|
4135
|
+
return JSON.parse(readFileSync5(path, "utf-8"));
|
|
4136
|
+
} catch {
|
|
4137
|
+
return {};
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4140
|
+
function writeJsonFile(path, data) {
|
|
4141
|
+
mkdirSync3(join3(path, ".."), { recursive: true });
|
|
4142
|
+
writeFileSync3(path, `${JSON.stringify(data, null, 2)}
|
|
4143
|
+
`, "utf-8");
|
|
4144
|
+
}
|
|
4145
|
+
function addSetupClaudeCommand(program2) {
|
|
4146
|
+
program2.command("setup-claude").description("Install Elnora skills for Claude Code").action(() => {
|
|
4147
|
+
const color = isColorEnabled();
|
|
4148
|
+
const ok = (msg) => color ? ` ${pc3.green("\u2713")} ${msg}` : ` \u2713 ${msg}`;
|
|
4149
|
+
const fail = (msg) => color ? ` ${pc3.red("\u2717")} ${msg}` : ` \u2717 ${msg}`;
|
|
4150
|
+
console.error("");
|
|
4151
|
+
if (!existsSync4(CLAUDE_DIR)) {
|
|
4152
|
+
console.error(fail("Claude Code not found (~/.claude/ does not exist)"));
|
|
4153
|
+
console.error("");
|
|
4154
|
+
console.error(" Install Claude Code first: https://claude.ai/code");
|
|
4155
|
+
process.exit(1);
|
|
4156
|
+
}
|
|
4157
|
+
console.error(ok("Claude Code found"));
|
|
4158
|
+
const skillsDir = findSkillsDir();
|
|
4159
|
+
if (!skillsDir) {
|
|
4160
|
+
console.error(fail("Skills not found"));
|
|
4161
|
+
console.error("");
|
|
4162
|
+
console.error(" Skills may not be bundled with your install method.");
|
|
4163
|
+
console.error(" Try: npm install -g @elnora-ai/cli");
|
|
4164
|
+
process.exit(1);
|
|
4165
|
+
}
|
|
4166
|
+
console.error(ok(`Skills found at ${skillsDir}`));
|
|
4167
|
+
mkdirSync3(PLUGIN_DIR, { recursive: true });
|
|
4168
|
+
writeJsonFile(join3(PLUGIN_DIR, ".claude-plugin", "plugin.json"), {
|
|
4169
|
+
name: "elnora",
|
|
4170
|
+
version: VERSION,
|
|
4171
|
+
description: "AI-powered bioprotocol generation and lab workflow management. Connect to the Elnora AI Platform to generate, optimize, and manage bioprotocols.",
|
|
4172
|
+
author: { name: "Elnora AI", url: "https://elnora.ai" },
|
|
4173
|
+
repository: "https://github.com/Elnora-AI/elnora-cli",
|
|
4174
|
+
homepage: "https://elnora.ai",
|
|
4175
|
+
license: "Apache-2.0",
|
|
4176
|
+
keywords: ["bioprotocol", "lab", "pharma", "life-sciences"]
|
|
4177
|
+
});
|
|
4178
|
+
cpSync(skillsDir, join3(PLUGIN_DIR, "skills"), { recursive: true });
|
|
4179
|
+
console.error(ok("Skills installed to ~/.elnora/plugin/"));
|
|
4180
|
+
const marketplaces = readJsonFile(MARKETPLACES_FILE);
|
|
4181
|
+
marketplaces[MARKETPLACE_NAME] = {
|
|
4182
|
+
source: {
|
|
4183
|
+
source: "directory",
|
|
4184
|
+
path: join3(ELNORA_DIR, "plugin")
|
|
4185
|
+
},
|
|
4186
|
+
installLocation: join3(ELNORA_DIR, "plugin"),
|
|
4187
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
4188
|
+
};
|
|
4189
|
+
writeJsonFile(MARKETPLACES_FILE, marketplaces);
|
|
4190
|
+
console.error(ok("Marketplace registered in Claude Code"));
|
|
4191
|
+
const settings = readJsonFile(SETTINGS_FILE);
|
|
4192
|
+
if (!settings.enabledPlugins || typeof settings.enabledPlugins !== "object") {
|
|
4193
|
+
settings.enabledPlugins = {};
|
|
4194
|
+
}
|
|
4195
|
+
settings.enabledPlugins[PLUGIN_ID] = true;
|
|
4196
|
+
writeJsonFile(SETTINGS_FILE, settings);
|
|
4197
|
+
console.error(ok("Plugin enabled"));
|
|
4198
|
+
console.error("");
|
|
4199
|
+
console.error(" Elnora skills are now available in Claude Code.");
|
|
4200
|
+
console.error(" Restart Claude Code if it's currently running.");
|
|
4201
|
+
console.error("");
|
|
4202
|
+
});
|
|
4203
|
+
}
|
|
4204
|
+
|
|
3784
4205
|
// src/commands/tasks/archive.ts
|
|
3785
|
-
import { z as
|
|
3786
|
-
var
|
|
3787
|
-
taskId:
|
|
4206
|
+
import { z as z89 } from "zod";
|
|
4207
|
+
var inputSchema87 = z89.object({
|
|
4208
|
+
taskId: z89.string().uuid().describe("Task ID to archive")
|
|
3788
4209
|
});
|
|
3789
4210
|
var tasksArchive = {
|
|
3790
4211
|
name: "tasks.archive",
|
|
3791
4212
|
group: "tasks",
|
|
3792
4213
|
description: "Archive (delete) a task",
|
|
3793
|
-
inputSchema:
|
|
3794
|
-
outputSchema:
|
|
4214
|
+
inputSchema: inputSchema87,
|
|
4215
|
+
outputSchema: z89.any(),
|
|
3795
4216
|
annotations: { destructiveHint: true },
|
|
3796
4217
|
async execute(input, ctx) {
|
|
3797
4218
|
await ctx.client.del("task", {
|
|
@@ -3806,129 +4227,70 @@ var tasksArchive = {
|
|
|
3806
4227
|
};
|
|
3807
4228
|
|
|
3808
4229
|
// src/commands/tasks/create.ts
|
|
3809
|
-
import { z as z89 } from "zod";
|
|
3810
|
-
var inputSchema87 = z89.object({
|
|
3811
|
-
project: z89.string().uuid().describe("Project ID"),
|
|
3812
|
-
title: z89.string().optional().describe("Task title"),
|
|
3813
|
-
message: z89.string().optional().describe("Initial message")
|
|
3814
|
-
});
|
|
3815
|
-
var tasksCreate = {
|
|
3816
|
-
name: "tasks.create",
|
|
3817
|
-
group: "tasks",
|
|
3818
|
-
description: "Create a new task in a project",
|
|
3819
|
-
inputSchema: inputSchema87,
|
|
3820
|
-
outputSchema: z89.any(),
|
|
3821
|
-
async execute(input, ctx) {
|
|
3822
|
-
return ctx.client.post("tasks", {
|
|
3823
|
-
projectId: input.project,
|
|
3824
|
-
title: input.title,
|
|
3825
|
-
initialMessage: input.message
|
|
3826
|
-
});
|
|
3827
|
-
},
|
|
3828
|
-
formatOutput(output, format) {
|
|
3829
|
-
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
3830
|
-
return JSON.stringify(output, null, 2);
|
|
3831
|
-
}
|
|
3832
|
-
};
|
|
3833
|
-
|
|
3834
|
-
// src/commands/tasks/get.ts
|
|
3835
4230
|
import { z as z90 } from "zod";
|
|
3836
|
-
var inputSchema88 = z90.object({
|
|
3837
|
-
taskId: z90.string().uuid().describe("Task ID")
|
|
3838
|
-
});
|
|
3839
|
-
var tasksGet = {
|
|
3840
|
-
name: "tasks.get",
|
|
3841
|
-
group: "tasks",
|
|
3842
|
-
description: "Get details of a specific task",
|
|
3843
|
-
inputSchema: inputSchema88,
|
|
3844
|
-
outputSchema: z90.any(),
|
|
3845
|
-
annotations: { readOnlyHint: true },
|
|
3846
|
-
async execute(input, ctx) {
|
|
3847
|
-
return ctx.client.get("task", {
|
|
3848
|
-
pathParams: { id: input.taskId }
|
|
3849
|
-
});
|
|
3850
|
-
},
|
|
3851
|
-
formatOutput(output, format) {
|
|
3852
|
-
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
3853
|
-
return JSON.stringify(output, null, 2);
|
|
3854
|
-
}
|
|
3855
|
-
};
|
|
3856
4231
|
|
|
3857
|
-
// src/
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
group: "tasks",
|
|
3866
|
-
description: "List tasks, optionally filtered by project",
|
|
3867
|
-
inputSchema: inputSchema89,
|
|
3868
|
-
outputSchema: z91.any(),
|
|
3869
|
-
annotations: { readOnlyHint: true },
|
|
3870
|
-
async execute(input, ctx) {
|
|
3871
|
-
if (input.project) {
|
|
3872
|
-
return ctx.client.get("project_tasks", {
|
|
3873
|
-
pathParams: { id: input.project },
|
|
3874
|
-
queryParams: { page: input.page, pageSize: input.pageSize }
|
|
3875
|
-
});
|
|
3876
|
-
}
|
|
3877
|
-
return ctx.client.get("tasks", {
|
|
3878
|
-
queryParams: { page: input.page, pageSize: input.pageSize }
|
|
4232
|
+
// src/lib/poll.ts
|
|
4233
|
+
init_errors();
|
|
4234
|
+
async function pollForResponse(client, taskId, afterSequence = 0, timeoutMs = 12e4) {
|
|
4235
|
+
const deadline = Date.now() + timeoutMs;
|
|
4236
|
+
while (Date.now() < deadline) {
|
|
4237
|
+
const messages = await client.get("task_messages", {
|
|
4238
|
+
pathParams: { id: taskId },
|
|
4239
|
+
queryParams: { limit: 10 }
|
|
3879
4240
|
});
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
};
|
|
3886
|
-
|
|
3887
|
-
// src/commands/tasks/messages.ts
|
|
3888
|
-
import { z as z92 } from "zod";
|
|
3889
|
-
var inputSchema90 = z92.object({
|
|
3890
|
-
taskId: z92.string().uuid().describe("Task ID"),
|
|
3891
|
-
cursor: z92.string().optional().describe("Pagination cursor"),
|
|
3892
|
-
limit: z92.number().int().min(1).max(100).default(50).describe("Results per page")
|
|
3893
|
-
});
|
|
3894
|
-
var tasksMessages = {
|
|
3895
|
-
name: "tasks.messages",
|
|
3896
|
-
group: "tasks",
|
|
3897
|
-
description: "List messages for a task",
|
|
3898
|
-
inputSchema: inputSchema90,
|
|
3899
|
-
outputSchema: z92.any(),
|
|
3900
|
-
annotations: { readOnlyHint: true },
|
|
3901
|
-
async execute(input, ctx) {
|
|
3902
|
-
const queryParams = { limit: input.limit };
|
|
3903
|
-
if (input.cursor) {
|
|
3904
|
-
queryParams.cursor = input.cursor;
|
|
4241
|
+
const items = Array.isArray(messages) ? messages : messages?.items ?? [];
|
|
4242
|
+
if (Array.isArray(items)) {
|
|
4243
|
+
const assistantMsg = items.find((m) => {
|
|
4244
|
+
const msg = m;
|
|
4245
|
+
return (msg.role === "assistant" || msg.role === "Assistant") && (msg.sequence ?? msg.sequenceNumber ?? 0) > afterSequence;
|
|
4246
|
+
});
|
|
4247
|
+
if (assistantMsg) return assistantMsg;
|
|
3905
4248
|
}
|
|
3906
|
-
|
|
3907
|
-
pathParams: { id: input.taskId },
|
|
3908
|
-
queryParams
|
|
3909
|
-
});
|
|
3910
|
-
},
|
|
3911
|
-
formatOutput(output, format) {
|
|
3912
|
-
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
3913
|
-
return JSON.stringify(output, null, 2);
|
|
4249
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
3914
4250
|
}
|
|
3915
|
-
|
|
4251
|
+
throw new ElnoraError("Timed out waiting for agent response (120s)", {
|
|
4252
|
+
code: "RESPONSE_TIMEOUT",
|
|
4253
|
+
suggestion: "The agent may still be processing. Check with: elnora tasks messages <task-id>"
|
|
4254
|
+
});
|
|
4255
|
+
}
|
|
3916
4256
|
|
|
3917
|
-
// src/commands/tasks/
|
|
3918
|
-
init_errors();
|
|
4257
|
+
// src/commands/tasks/create.ts
|
|
3919
4258
|
init_profiles();
|
|
3920
|
-
import { z as z93 } from "zod";
|
|
3921
4259
|
|
|
3922
4260
|
// src/lib/stream.ts
|
|
3923
4261
|
init_config();
|
|
4262
|
+
async function getStreamToken(taskId, apiKey) {
|
|
4263
|
+
const url = `${BASE_URL}/auth/stream-token`;
|
|
4264
|
+
const response = await fetch(url, {
|
|
4265
|
+
method: "POST",
|
|
4266
|
+
headers: {
|
|
4267
|
+
...DEFAULT_HEADERS,
|
|
4268
|
+
"X-API-Key": apiKey
|
|
4269
|
+
},
|
|
4270
|
+
body: JSON.stringify({ taskId }),
|
|
4271
|
+
signal: AbortSignal.timeout(1e4)
|
|
4272
|
+
});
|
|
4273
|
+
if (!response.ok) {
|
|
4274
|
+
throw new Error(`Stream token exchange failed: HTTP ${response.status}`);
|
|
4275
|
+
}
|
|
4276
|
+
const data = await response.json();
|
|
4277
|
+
return data.token;
|
|
4278
|
+
}
|
|
3924
4279
|
var TERMINAL_EVENTS = /* @__PURE__ */ new Set(["completed", "error", "timeout"]);
|
|
3925
4280
|
var STREAM_TIMEOUT_MS = 3e5;
|
|
3926
4281
|
async function* streamTask(taskId, apiKey, options) {
|
|
4282
|
+
let streamToken;
|
|
4283
|
+
try {
|
|
4284
|
+
streamToken = await getStreamToken(taskId, apiKey);
|
|
4285
|
+
} catch (err) {
|
|
4286
|
+
yield { type: "error", content: `Failed to get stream token: ${err instanceof Error ? err.message : String(err)}` };
|
|
4287
|
+
return;
|
|
4288
|
+
}
|
|
3927
4289
|
const baseUrl = options?.aiServerBaseUrl ?? AI_SERVER_URL;
|
|
3928
4290
|
const url = `${baseUrl}/api/v1/tasks/${taskId}/stream`;
|
|
3929
4291
|
const response = await fetch(url, {
|
|
3930
4292
|
headers: {
|
|
3931
|
-
|
|
4293
|
+
Authorization: `Bearer ${streamToken}`,
|
|
3932
4294
|
Accept: "text/event-stream"
|
|
3933
4295
|
},
|
|
3934
4296
|
signal: options?.signal ?? AbortSignal.timeout(STREAM_TIMEOUT_MS)
|
|
@@ -3987,7 +4349,7 @@ async function collectStreamResponse(taskId, apiKey, options) {
|
|
|
3987
4349
|
}
|
|
3988
4350
|
|
|
3989
4351
|
// src/lib/stream-renderer.ts
|
|
3990
|
-
import
|
|
4352
|
+
import pc4 from "picocolors";
|
|
3991
4353
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3992
4354
|
var SPINNER_INTERVAL_MS = 80;
|
|
3993
4355
|
var StreamRenderer = class {
|
|
@@ -4001,27 +4363,16 @@ var StreamRenderer = class {
|
|
|
4001
4363
|
renderEvent(event) {
|
|
4002
4364
|
switch (event.type) {
|
|
4003
4365
|
case "think":
|
|
4004
|
-
this.startSpinner(
|
|
4366
|
+
this.startSpinner(event.content);
|
|
4005
4367
|
break;
|
|
4006
4368
|
case "tool_start":
|
|
4007
|
-
this.
|
|
4008
|
-
this.startSpinner(`Using ${event.tool}...`);
|
|
4369
|
+
this.startSpinner(`Using ${event.tool}\u2026`);
|
|
4009
4370
|
break;
|
|
4010
|
-
case "tool_end":
|
|
4371
|
+
case "tool_end":
|
|
4011
4372
|
this.stopSpinner();
|
|
4012
|
-
const toolEvent = event;
|
|
4013
|
-
const icon = toolEvent.success ? this.useColor ? pc2.green("\u2713") : "\u2713" : this.useColor ? pc2.red("\u2717") : "\u2717";
|
|
4014
|
-
const suffix = toolEvent.result ? ` (${toolEvent.result})` : "";
|
|
4015
|
-
process.stderr.write(` ${icon} ${toolEvent.tool}${suffix}
|
|
4016
|
-
`);
|
|
4017
4373
|
break;
|
|
4018
|
-
}
|
|
4019
4374
|
case "progress":
|
|
4020
|
-
this.
|
|
4021
|
-
process.stderr.write(
|
|
4022
|
-
` ${this.useColor ? pc2.dim(event.content) : event.content}
|
|
4023
|
-
`
|
|
4024
|
-
);
|
|
4375
|
+
this.startSpinner(event.content);
|
|
4025
4376
|
break;
|
|
4026
4377
|
case "token":
|
|
4027
4378
|
this.stopSpinner();
|
|
@@ -4035,14 +4386,14 @@ var StreamRenderer = class {
|
|
|
4035
4386
|
this.stopSpinner();
|
|
4036
4387
|
process.stderr.write(
|
|
4037
4388
|
`
|
|
4038
|
-
${this.useColor ?
|
|
4389
|
+
${this.useColor ? pc4.red("Error:") : "Error:"} ${event.content}
|
|
4039
4390
|
`
|
|
4040
4391
|
);
|
|
4041
4392
|
break;
|
|
4042
4393
|
case "timeout":
|
|
4043
4394
|
this.stopSpinner();
|
|
4044
4395
|
process.stderr.write(`
|
|
4045
|
-
${this.useColor ?
|
|
4396
|
+
${this.useColor ? pc4.yellow("Stream timed out.") : "Stream timed out."}
|
|
4046
4397
|
`);
|
|
4047
4398
|
break;
|
|
4048
4399
|
}
|
|
@@ -4064,7 +4415,7 @@ ${this.useColor ? pc2.yellow("Stream timed out.") : "Stream timed out."}
|
|
|
4064
4415
|
}
|
|
4065
4416
|
writeSpinnerFrame() {
|
|
4066
4417
|
const frame = SPINNER_FRAMES[this.spinnerFrame];
|
|
4067
|
-
const text = this.useColor ?
|
|
4418
|
+
const text = this.useColor ? pc4.dim(`${frame} ${this.currentLabel}`) : `${frame} ${this.currentLabel}`;
|
|
4068
4419
|
process.stderr.write(`\r ${text}`);
|
|
4069
4420
|
}
|
|
4070
4421
|
stopSpinner() {
|
|
@@ -4078,43 +4429,168 @@ ${this.useColor ? pc2.yellow("Stream timed out.") : "Stream timed out."}
|
|
|
4078
4429
|
}
|
|
4079
4430
|
};
|
|
4080
4431
|
|
|
4081
|
-
// src/commands/tasks/
|
|
4082
|
-
var
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
message:
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
stream: z93.boolean().default(false).describe("Stream agent response in real-time (SSE)")
|
|
4432
|
+
// src/commands/tasks/create.ts
|
|
4433
|
+
var inputSchema88 = z90.object({
|
|
4434
|
+
project: z90.string().uuid().describe("Project ID"),
|
|
4435
|
+
title: z90.string().optional().describe("Task title"),
|
|
4436
|
+
message: z90.string().optional().describe("Initial message"),
|
|
4437
|
+
wait: z90.boolean().default(false).describe("Wait for agent response (polling)"),
|
|
4438
|
+
stream: z90.boolean().default(false).describe("Stream agent response in real-time (SSE)")
|
|
4089
4439
|
});
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4440
|
+
var tasksCreate = {
|
|
4441
|
+
name: "tasks.create",
|
|
4442
|
+
group: "tasks",
|
|
4443
|
+
description: "Create a new task in a project",
|
|
4444
|
+
inputSchema: inputSchema88,
|
|
4445
|
+
outputSchema: z90.any(),
|
|
4446
|
+
async execute(input, ctx) {
|
|
4447
|
+
const result = await ctx.client.post("tasks", {
|
|
4448
|
+
projectId: input.project,
|
|
4449
|
+
title: input.title,
|
|
4450
|
+
initialMessage: input.message
|
|
4096
4451
|
});
|
|
4097
|
-
const
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4452
|
+
const taskId = result.id;
|
|
4453
|
+
const sequence = result?.sequence ?? result?.sequenceNumber ?? 0;
|
|
4454
|
+
if (!input.message || !input.stream && !input.wait) {
|
|
4455
|
+
return result;
|
|
4456
|
+
}
|
|
4457
|
+
if (ctx.mode === "mcp") {
|
|
4458
|
+
try {
|
|
4459
|
+
const apiKey = resolveApiKey(ctx.profileName);
|
|
4460
|
+
const content = await collectStreamResponse(taskId, apiKey);
|
|
4461
|
+
return { ...result, response: content };
|
|
4462
|
+
} catch (err) {
|
|
4463
|
+
process.stderr.write(
|
|
4464
|
+
`Stream failed (${err instanceof Error ? err.message : String(err)}), polling fallback.
|
|
4465
|
+
`
|
|
4466
|
+
);
|
|
4467
|
+
return pollForResponse(ctx.client, taskId, sequence);
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
if (input.stream) {
|
|
4471
|
+
try {
|
|
4472
|
+
const apiKey = resolveApiKey(ctx.profileName);
|
|
4473
|
+
const renderer = new StreamRenderer();
|
|
4474
|
+
for await (const event of streamTask(taskId, apiKey)) {
|
|
4475
|
+
renderer.renderEvent(event);
|
|
4476
|
+
}
|
|
4477
|
+
renderer.stopSpinner();
|
|
4478
|
+
return { ...result, streamed: true };
|
|
4479
|
+
} catch {
|
|
4480
|
+
process.stderr.write("Streaming failed \u2014 falling back to polling.\n");
|
|
4481
|
+
return pollForResponse(ctx.client, taskId, sequence);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
return pollForResponse(ctx.client, taskId, sequence);
|
|
4485
|
+
},
|
|
4486
|
+
formatOutput(output, format) {
|
|
4487
|
+
const data = output;
|
|
4488
|
+
if (format === "compact") return data.id ?? JSON.stringify(output);
|
|
4489
|
+
if (data.streamed) return "";
|
|
4490
|
+
return JSON.stringify(output, null, 2);
|
|
4491
|
+
}
|
|
4492
|
+
};
|
|
4493
|
+
|
|
4494
|
+
// src/commands/tasks/get.ts
|
|
4495
|
+
import { z as z91 } from "zod";
|
|
4496
|
+
var inputSchema89 = z91.object({
|
|
4497
|
+
taskId: z91.string().uuid().describe("Task ID")
|
|
4498
|
+
});
|
|
4499
|
+
var tasksGet = {
|
|
4500
|
+
name: "tasks.get",
|
|
4501
|
+
group: "tasks",
|
|
4502
|
+
description: "Get details of a specific task",
|
|
4503
|
+
inputSchema: inputSchema89,
|
|
4504
|
+
outputSchema: z91.any(),
|
|
4505
|
+
annotations: { readOnlyHint: true },
|
|
4506
|
+
async execute(input, ctx) {
|
|
4507
|
+
return ctx.client.get("task", {
|
|
4508
|
+
pathParams: { id: input.taskId }
|
|
4509
|
+
});
|
|
4510
|
+
},
|
|
4511
|
+
formatOutput(output, format) {
|
|
4512
|
+
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
4513
|
+
return JSON.stringify(output, null, 2);
|
|
4514
|
+
}
|
|
4515
|
+
};
|
|
4516
|
+
|
|
4517
|
+
// src/commands/tasks/list.ts
|
|
4518
|
+
import { z as z92 } from "zod";
|
|
4519
|
+
var inputSchema90 = z92.object({
|
|
4520
|
+
project: z92.string().uuid().optional().describe("Project ID to filter by"),
|
|
4521
|
+
...paginationInput
|
|
4522
|
+
});
|
|
4523
|
+
var tasksList = {
|
|
4524
|
+
name: "tasks.list",
|
|
4525
|
+
group: "tasks",
|
|
4526
|
+
description: "List tasks, optionally filtered by project",
|
|
4527
|
+
inputSchema: inputSchema90,
|
|
4528
|
+
outputSchema: z92.any(),
|
|
4529
|
+
annotations: { readOnlyHint: true },
|
|
4530
|
+
async execute(input, ctx) {
|
|
4531
|
+
if (input.project) {
|
|
4532
|
+
return ctx.client.get("project_tasks", {
|
|
4533
|
+
pathParams: { id: input.project },
|
|
4534
|
+
queryParams: { page: input.page, pageSize: input.pageSize }
|
|
4102
4535
|
});
|
|
4103
|
-
if (assistantMsg) return assistantMsg;
|
|
4104
4536
|
}
|
|
4105
|
-
|
|
4537
|
+
return ctx.client.get("tasks", {
|
|
4538
|
+
queryParams: { page: input.page, pageSize: input.pageSize }
|
|
4539
|
+
});
|
|
4540
|
+
},
|
|
4541
|
+
formatOutput(output, format) {
|
|
4542
|
+
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
4543
|
+
return JSON.stringify(output, null, 2);
|
|
4106
4544
|
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4545
|
+
};
|
|
4546
|
+
|
|
4547
|
+
// src/commands/tasks/messages.ts
|
|
4548
|
+
import { z as z93 } from "zod";
|
|
4549
|
+
var inputSchema91 = z93.object({
|
|
4550
|
+
taskId: z93.string().uuid().describe("Task ID"),
|
|
4551
|
+
cursor: z93.string().optional().describe("Pagination cursor"),
|
|
4552
|
+
limit: z93.number().int().min(1).max(100).default(50).describe("Results per page")
|
|
4553
|
+
});
|
|
4554
|
+
var tasksMessages = {
|
|
4555
|
+
name: "tasks.messages",
|
|
4556
|
+
group: "tasks",
|
|
4557
|
+
description: "List messages for a task",
|
|
4558
|
+
inputSchema: inputSchema91,
|
|
4559
|
+
outputSchema: z93.any(),
|
|
4560
|
+
annotations: { readOnlyHint: true },
|
|
4561
|
+
async execute(input, ctx) {
|
|
4562
|
+
const queryParams = { limit: input.limit };
|
|
4563
|
+
if (input.cursor) {
|
|
4564
|
+
queryParams.cursor = input.cursor;
|
|
4565
|
+
}
|
|
4566
|
+
return ctx.client.get("task_messages", {
|
|
4567
|
+
pathParams: { id: input.taskId },
|
|
4568
|
+
queryParams
|
|
4569
|
+
});
|
|
4570
|
+
},
|
|
4571
|
+
formatOutput(output, format) {
|
|
4572
|
+
if (format === "compact") return output.id ?? JSON.stringify(output);
|
|
4573
|
+
return JSON.stringify(output, null, 2);
|
|
4574
|
+
}
|
|
4575
|
+
};
|
|
4576
|
+
|
|
4577
|
+
// src/commands/tasks/send.ts
|
|
4578
|
+
import { z as z94 } from "zod";
|
|
4579
|
+
init_profiles();
|
|
4580
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4581
|
+
var inputSchema92 = z94.object({
|
|
4582
|
+
taskId: z94.string().uuid().describe("Task ID"),
|
|
4583
|
+
message: z94.string().min(1).describe("Message content"),
|
|
4584
|
+
fileRefs: z94.string().optional().describe("Comma-separated file reference UUIDs"),
|
|
4585
|
+
wait: z94.boolean().default(false).describe("Wait for agent response (polling)"),
|
|
4586
|
+
stream: z94.boolean().default(false).describe("Stream agent response in real-time (SSE)")
|
|
4587
|
+
});
|
|
4112
4588
|
var tasksSend = {
|
|
4113
4589
|
name: "tasks.send",
|
|
4114
4590
|
group: "tasks",
|
|
4115
4591
|
description: "Send a message to a task",
|
|
4116
|
-
inputSchema:
|
|
4117
|
-
outputSchema:
|
|
4592
|
+
inputSchema: inputSchema92,
|
|
4593
|
+
outputSchema: z94.any(),
|
|
4118
4594
|
async execute(input, ctx) {
|
|
4119
4595
|
let referencedFileIds;
|
|
4120
4596
|
if (input.fileRefs) {
|
|
@@ -4128,51 +4604,61 @@ var tasksSend = {
|
|
|
4128
4604
|
},
|
|
4129
4605
|
{ pathParams: { id: input.taskId } }
|
|
4130
4606
|
);
|
|
4607
|
+
const sequence = result?.sequence ?? result?.sequenceNumber ?? 0;
|
|
4131
4608
|
if (ctx.mode === "mcp") {
|
|
4132
4609
|
try {
|
|
4133
4610
|
const apiKey = resolveApiKey(ctx.profileName);
|
|
4134
4611
|
const content = await collectStreamResponse(input.taskId, apiKey);
|
|
4135
4612
|
return { sent: true, taskId: input.taskId, response: content };
|
|
4136
|
-
} catch {
|
|
4137
|
-
|
|
4138
|
-
|
|
4613
|
+
} catch (err) {
|
|
4614
|
+
process.stderr.write(
|
|
4615
|
+
`Stream failed (${err instanceof Error ? err.message : String(err)}), polling fallback.
|
|
4616
|
+
`
|
|
4617
|
+
);
|
|
4618
|
+
return pollForResponse(ctx.client, input.taskId, sequence);
|
|
4139
4619
|
}
|
|
4140
4620
|
}
|
|
4141
4621
|
if (!input.wait && !input.stream) {
|
|
4142
4622
|
return result;
|
|
4143
4623
|
}
|
|
4144
4624
|
if (input.stream) {
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4625
|
+
try {
|
|
4626
|
+
const apiKey = resolveApiKey(ctx.profileName);
|
|
4627
|
+
const renderer = new StreamRenderer();
|
|
4628
|
+
for await (const event of streamTask(input.taskId, apiKey)) {
|
|
4629
|
+
renderer.renderEvent(event);
|
|
4630
|
+
}
|
|
4631
|
+
renderer.stopSpinner();
|
|
4632
|
+
return { sent: true, taskId: input.taskId, streamed: true };
|
|
4633
|
+
} catch {
|
|
4634
|
+
process.stderr.write("Streaming failed \u2014 falling back to polling.\n");
|
|
4635
|
+
return pollForResponse(ctx.client, input.taskId, sequence);
|
|
4149
4636
|
}
|
|
4150
|
-
renderer.stopSpinner();
|
|
4151
|
-
return { sent: true, taskId: input.taskId, streamed: true };
|
|
4152
4637
|
}
|
|
4153
|
-
const sequence = result?.sequence ?? result?.sequenceNumber ?? 0;
|
|
4154
4638
|
return pollForResponse(ctx.client, input.taskId, sequence);
|
|
4155
4639
|
},
|
|
4156
4640
|
formatOutput(output, format) {
|
|
4157
|
-
|
|
4641
|
+
const data = output;
|
|
4642
|
+
if (format === "compact") return data.id ?? JSON.stringify(output);
|
|
4643
|
+
if (data.streamed) return "";
|
|
4158
4644
|
return JSON.stringify(output, null, 2);
|
|
4159
4645
|
}
|
|
4160
4646
|
};
|
|
4161
4647
|
|
|
4162
4648
|
// src/commands/tasks/update.ts
|
|
4163
4649
|
init_errors();
|
|
4164
|
-
import { z as
|
|
4165
|
-
var
|
|
4166
|
-
taskId:
|
|
4167
|
-
title:
|
|
4168
|
-
status:
|
|
4650
|
+
import { z as z95 } from "zod";
|
|
4651
|
+
var inputSchema93 = z95.object({
|
|
4652
|
+
taskId: z95.string().uuid().describe("Task ID"),
|
|
4653
|
+
title: z95.string().min(1).optional().describe("New task title"),
|
|
4654
|
+
status: z95.string().optional().describe("New task status")
|
|
4169
4655
|
});
|
|
4170
4656
|
var tasksUpdate = {
|
|
4171
4657
|
name: "tasks.update",
|
|
4172
4658
|
group: "tasks",
|
|
4173
4659
|
description: "Update an existing task",
|
|
4174
|
-
inputSchema:
|
|
4175
|
-
outputSchema:
|
|
4660
|
+
inputSchema: inputSchema93,
|
|
4661
|
+
outputSchema: z95.any(),
|
|
4176
4662
|
annotations: { idempotentHint: true },
|
|
4177
4663
|
async execute(input, ctx) {
|
|
4178
4664
|
const { taskId, ...fields } = input;
|
|
@@ -4197,6 +4683,136 @@ function registerTaskCommands() {
|
|
|
4197
4683
|
return [tasksList, tasksGet, tasksCreate, tasksSend, tasksMessages, tasksUpdate, tasksArchive];
|
|
4198
4684
|
}
|
|
4199
4685
|
|
|
4686
|
+
// src/commands/update.ts
|
|
4687
|
+
init_config();
|
|
4688
|
+
import { execFileSync } from "node:child_process";
|
|
4689
|
+
import { createHash } from "node:crypto";
|
|
4690
|
+
import { chmodSync as chmodSync2, copyFileSync, createWriteStream, mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync } from "node:fs";
|
|
4691
|
+
import { tmpdir } from "node:os";
|
|
4692
|
+
import { join as join4 } from "node:path";
|
|
4693
|
+
import { Readable } from "node:stream";
|
|
4694
|
+
import { pipeline } from "node:stream/promises";
|
|
4695
|
+
import pc5 from "picocolors";
|
|
4696
|
+
var NPM_REGISTRY_URL2 = "https://registry.npmjs.org/@elnora-ai/cli/latest";
|
|
4697
|
+
var GITHUB_REPO = "Elnora-AI/elnora-cli";
|
|
4698
|
+
function getPlatformTarget() {
|
|
4699
|
+
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
4700
|
+
switch (process.platform) {
|
|
4701
|
+
case "darwin":
|
|
4702
|
+
return { target: `elnora-macos-${arch}`, ext: "tar.gz" };
|
|
4703
|
+
case "linux":
|
|
4704
|
+
return { target: `elnora-linux-${arch}`, ext: "tar.gz" };
|
|
4705
|
+
case "win32":
|
|
4706
|
+
return { target: `elnora-win-${arch}.exe`, ext: "zip" };
|
|
4707
|
+
default:
|
|
4708
|
+
return null;
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
function getManualHint() {
|
|
4712
|
+
return process.platform === "win32" ? " irm https://cli.elnora.ai/install.ps1 | iex" : " curl -fsSL https://cli.elnora.ai/install.sh | bash";
|
|
4713
|
+
}
|
|
4714
|
+
async function downloadToFile(url, dest) {
|
|
4715
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(12e4), redirect: "follow" });
|
|
4716
|
+
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);
|
|
4717
|
+
const nodeStream = Readable.fromWeb(res.body);
|
|
4718
|
+
await pipeline(nodeStream, createWriteStream(dest));
|
|
4719
|
+
}
|
|
4720
|
+
function addUpdateCommand(program2) {
|
|
4721
|
+
program2.command("update").description("Update Elnora CLI to the latest version").action(async () => {
|
|
4722
|
+
const color = isColorEnabled();
|
|
4723
|
+
const hint = getManualHint();
|
|
4724
|
+
let latest;
|
|
4725
|
+
try {
|
|
4726
|
+
const res = await fetch(NPM_REGISTRY_URL2, { signal: AbortSignal.timeout(5e3) });
|
|
4727
|
+
if (!res.ok) {
|
|
4728
|
+
console.error("Failed to check for updates. Try manually:");
|
|
4729
|
+
console.error(hint);
|
|
4730
|
+
process.exit(1);
|
|
4731
|
+
}
|
|
4732
|
+
const data = await res.json();
|
|
4733
|
+
latest = data.version ?? "unknown";
|
|
4734
|
+
} catch {
|
|
4735
|
+
console.error("Failed to reach registry. Try manually:");
|
|
4736
|
+
console.error(hint);
|
|
4737
|
+
process.exit(1);
|
|
4738
|
+
}
|
|
4739
|
+
if (latest === VERSION || !isNewerVersion(latest, VERSION)) {
|
|
4740
|
+
const msg2 = color ? `${pc5.green("\u2713")} Already on the latest version (v${VERSION})` : `\u2713 Already on the latest version (v${VERSION})`;
|
|
4741
|
+
console.error(msg2);
|
|
4742
|
+
return;
|
|
4743
|
+
}
|
|
4744
|
+
const msg = color ? `Updating Elnora CLI ${pc5.dim(`v${VERSION}`)} \u2192 ${pc5.green(`v${latest}`)}...` : `Updating Elnora CLI v${VERSION} \u2192 v${latest}...`;
|
|
4745
|
+
console.error(msg);
|
|
4746
|
+
const platformInfo = getPlatformTarget();
|
|
4747
|
+
if (!platformInfo) {
|
|
4748
|
+
console.error(`
|
|
4749
|
+
Unsupported platform: ${process.platform}. Try manually:`);
|
|
4750
|
+
console.error(" npm update -g @elnora-ai/cli");
|
|
4751
|
+
process.exit(1);
|
|
4752
|
+
}
|
|
4753
|
+
const { target, ext } = platformInfo;
|
|
4754
|
+
const binaryUrl = `https://github.com/${GITHUB_REPO}/releases/download/v${latest}/${target}.${ext}`;
|
|
4755
|
+
const checksumUrl = `https://github.com/${GITHUB_REPO}/releases/download/v${latest}/${target}.sha256`;
|
|
4756
|
+
const tmp = join4(tmpdir(), `elnora-update-${Date.now()}`);
|
|
4757
|
+
mkdirSync4(tmp, { recursive: true });
|
|
4758
|
+
const archivePath = join4(tmp, `${target}.${ext}`);
|
|
4759
|
+
try {
|
|
4760
|
+
await downloadToFile(binaryUrl, archivePath);
|
|
4761
|
+
let checksumVerified = false;
|
|
4762
|
+
try {
|
|
4763
|
+
const checksumRes = await fetch(checksumUrl, { signal: AbortSignal.timeout(15e3) });
|
|
4764
|
+
if (checksumRes.ok) {
|
|
4765
|
+
const checksumText = await checksumRes.text();
|
|
4766
|
+
const expected = checksumText.split(/\s/)[0].trim();
|
|
4767
|
+
if (expected.length === 64) {
|
|
4768
|
+
const actual = createHash("sha256").update(readFileSync6(archivePath)).digest("hex");
|
|
4769
|
+
if (actual.toLowerCase() !== expected.toLowerCase()) {
|
|
4770
|
+
console.error("\nChecksum verification failed. The download may be corrupted.");
|
|
4771
|
+
console.error("Try again, or install manually:");
|
|
4772
|
+
console.error(hint);
|
|
4773
|
+
process.exit(1);
|
|
4774
|
+
}
|
|
4775
|
+
checksumVerified = true;
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
} catch {
|
|
4779
|
+
}
|
|
4780
|
+
if (!checksumVerified) {
|
|
4781
|
+
console.error("Warning: Could not verify download checksum. Proceeding anyway.");
|
|
4782
|
+
}
|
|
4783
|
+
if (process.platform === "win32") {
|
|
4784
|
+
const extractDir = join4(tmp, "extracted");
|
|
4785
|
+
execFileSync("powershell", [
|
|
4786
|
+
"-NoProfile",
|
|
4787
|
+
"-Command",
|
|
4788
|
+
`Expand-Archive -Path '${archivePath}' -DestinationPath '${extractDir}' -Force`
|
|
4789
|
+
]);
|
|
4790
|
+
const installDir = join4(process.env.USERPROFILE ?? process.env.HOME ?? "", ".elnora", "bin");
|
|
4791
|
+
mkdirSync4(installDir, { recursive: true });
|
|
4792
|
+
copyFileSync(join4(extractDir, target), join4(installDir, "elnora.exe"));
|
|
4793
|
+
} else {
|
|
4794
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", tmp]);
|
|
4795
|
+
const installDir = process.env.ELNORA_INSTALL_DIR ?? join4(process.env.HOME ?? "", ".local", "bin");
|
|
4796
|
+
mkdirSync4(installDir, { recursive: true });
|
|
4797
|
+
const dest = join4(installDir, "elnora");
|
|
4798
|
+
copyFileSync(join4(tmp, target), dest);
|
|
4799
|
+
chmodSync2(dest, 493);
|
|
4800
|
+
}
|
|
4801
|
+
const done = color ? `${pc5.green("\u2713")} Updated to v${latest}` : `\u2713 Updated to v${latest}`;
|
|
4802
|
+
console.error(done);
|
|
4803
|
+
} catch {
|
|
4804
|
+
console.error("\nUpdate failed. Try manually:");
|
|
4805
|
+
console.error(hint);
|
|
4806
|
+
process.exit(1);
|
|
4807
|
+
} finally {
|
|
4808
|
+
try {
|
|
4809
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
4810
|
+
} catch {
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
});
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4200
4816
|
// src/commands/whoami.ts
|
|
4201
4817
|
init_client();
|
|
4202
4818
|
init_profiles();
|
|
@@ -4261,95 +4877,26 @@ var CommandRegistry = class {
|
|
|
4261
4877
|
|
|
4262
4878
|
// src/cli.ts
|
|
4263
4879
|
init_errors();
|
|
4264
|
-
|
|
4265
|
-
// src/lib/update-check.ts
|
|
4266
|
-
init_config();
|
|
4267
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4268
|
-
import { homedir as homedir2 } from "node:os";
|
|
4269
|
-
import { join as join3 } from "node:path";
|
|
4270
|
-
import pc3 from "picocolors";
|
|
4271
|
-
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@elnora-ai/cli/latest";
|
|
4272
|
-
var CACHE_DIR = join3(homedir2(), ".elnora");
|
|
4273
|
-
var CACHE_FILE = join3(CACHE_DIR, ".update-check");
|
|
4274
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
4275
|
-
var FETCH_TIMEOUT_MS = 3e3;
|
|
4276
|
-
function shouldSkip() {
|
|
4277
|
-
if (process.env.CI || process.env.GITHUB_ACTIONS) return true;
|
|
4278
|
-
if (process.env.ELNORA_NO_UPDATE_CHECK) return true;
|
|
4279
|
-
if (!process.stderr.isTTY) return true;
|
|
4280
|
-
return false;
|
|
4281
|
-
}
|
|
4282
|
-
function readCache() {
|
|
4283
|
-
try {
|
|
4284
|
-
if (!existsSync3(CACHE_FILE)) return null;
|
|
4285
|
-
const raw = readFileSync5(CACHE_FILE, "utf-8");
|
|
4286
|
-
return JSON.parse(raw);
|
|
4287
|
-
} catch {
|
|
4288
|
-
return null;
|
|
4289
|
-
}
|
|
4290
|
-
}
|
|
4291
|
-
function writeCache(entry) {
|
|
4292
|
-
try {
|
|
4293
|
-
mkdirSync2(CACHE_DIR, { recursive: true });
|
|
4294
|
-
writeFileSync2(CACHE_FILE, JSON.stringify(entry), "utf-8");
|
|
4295
|
-
} catch {
|
|
4296
|
-
}
|
|
4297
|
-
}
|
|
4298
|
-
function showUpdateNotice(latest) {
|
|
4299
|
-
const color = isColorEnabled();
|
|
4300
|
-
const msg = color ? `
|
|
4301
|
-
${pc3.yellow(`Update available: v${VERSION} \u2192 v${latest}`)}
|
|
4302
|
-
Run: curl -fsSL https://cli.elnora.ai/install.sh | bash
|
|
4303
|
-
` : `
|
|
4304
|
-
Update available: v${VERSION} \u2192 v${latest}
|
|
4305
|
-
Run: curl -fsSL https://cli.elnora.ai/install.sh | bash
|
|
4306
|
-
`;
|
|
4307
|
-
process.stderr.write(msg);
|
|
4308
|
-
}
|
|
4309
|
-
function registerUpdateCheck() {
|
|
4310
|
-
if (shouldSkip()) return;
|
|
4311
|
-
const cached = readCache();
|
|
4312
|
-
if (cached) {
|
|
4313
|
-
const elapsed = Date.now() - new Date(cached.checkedAt).getTime();
|
|
4314
|
-
if (elapsed < CHECK_INTERVAL_MS) {
|
|
4315
|
-
if (cached.latest && cached.latest !== VERSION && cached.latest !== "unknown") {
|
|
4316
|
-
if (cached.latest > VERSION) {
|
|
4317
|
-
showUpdateNotice(cached.latest);
|
|
4318
|
-
}
|
|
4319
|
-
}
|
|
4320
|
-
return;
|
|
4321
|
-
}
|
|
4322
|
-
}
|
|
4323
|
-
setTimeout(async () => {
|
|
4324
|
-
try {
|
|
4325
|
-
const response = await fetch(NPM_REGISTRY_URL, {
|
|
4326
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
4327
|
-
});
|
|
4328
|
-
if (!response.ok) return;
|
|
4329
|
-
const data = await response.json();
|
|
4330
|
-
const latest = data.version ?? "unknown";
|
|
4331
|
-
writeCache({ checkedAt: (/* @__PURE__ */ new Date()).toISOString(), latest });
|
|
4332
|
-
if (latest !== VERSION && latest !== "unknown" && latest > VERSION) {
|
|
4333
|
-
showUpdateNotice(latest);
|
|
4334
|
-
}
|
|
4335
|
-
} catch {
|
|
4336
|
-
}
|
|
4337
|
-
}, 0);
|
|
4338
|
-
}
|
|
4339
|
-
|
|
4340
|
-
// src/cli.ts
|
|
4341
4880
|
process.on("uncaughtException", (err) => {
|
|
4342
|
-
|
|
4343
|
-
|
|
4881
|
+
if (process.stderr.isTTY) {
|
|
4882
|
+
process.stderr.write(`${formatErrorForHuman(err)}
|
|
4883
|
+
`);
|
|
4884
|
+
} else {
|
|
4885
|
+
process.stderr.write(`${JSON.stringify(formatErrorPayload(err), null, 2)}
|
|
4344
4886
|
`);
|
|
4345
|
-
|
|
4887
|
+
}
|
|
4888
|
+
process.exitCode = getExitCode(err);
|
|
4346
4889
|
});
|
|
4347
4890
|
process.on("unhandledRejection", (reason) => {
|
|
4348
4891
|
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
4349
|
-
|
|
4350
|
-
|
|
4892
|
+
if (process.stderr.isTTY) {
|
|
4893
|
+
process.stderr.write(`${formatErrorForHuman(err)}
|
|
4894
|
+
`);
|
|
4895
|
+
} else {
|
|
4896
|
+
process.stderr.write(`${JSON.stringify(formatErrorPayload(err), null, 2)}
|
|
4351
4897
|
`);
|
|
4352
|
-
|
|
4898
|
+
}
|
|
4899
|
+
process.exitCode = 1;
|
|
4353
4900
|
});
|
|
4354
4901
|
var registry = new CommandRegistry();
|
|
4355
4902
|
var commandGroups = [
|
|
@@ -4378,6 +4925,8 @@ addMcpCommands(program, registry);
|
|
|
4378
4925
|
addDoctorCommand(program);
|
|
4379
4926
|
addWhoamiCommand(program);
|
|
4380
4927
|
addOpenCommand(program);
|
|
4928
|
+
addSetupClaudeCommand(program);
|
|
4929
|
+
addUpdateCommand(program);
|
|
4381
4930
|
addCompletionCommand(program);
|
|
4382
4931
|
registerUpdateCheck();
|
|
4383
4932
|
program.parseAsync(process.argv);
|