@agentsky/sky 2.6.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -22176,17 +22176,20 @@ function modelSubscriptionProviderFor(agentType, llm) {
|
|
|
22176
22176
|
if (agentType === "claude_code" && CLAUDE_PLAN_MODELS.has(llm)) return "anthropic";
|
|
22177
22177
|
if (agentType === "codex" && CHATGPT_PLAN_MODELS.has(llm)) return "openai";
|
|
22178
22178
|
if (agentType === "cursor" && llm === "harness-default") return "cursor";
|
|
22179
|
+
if (agentType === "grok_build" && llm === "harness-default") return "xai";
|
|
22179
22180
|
return null;
|
|
22180
22181
|
}
|
|
22181
22182
|
var MODEL_SUBSCRIPTION_PROVIDER_NAME = {
|
|
22182
22183
|
anthropic: "Claude",
|
|
22183
22184
|
openai: "ChatGPT",
|
|
22184
|
-
cursor: "Cursor"
|
|
22185
|
+
cursor: "Cursor",
|
|
22186
|
+
xai: "Grok"
|
|
22185
22187
|
};
|
|
22186
22188
|
var MODEL_SUBSCRIPTION_PLAN_PHRASE = {
|
|
22187
22189
|
anthropic: "Claude subscription",
|
|
22188
22190
|
openai: "ChatGPT plan",
|
|
22189
|
-
cursor: "Cursor plan"
|
|
22191
|
+
cursor: "Cursor plan",
|
|
22192
|
+
xai: "Grok subscription"
|
|
22190
22193
|
};
|
|
22191
22194
|
var AGENT_TYPE_META = {
|
|
22192
22195
|
...Object.fromEntries(ACP_AGENT_TYPES.map((agentType) => [agentType, {
|
package/dist/index.js
CHANGED
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
skillRefSchema,
|
|
17
17
|
splitMcpServers,
|
|
18
18
|
supersededBy
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-IC4N2ZPL.js";
|
|
20
20
|
|
|
21
21
|
// src/index.ts
|
|
22
|
-
import { existsSync as
|
|
23
|
-
import { basename as basename3, resolve as
|
|
24
|
-
import { createInterface as
|
|
22
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
23
|
+
import { basename as basename3, resolve as resolve4 } from "path";
|
|
24
|
+
import { createInterface as createInterface4 } from "readline";
|
|
25
25
|
import { Command } from "commander";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
@@ -64,13 +64,13 @@ function currentUniverse() {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// src/version.ts
|
|
67
|
-
var CLI_VERSION = "2.
|
|
67
|
+
var CLI_VERSION = "2.7.1";
|
|
68
68
|
var USER_AGENT = `sky-cli/${CLI_VERSION} (${process.platform}; node/${process.versions.node})`;
|
|
69
69
|
|
|
70
70
|
// src/api.ts
|
|
71
71
|
var ApiError = class extends Error {
|
|
72
|
-
constructor(status, code,
|
|
73
|
-
super(
|
|
72
|
+
constructor(status, code, message2) {
|
|
73
|
+
super(message2);
|
|
74
74
|
this.status = status;
|
|
75
75
|
this.code = code;
|
|
76
76
|
}
|
|
@@ -89,16 +89,16 @@ function authHeaders() {
|
|
|
89
89
|
}
|
|
90
90
|
async function parseError(res) {
|
|
91
91
|
let code = "http_error";
|
|
92
|
-
let
|
|
92
|
+
let message2 = `${res.status} ${res.statusText}`;
|
|
93
93
|
try {
|
|
94
94
|
const body = await res.json();
|
|
95
95
|
if (body.error?.message) {
|
|
96
96
|
code = body.error.code ?? code;
|
|
97
|
-
|
|
97
|
+
message2 = body.error.message;
|
|
98
98
|
}
|
|
99
99
|
} catch {
|
|
100
100
|
}
|
|
101
|
-
throw new ApiError(res.status, code,
|
|
101
|
+
throw new ApiError(res.status, code, message2);
|
|
102
102
|
}
|
|
103
103
|
async function api(method, path, body, opts = {}) {
|
|
104
104
|
const res = await fetch(`${apiBase()}${path}`, {
|
|
@@ -167,7 +167,7 @@ async function resolveSessionRef(ref) {
|
|
|
167
167
|
return agent2.defaultSessionId;
|
|
168
168
|
}
|
|
169
169
|
async function* streamEvents(sessionId, signal) {
|
|
170
|
-
const { parseSseStream } = await import("./src-
|
|
170
|
+
const { parseSseStream } = await import("./src-S76Y7GHZ.js");
|
|
171
171
|
const res = await fetch(
|
|
172
172
|
`${apiBase()}/sessions/${encodeURIComponent(sessionId)}/stream`,
|
|
173
173
|
{
|
|
@@ -1065,7 +1065,7 @@ async function login(opts) {
|
|
|
1065
1065
|
}
|
|
1066
1066
|
async function browserLogin(verifier, challenge) {
|
|
1067
1067
|
const state = b64url(randomBytes(16));
|
|
1068
|
-
const code = await new Promise((
|
|
1068
|
+
const code = await new Promise((resolve5, reject) => {
|
|
1069
1069
|
const server = createServer((req, res) => {
|
|
1070
1070
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
1071
1071
|
if (url.pathname !== "/callback") {
|
|
@@ -1078,7 +1078,7 @@ async function browserLogin(verifier, challenge) {
|
|
|
1078
1078
|
if (url.searchParams.get("state") !== state || !got) {
|
|
1079
1079
|
reject(new Error("login callback state mismatch"));
|
|
1080
1080
|
} else {
|
|
1081
|
-
|
|
1081
|
+
resolve5(got);
|
|
1082
1082
|
}
|
|
1083
1083
|
server.close();
|
|
1084
1084
|
});
|
|
@@ -1188,9 +1188,9 @@ function renderEvent(event, data) {
|
|
|
1188
1188
|
break;
|
|
1189
1189
|
case "session.error": {
|
|
1190
1190
|
const error = data.error;
|
|
1191
|
-
const
|
|
1192
|
-
if (error?.retry_status === "retrying") console.error(dim(`retrying: ${
|
|
1193
|
-
else console.error(red(`error: ${
|
|
1191
|
+
const message2 = String(error?.message ?? error?.type ?? "unknown");
|
|
1192
|
+
if (error?.retry_status === "retrying") console.error(dim(`retrying: ${message2}`));
|
|
1193
|
+
else console.error(red(`error: ${message2}`));
|
|
1194
1194
|
break;
|
|
1195
1195
|
}
|
|
1196
1196
|
case "session.status_terminated":
|
|
@@ -1242,10 +1242,21 @@ async function readTurn(sessionId, opts = {}) {
|
|
|
1242
1242
|
for (; ; ) {
|
|
1243
1243
|
const reading = latestTurn(await history(sessionId));
|
|
1244
1244
|
if (reading.finished || !opts.wait || Date.now() >= deadline) return reading;
|
|
1245
|
-
await new Promise((
|
|
1245
|
+
await new Promise((resolve5) => setTimeout(resolve5, opts.intervalMs ?? 3e3));
|
|
1246
1246
|
}
|
|
1247
1247
|
}
|
|
1248
1248
|
|
|
1249
|
+
// src/setup.ts
|
|
1250
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
1251
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
1252
|
+
import { createInterface as createInterface3 } from "readline";
|
|
1253
|
+
|
|
1254
|
+
// src/setup-local.ts
|
|
1255
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
1256
|
+
import { homedir as homedir5 } from "os";
|
|
1257
|
+
import { dirname as dirname3, isAbsolute, join as join6, resolve as resolve3 } from "path";
|
|
1258
|
+
import { parse as parseToml2 } from "smol-toml";
|
|
1259
|
+
|
|
1249
1260
|
// src/skill.ts
|
|
1250
1261
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1251
1262
|
import { join as join5 } from "path";
|
|
@@ -1336,6 +1347,9 @@ function installedVersion(dir) {
|
|
|
1336
1347
|
return null;
|
|
1337
1348
|
}
|
|
1338
1349
|
}
|
|
1350
|
+
function isInstalledSkill(dir) {
|
|
1351
|
+
return installedVersion(dir) !== null;
|
|
1352
|
+
}
|
|
1339
1353
|
function installSkill(agent2, opts = {}) {
|
|
1340
1354
|
const dir = join5(agent2.skillsDir, SKILL_NAME);
|
|
1341
1355
|
const previous = installedVersion(dir);
|
|
@@ -1349,11 +1363,154 @@ function installSkill(agent2, opts = {}) {
|
|
|
1349
1363
|
return { agent: agent2.id, path: dir, outcome };
|
|
1350
1364
|
}
|
|
1351
1365
|
|
|
1366
|
+
// src/setup-local.ts
|
|
1367
|
+
var MAX_SKILL_FILE_BYTES = 1024 * 1024;
|
|
1368
|
+
var MAX_IMPORT_DEPTH = 5;
|
|
1369
|
+
var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
1370
|
+
function frontmatterName(skillMd) {
|
|
1371
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd)?.[1];
|
|
1372
|
+
const name = block?.match(/^name:\s*["']?([^"'\r\n]+?)["']?\s*$/m)?.[1];
|
|
1373
|
+
return name?.trim() || null;
|
|
1374
|
+
}
|
|
1375
|
+
function sensitive(name) {
|
|
1376
|
+
return name.startsWith(".env") || name.endsWith(".pem") || name.endsWith(".key");
|
|
1377
|
+
}
|
|
1378
|
+
function skillFiles(root, prefix) {
|
|
1379
|
+
const files2 = [];
|
|
1380
|
+
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
1381
|
+
const path = join6(root, entry.name);
|
|
1382
|
+
if (entry.isDirectory()) {
|
|
1383
|
+
if (!SKIPPED_DIRS.has(entry.name)) files2.push(...skillFiles(path, `${prefix}/${entry.name}`));
|
|
1384
|
+
} else if (entry.isFile() && !sensitive(entry.name) && statSync2(path).size <= MAX_SKILL_FILE_BYTES) {
|
|
1385
|
+
files2.push({ path: `${prefix}/${entry.name}`, bytes: new Uint8Array(readFileSync7(path)) });
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return files2;
|
|
1389
|
+
}
|
|
1390
|
+
function readLocalSkills(agents) {
|
|
1391
|
+
const skills = [];
|
|
1392
|
+
const ignored = [];
|
|
1393
|
+
for (const agent2 of agents) {
|
|
1394
|
+
if (!existsSync5(agent2.skillsDir)) continue;
|
|
1395
|
+
for (const entry of readdirSync2(agent2.skillsDir, { withFileTypes: true })) {
|
|
1396
|
+
const dir = join6(agent2.skillsDir, entry.name);
|
|
1397
|
+
let isDir = entry.isDirectory();
|
|
1398
|
+
if (entry.isSymbolicLink()) {
|
|
1399
|
+
try {
|
|
1400
|
+
isDir = statSync2(dir).isDirectory();
|
|
1401
|
+
} catch {
|
|
1402
|
+
isDir = false;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (!isDir || !existsSync5(join6(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
|
|
1406
|
+
const name = frontmatterName(readFileSync7(join6(dir, "SKILL.md"), "utf8"));
|
|
1407
|
+
if (!name) {
|
|
1408
|
+
ignored.push({ agent: agent2.id, dir, reason: "SKILL.md has no name" });
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
const taken = skills.find((skill) => skill.name === name);
|
|
1412
|
+
if (taken) {
|
|
1413
|
+
ignored.push({ agent: agent2.id, dir, reason: `${taken.agent} already has a skill named ${name}` });
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
const files2 = skillFiles(dir, entry.name);
|
|
1417
|
+
skills.push({ name, agent: agent2.id, dir, files: files2, bytes: files2.reduce((sum, file) => sum + file.bytes.length, 0) });
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return { skills, ignored };
|
|
1421
|
+
}
|
|
1422
|
+
var INSTRUCTION_FILES = { claude_code: "CLAUDE.md", codex: "AGENTS.md" };
|
|
1423
|
+
function inlineImports(content, from, home, depth, seen) {
|
|
1424
|
+
let fence;
|
|
1425
|
+
return content.split(/\r?\n/).map((line) => {
|
|
1426
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
1427
|
+
if (marker) {
|
|
1428
|
+
if (!fence) fence = { character: marker[1][0], length: marker[1].length };
|
|
1429
|
+
else if (marker[1][0] === fence.character && marker[1].length >= fence.length && !marker[2].trim()) fence = void 0;
|
|
1430
|
+
return line;
|
|
1431
|
+
}
|
|
1432
|
+
const target = fence ? void 0 : line.match(/^@(\S+\.(?:md|markdown|txt))$/i)?.[1];
|
|
1433
|
+
if (!target || depth >= MAX_IMPORT_DEPTH) return line;
|
|
1434
|
+
const path = target.startsWith("~/") ? join6(home, target.slice(2)) : isAbsolute(target) ? target : resolve3(dirname3(from), target);
|
|
1435
|
+
if (seen.includes(path)) return "";
|
|
1436
|
+
let imported;
|
|
1437
|
+
try {
|
|
1438
|
+
imported = readFileSync7(path, "utf8");
|
|
1439
|
+
} catch {
|
|
1440
|
+
return line;
|
|
1441
|
+
}
|
|
1442
|
+
seen.push(path);
|
|
1443
|
+
return inlineImports(imported, path, home, depth + 1, seen).trim();
|
|
1444
|
+
}).join("\n");
|
|
1445
|
+
}
|
|
1446
|
+
function readGlobalInstructions(agents, home = homedir5()) {
|
|
1447
|
+
const found = [];
|
|
1448
|
+
for (const agent2 of agents) {
|
|
1449
|
+
const file = INSTRUCTION_FILES[agent2.id];
|
|
1450
|
+
if (!file) continue;
|
|
1451
|
+
const path = join6(agent2.root, file);
|
|
1452
|
+
let raw;
|
|
1453
|
+
try {
|
|
1454
|
+
raw = readFileSync7(path, "utf8");
|
|
1455
|
+
} catch {
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
if (!raw.trim()) continue;
|
|
1459
|
+
const imports = [];
|
|
1460
|
+
found.push({ agent: agent2.id, agentName: agent2.name, path, content: inlineImports(raw, path, home, 0, imports), imports });
|
|
1461
|
+
}
|
|
1462
|
+
return found;
|
|
1463
|
+
}
|
|
1464
|
+
function combineInstructions(found) {
|
|
1465
|
+
const distinct = found.filter((item, index) => found.findIndex((other) => other.content.trim() === item.content.trim()) === index);
|
|
1466
|
+
if (distinct.length <= 1) return distinct[0]?.content ?? "";
|
|
1467
|
+
return distinct.map((item) => `# From ${item.agentName}
|
|
1468
|
+
|
|
1469
|
+
${item.content.trim()}`).join("\n\n");
|
|
1470
|
+
}
|
|
1471
|
+
function readJson2(path) {
|
|
1472
|
+
try {
|
|
1473
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
1474
|
+
} catch {
|
|
1475
|
+
return void 0;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function bearerOf(def) {
|
|
1479
|
+
if (typeof def.bearer_token === "string" && def.bearer_token.trim()) return def.bearer_token.trim();
|
|
1480
|
+
const headers = def.headers ?? def.http_headers;
|
|
1481
|
+
if (!headers || typeof headers !== "object") return null;
|
|
1482
|
+
const entry = Object.entries(headers).find(([name]) => name.toLowerCase() === "authorization");
|
|
1483
|
+
const match = typeof entry?.[1] === "string" ? /^Bearer\s+(\S+)$/i.exec(entry[1].trim()) : null;
|
|
1484
|
+
return match?.[1] ?? null;
|
|
1485
|
+
}
|
|
1486
|
+
function serversOf(agent2, home) {
|
|
1487
|
+
if (agent2.id === "claude_code") return readJson2(join6(home, ".claude.json"))?.mcpServers ?? {};
|
|
1488
|
+
if (agent2.id === "cursor") return readJson2(join6(agent2.root, "mcp.json"))?.mcpServers ?? {};
|
|
1489
|
+
try {
|
|
1490
|
+
return parseToml2(readFileSync7(join6(agent2.root, "config.toml"), "utf8")).mcp_servers ?? {};
|
|
1491
|
+
} catch {
|
|
1492
|
+
return {};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
function findLocalSecrets(agents, home = homedir5()) {
|
|
1496
|
+
const secrets2 = [];
|
|
1497
|
+
for (const agent2 of agents) {
|
|
1498
|
+
for (const [name, def] of Object.entries(serversOf(agent2, home))) {
|
|
1499
|
+
if (!def || typeof def !== "object" || typeof def.url !== "string" || !/^https:\/\//i.test(def.url)) continue;
|
|
1500
|
+
const token = bearerOf(def);
|
|
1501
|
+
if (!token || /^\$\{?[A-Za-z_]/.test(token)) continue;
|
|
1502
|
+
if (secrets2.some((secret) => secret.url === def.url)) continue;
|
|
1503
|
+
secrets2.push({ name: secrets2.some((secret) => secret.name === name) ? `${name}-${agent2.id}` : name, agent: agent2.id, url: def.url, token });
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return secrets2;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1352
1509
|
// src/subscriptions.ts
|
|
1353
1510
|
import { spawn as spawn3 } from "child_process";
|
|
1354
|
-
import { existsSync as
|
|
1355
|
-
import { homedir as
|
|
1356
|
-
import { join as
|
|
1511
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
|
|
1512
|
+
import { homedir as homedir6 } from "os";
|
|
1513
|
+
import { join as join7 } from "path";
|
|
1357
1514
|
import { createInterface as createInterface2 } from "readline";
|
|
1358
1515
|
var ALIASES = {
|
|
1359
1516
|
claude: "anthropic",
|
|
@@ -1438,20 +1595,35 @@ async function connectClaude() {
|
|
|
1438
1595
|
const expiry = sub.expiresAt ? ` (expires ~${sub.expiresAt.slice(0, 10)})` : "";
|
|
1439
1596
|
console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
|
|
1440
1597
|
}
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
if (!existsSync5(path)) {
|
|
1598
|
+
function codexSignIn(env = process.env, home = homedir6()) {
|
|
1599
|
+
const path = join7(env.CODEX_HOME || join7(home, ".codex"), "auth.json");
|
|
1600
|
+
if (!existsSync6(path)) {
|
|
1445
1601
|
throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
|
|
1446
1602
|
}
|
|
1447
|
-
const raw =
|
|
1448
|
-
let accountId;
|
|
1603
|
+
const raw = readFileSync8(path, "utf8");
|
|
1449
1604
|
try {
|
|
1450
1605
|
const parsed = JSON.parse(raw);
|
|
1451
|
-
|
|
1606
|
+
return { path, raw, account: parsed.tokens?.account_id };
|
|
1452
1607
|
} catch {
|
|
1453
1608
|
throw new ApiError(400, "invalid_codex_auth", `${path} isn't valid JSON \u2014 re-run \`codex login\``);
|
|
1454
1609
|
}
|
|
1610
|
+
}
|
|
1611
|
+
function cursorSignIn(env = process.env, home = homedir6()) {
|
|
1612
|
+
const path = join7(env.XDG_CONFIG_HOME || join7(home, ".config"), "cursor", "auth.json");
|
|
1613
|
+
if (!existsSync6(path)) {
|
|
1614
|
+
throw new ApiError(404, "no_cursor_auth", `no ${path} \u2014 run \`cursor-agent login\` first`);
|
|
1615
|
+
}
|
|
1616
|
+
const raw = readFileSync8(path, "utf8");
|
|
1617
|
+
try {
|
|
1618
|
+
const parsed = JSON.parse(raw);
|
|
1619
|
+
if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string") throw new Error("shape");
|
|
1620
|
+
return { path, raw };
|
|
1621
|
+
} catch {
|
|
1622
|
+
throw new ApiError(400, "invalid_cursor_auth", `${path} isn't a Cursor sign-in \u2014 re-run \`cursor-agent login\``);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
async function connectChatgpt(opts) {
|
|
1626
|
+
const { path, raw, account: accountId } = codexSignIn();
|
|
1455
1627
|
console.log(`Found Codex sign-in at ${path}${accountId ? ` (account ${accountId})` : ""}.`);
|
|
1456
1628
|
if (!opts.yes) {
|
|
1457
1629
|
const ok = await confirm2("Upload it so your cloud Codex agents run on your ChatGPT plan?");
|
|
@@ -1508,6 +1680,292 @@ async function disconnect(name, opts) {
|
|
|
1508
1680
|
}
|
|
1509
1681
|
}
|
|
1510
1682
|
|
|
1683
|
+
// src/setup.ts
|
|
1684
|
+
var SETUP_STEPS = ["skill", "logins", "import", "secrets"];
|
|
1685
|
+
function reportPath() {
|
|
1686
|
+
return join8(dirname4(configPath()), "setup.json");
|
|
1687
|
+
}
|
|
1688
|
+
function loadReport() {
|
|
1689
|
+
try {
|
|
1690
|
+
const saved = JSON.parse(readFileSync9(reportPath(), "utf8"));
|
|
1691
|
+
return { agents: saved.agents ?? [], plans: saved.plans ?? [], ...saved.secrets ? { secrets: saved.secrets } : {} };
|
|
1692
|
+
} catch {
|
|
1693
|
+
return { agents: [], plans: [] };
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
function mergeReport(base, next) {
|
|
1697
|
+
const agents = [...base.agents.filter((old) => !next.agents.some((item) => item.id === old.id)), ...next.agents];
|
|
1698
|
+
const plans = [...base.plans.filter((old) => !next.plans.some((item) => item.provider === old.provider)), ...next.plans];
|
|
1699
|
+
const secrets2 = base.secrets || next.secrets ? { saved: (base.secrets?.saved ?? 0) + (next.secrets?.saved ?? 0), skipped: next.secrets?.skipped ?? base.secrets?.skipped ?? 0 } : void 0;
|
|
1700
|
+
return { agents, plans, ...secrets2 ? { secrets: secrets2 } : {} };
|
|
1701
|
+
}
|
|
1702
|
+
function saveReport(report) {
|
|
1703
|
+
mkdirSync3(dirname4(reportPath()), { recursive: true });
|
|
1704
|
+
writeFileSync5(reportPath(), `${JSON.stringify(report, null, 2)}
|
|
1705
|
+
`, { mode: 384 });
|
|
1706
|
+
}
|
|
1707
|
+
function confirm3(question) {
|
|
1708
|
+
return new Promise((done) => {
|
|
1709
|
+
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
1710
|
+
rl.question(`${question} [Y/n] `, (answer) => {
|
|
1711
|
+
rl.close();
|
|
1712
|
+
done(/^(y(es)?)?$/i.test(answer.trim()));
|
|
1713
|
+
});
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
function kb2(bytes) {
|
|
1717
|
+
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
1718
|
+
}
|
|
1719
|
+
function describe(event) {
|
|
1720
|
+
switch (event.type) {
|
|
1721
|
+
case "agents":
|
|
1722
|
+
return event.agents.length ? `Found on this computer: ${event.agents.map((agent2) => agent2.name).join(", ")}` : "No agent found on this computer (looked for Claude Code, Codex and Cursor).";
|
|
1723
|
+
case "skill":
|
|
1724
|
+
return ` AgentSky skill ${event.outcome === "current" ? "already up to date" : event.outcome} for ${event.agent}`;
|
|
1725
|
+
case "plan":
|
|
1726
|
+
return ` ${event.provider} plan: ${event.outcome === "already" ? "already connected" : event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
|
|
1727
|
+
case "found": {
|
|
1728
|
+
const lines = [
|
|
1729
|
+
` ${event.skills.length} skill${event.skills.length === 1 ? "" : "s"}${event.skills.length ? `: ${event.skills.map((skill) => skill.name).join(", ")}` : ""}`,
|
|
1730
|
+
...event.instructions.map((item) => ` instructions from ${item.agent}: ${item.path} (${kb2(item.bytes)}${item.imports.length ? `, with ${item.imports.length} imported file${item.imports.length === 1 ? "" : "s"}` : ""})`),
|
|
1731
|
+
...event.instructions.length ? [] : [" no global instructions file"],
|
|
1732
|
+
...event.ignored.map((item) => ` left out ${item.dir}: ${item.reason}`),
|
|
1733
|
+
...event.secrets.map((secret) => ` key for ${secret.name} (${secret.url}), from ${secret.agent}`)
|
|
1734
|
+
];
|
|
1735
|
+
return lines.join("\n");
|
|
1736
|
+
}
|
|
1737
|
+
case "imported_skill":
|
|
1738
|
+
return ` skill ${event.name}: ${event.outcome}`;
|
|
1739
|
+
case "instructions":
|
|
1740
|
+
return event.outcome === "none" ? null : ` instructions: ${event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
|
|
1741
|
+
case "secret":
|
|
1742
|
+
return ` key for ${event.name}: ${event.outcome === "already" ? "already in your vault" : event.outcome}${event.detail ? ` (${event.detail})` : ""}`;
|
|
1743
|
+
case "error":
|
|
1744
|
+
return `sky: ${event.message} (${event.code})`;
|
|
1745
|
+
case "done":
|
|
1746
|
+
return "Done. Your agentsky.dev tab has updated itself.";
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
async function cloneCall(key, path, init = {}) {
|
|
1750
|
+
const res = await fetch(`${agentUseBase()}/api/clone/${path}`, {
|
|
1751
|
+
method: "POST",
|
|
1752
|
+
headers: {
|
|
1753
|
+
"user-agent": USER_AGENT,
|
|
1754
|
+
authorization: `Bearer ${key}`,
|
|
1755
|
+
...init.json !== void 0 ? { "content-type": "application/json" } : {}
|
|
1756
|
+
},
|
|
1757
|
+
body: init.form ?? (init.json !== void 0 ? JSON.stringify(init.json) : void 0)
|
|
1758
|
+
});
|
|
1759
|
+
const body = await res.json().catch(() => ({}));
|
|
1760
|
+
if (res.ok) return { ok: true, body };
|
|
1761
|
+
return { ok: false, status: res.status, code: body.error?.code ?? "http_error", message: body.error?.message ?? `${res.status} ${res.statusText}` };
|
|
1762
|
+
}
|
|
1763
|
+
async function mintKey() {
|
|
1764
|
+
const token = apiToken();
|
|
1765
|
+
if (!token) throw new ApiError(401, "not_logged_in", "not logged in \u2014 run `sky auth login`");
|
|
1766
|
+
const res = await fetch(`${agentUseBase()}/api/clone/grants`, { method: "POST", headers: { "user-agent": USER_AGENT, authorization: `Bearer ${token}` } });
|
|
1767
|
+
const body = await res.json().catch(() => ({}));
|
|
1768
|
+
if (!res.ok || !body.key) throw new ApiError(res.status, body.error?.code ?? "http_error", body.error?.message ?? `${res.status} ${res.statusText}`);
|
|
1769
|
+
return body.key;
|
|
1770
|
+
}
|
|
1771
|
+
async function connectedProviders() {
|
|
1772
|
+
const { modelSubscriptions } = await api("GET", "/model-subscriptions");
|
|
1773
|
+
return new Set(modelSubscriptions.map((subscription) => subscription.provider));
|
|
1774
|
+
}
|
|
1775
|
+
async function defaultVaultId() {
|
|
1776
|
+
const { vaults } = await api("GET", "/vaults");
|
|
1777
|
+
const existing = vaults.find((vault) => vault.display_name === "default");
|
|
1778
|
+
if (existing) return existing.id;
|
|
1779
|
+
return (await api("POST", "/vaults", { display_name: "default" })).vault.id;
|
|
1780
|
+
}
|
|
1781
|
+
function message(error) {
|
|
1782
|
+
return error instanceof Error ? error.message : String(error);
|
|
1783
|
+
}
|
|
1784
|
+
async function runSetup(opts) {
|
|
1785
|
+
const steps = opts.steps?.length ? opts.steps : [...SETUP_STEPS];
|
|
1786
|
+
const interactive = !opts.json && !opts.yes && !opts.dryRun;
|
|
1787
|
+
const emit = (event) => {
|
|
1788
|
+
if (opts.json) console.log(JSON.stringify(event));
|
|
1789
|
+
else {
|
|
1790
|
+
const line = describe(event);
|
|
1791
|
+
if (line) console.log(line);
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
const agreed = async (question) => !interactive || confirm3(question);
|
|
1795
|
+
const has = (step) => steps.includes(step);
|
|
1796
|
+
const agents = selectLocalAgents(opts.agents);
|
|
1797
|
+
emit({ type: "agents", agents: agents.map((agent2) => ({ id: agent2.id, name: agent2.name })) });
|
|
1798
|
+
const needsToken = !opts.dryRun && (has("logins") || has("secrets") || has("import") && !opts.key);
|
|
1799
|
+
if (needsToken && !apiToken()) {
|
|
1800
|
+
if (opts.json) throw new ApiError(401, "not_logged_in", "not logged in \u2014 run `sky auth login`");
|
|
1801
|
+
console.log("First, sign this computer in to AgentSky.");
|
|
1802
|
+
await login({});
|
|
1803
|
+
}
|
|
1804
|
+
const run2 = { agents: [], plans: [] };
|
|
1805
|
+
const earlier = loadReport();
|
|
1806
|
+
const wantsImport = has("import");
|
|
1807
|
+
const wantsSecrets = has("secrets");
|
|
1808
|
+
const tellsWorkspace = !opts.dryRun && (wantsImport || Boolean(opts.key));
|
|
1809
|
+
let key;
|
|
1810
|
+
let held = /* @__PURE__ */ new Map();
|
|
1811
|
+
const tell = async () => {
|
|
1812
|
+
if (!tellsWorkspace) return;
|
|
1813
|
+
key ??= opts.key ?? await mintKey();
|
|
1814
|
+
const claimed = await cloneCall(key, "claim", { json: { setup: mergeReport(earlier, run2) } });
|
|
1815
|
+
if (!claimed.ok) throw new ApiError(claimed.status, claimed.code, claimed.message);
|
|
1816
|
+
held = new Map((claimed.body.skills ?? []).map((skill) => [skill.name, skill.fingerprint]));
|
|
1817
|
+
};
|
|
1818
|
+
await tell();
|
|
1819
|
+
if (has("skill") && agents.length && await agreed("Add the AgentSky skill to these agents, so they can hand work to others?")) {
|
|
1820
|
+
for (const agent2 of agents) {
|
|
1821
|
+
try {
|
|
1822
|
+
const installed = installSkill(agent2, { dryRun: opts.dryRun });
|
|
1823
|
+
emit({ type: "skill", agent: agent2.id, outcome: installed.outcome, path: installed.path });
|
|
1824
|
+
if (!opts.dryRun) run2.agents.push({ id: agent2.id, skill: installed.outcome });
|
|
1825
|
+
} catch (error) {
|
|
1826
|
+
emit({ type: "skill", agent: agent2.id, outcome: "failed", path: agent2.skillsDir });
|
|
1827
|
+
emit({ type: "error", code: "skill_install_failed", message: message(error) });
|
|
1828
|
+
run2.agents.push({ id: agent2.id, skill: "failed" });
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
await tell();
|
|
1832
|
+
}
|
|
1833
|
+
if (has("logins") && agents.length) {
|
|
1834
|
+
await connectPlans(agents, opts, interactive, emit, run2);
|
|
1835
|
+
await tell();
|
|
1836
|
+
}
|
|
1837
|
+
const { skills, ignored } = wantsImport ? readLocalSkills(agents) : { skills: [], ignored: [] };
|
|
1838
|
+
const instructions = wantsImport ? readGlobalInstructions(agents) : [];
|
|
1839
|
+
const secrets2 = wantsSecrets ? findLocalSecrets(agents) : [];
|
|
1840
|
+
if (wantsImport || wantsSecrets) {
|
|
1841
|
+
emit({
|
|
1842
|
+
type: "found",
|
|
1843
|
+
skills: skills.map((skill) => ({ name: skill.name, agent: skill.agent, files: skill.files.length, bytes: skill.bytes })),
|
|
1844
|
+
ignored: ignored.map((item) => ({ dir: item.dir, reason: item.reason })),
|
|
1845
|
+
instructions: instructions.map((item) => ({ agent: item.agent, path: item.path, bytes: Buffer.byteLength(item.content), imports: item.imports })),
|
|
1846
|
+
secrets: secrets2.map((secret) => ({ name: secret.name, agent: secret.agent, url: secret.url }))
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
if (opts.dryRun) return;
|
|
1850
|
+
if (wantsSecrets && secrets2.length) {
|
|
1851
|
+
run2.secrets = await saveSecrets(secrets2, opts, interactive, emit);
|
|
1852
|
+
await tell();
|
|
1853
|
+
}
|
|
1854
|
+
const report = mergeReport(earlier, run2);
|
|
1855
|
+
saveReport(report);
|
|
1856
|
+
if (key) {
|
|
1857
|
+
const sendFiles = wantsImport && (skills.length > 0 || instructions.length > 0) && await agreed("Send these skills and instructions to your AgentSky workspace?");
|
|
1858
|
+
await importToWorkspace(key, held, sendFiles ? skills : [], sendFiles ? combineInstructions(instructions) : "", emit);
|
|
1859
|
+
}
|
|
1860
|
+
emit({ type: "done", report });
|
|
1861
|
+
}
|
|
1862
|
+
async function connectPlans(agents, opts, interactive, emit, run2) {
|
|
1863
|
+
const connected = opts.dryRun && !apiToken() ? /* @__PURE__ */ new Set() : await connectedProviders();
|
|
1864
|
+
const record = (provider, outcome, detail) => {
|
|
1865
|
+
emit({ type: "plan", provider, outcome, ...detail ? { detail } : {} });
|
|
1866
|
+
if (outcome === "available") return;
|
|
1867
|
+
run2.plans.push({ provider, outcome: outcome === "already" ? "connected" : outcome });
|
|
1868
|
+
};
|
|
1869
|
+
const copy = async (provider, name, read) => {
|
|
1870
|
+
if (connected.has(provider)) return record(provider, "already");
|
|
1871
|
+
let found;
|
|
1872
|
+
try {
|
|
1873
|
+
found = read();
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
if (error instanceof ApiError && error.status === 404) return;
|
|
1876
|
+
return record(provider, "failed", message(error));
|
|
1877
|
+
}
|
|
1878
|
+
if (opts.dryRun) return record(provider, "available", found.account ? `account ${found.account}` : void 0);
|
|
1879
|
+
if (interactive && !await confirm3(`Use the ${name} sign-in on this computer${found.account ? ` (account ${found.account})` : ""} for your cloud agents?`)) {
|
|
1880
|
+
return record(provider, "skipped");
|
|
1881
|
+
}
|
|
1882
|
+
try {
|
|
1883
|
+
await put(provider, found.raw);
|
|
1884
|
+
record(provider, "connected");
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
record(provider, "failed", message(error));
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1889
|
+
if (agents.some((agent2) => agent2.id === "claude_code")) {
|
|
1890
|
+
if (connected.has("anthropic")) record("anthropic", "already");
|
|
1891
|
+
else if (opts.dryRun) record("anthropic", "available");
|
|
1892
|
+
else if (opts.json || opts.yes || opts.claude === false) record("anthropic", "skipped", "needs a browser approval");
|
|
1893
|
+
else if (!await confirm3("Connect your Claude plan? It opens the browser for one approval and does not sign Claude Code out.")) record("anthropic", "skipped");
|
|
1894
|
+
else {
|
|
1895
|
+
const token = await mintClaudeToken();
|
|
1896
|
+
if (!token) record("anthropic", "failed", "`claude setup-token` did not return a token");
|
|
1897
|
+
else {
|
|
1898
|
+
try {
|
|
1899
|
+
await put("anthropic", token);
|
|
1900
|
+
record("anthropic", "connected");
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
record("anthropic", "failed", message(error));
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (agents.some((agent2) => agent2.id === "codex")) await copy("openai", "ChatGPT", () => codexSignIn());
|
|
1908
|
+
if (agents.some((agent2) => agent2.id === "cursor")) await copy("cursor", "Cursor", () => cursorSignIn());
|
|
1909
|
+
}
|
|
1910
|
+
async function saveSecrets(found, opts, interactive, emit) {
|
|
1911
|
+
const counts = { saved: 0, skipped: 0 };
|
|
1912
|
+
let vaultId;
|
|
1913
|
+
for (const secret of found) {
|
|
1914
|
+
const chosen = interactive ? await confirm3(`Save the key your ${secret.agent} uses for ${secret.name} (${secret.url}) to your vault?`) : (opts.secrets ?? []).includes(secret.name);
|
|
1915
|
+
if (!chosen) {
|
|
1916
|
+
counts.skipped += 1;
|
|
1917
|
+
emit({ type: "secret", name: secret.name, outcome: "skipped" });
|
|
1918
|
+
continue;
|
|
1919
|
+
}
|
|
1920
|
+
try {
|
|
1921
|
+
vaultId ??= await defaultVaultId();
|
|
1922
|
+
await api("POST", `/vaults/${encodeURIComponent(vaultId)}/secrets`, {
|
|
1923
|
+
display_name: secret.name,
|
|
1924
|
+
auth: { type: "static_bearer", mcp_server_url: secret.url, token: secret.token }
|
|
1925
|
+
});
|
|
1926
|
+
counts.saved += 1;
|
|
1927
|
+
emit({ type: "secret", name: secret.name, outcome: "saved" });
|
|
1928
|
+
} catch (error) {
|
|
1929
|
+
if (error instanceof ApiError && error.status === 409) emit({ type: "secret", name: secret.name, outcome: "already" });
|
|
1930
|
+
else emit({ type: "secret", name: secret.name, outcome: "failed", detail: message(error) });
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return counts;
|
|
1934
|
+
}
|
|
1935
|
+
async function importToWorkspace(key, held, skills, instructions, emit) {
|
|
1936
|
+
const outcomes = [];
|
|
1937
|
+
let signedOut = false;
|
|
1938
|
+
for (const skill of skills) {
|
|
1939
|
+
if (signedOut) {
|
|
1940
|
+
outcomes.push({ name: skill.name, outcome: "skipped: sign in to agentsky.dev again" });
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
const form = new FormData();
|
|
1944
|
+
form.append("name", skill.name);
|
|
1945
|
+
form.append("expectedFingerprint", held.get(skill.name) ?? "");
|
|
1946
|
+
for (const file of skill.files) form.append("files", new Blob([new Uint8Array(file.bytes)]), file.path);
|
|
1947
|
+
const sent = await cloneCall(key, "skills", { form });
|
|
1948
|
+
const outcome = sent.ok ? String(sent.body.status ?? "created") : `skipped: ${sent.message}`;
|
|
1949
|
+
if (!sent.ok && sent.code === "sign_in_required") signedOut = true;
|
|
1950
|
+
outcomes.push({ name: skill.name, outcome });
|
|
1951
|
+
emit({ type: "imported_skill", name: skill.name, outcome });
|
|
1952
|
+
}
|
|
1953
|
+
let instructionsOutcome = null;
|
|
1954
|
+
let instructionError;
|
|
1955
|
+
if (instructions.trim()) {
|
|
1956
|
+
const sent = await cloneCall(key, "instructions", { json: { instructions } });
|
|
1957
|
+
instructionsOutcome = sent.ok ? "applied" : "failed";
|
|
1958
|
+
if (!sent.ok) instructionError = sent.message;
|
|
1959
|
+
emit({ type: "instructions", outcome: instructionsOutcome, ...instructionError ? { detail: instructionError } : {} });
|
|
1960
|
+
} else {
|
|
1961
|
+
emit({ type: "instructions", outcome: "none" });
|
|
1962
|
+
}
|
|
1963
|
+
const finished = await cloneCall(key, "finish", {
|
|
1964
|
+
json: { summary: { skills: outcomes, instructions: instructionsOutcome, ...instructionError ? { instructionError } : {} } }
|
|
1965
|
+
});
|
|
1966
|
+
if (!finished.ok) emit({ type: "error", code: finished.code, message: finished.message });
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1511
1969
|
// src/index.ts
|
|
1512
1970
|
function printModelBilling(session2) {
|
|
1513
1971
|
if (session2.modelBilling !== "account") return;
|
|
@@ -1789,7 +2247,7 @@ session.command("read <ref>").description("what the agent has said since the las
|
|
|
1789
2247
|
console.log(JSON.stringify(reading));
|
|
1790
2248
|
return;
|
|
1791
2249
|
}
|
|
1792
|
-
for (const
|
|
2250
|
+
for (const message2 of reading.messages) console.log(message2);
|
|
1793
2251
|
for (const error of reading.errors) console.error(`error: ${error}`);
|
|
1794
2252
|
if (!reading.finished) console.error("The agent is still working. Run this again to read more.");
|
|
1795
2253
|
})
|
|
@@ -1914,7 +2372,7 @@ async function sendAndFollow(sessionId, text, opts) {
|
|
|
1914
2372
|
async function chatLoop(sessionId, opts = {}) {
|
|
1915
2373
|
console.log(`Chatting with ${sessionId} \u2014 Ctrl-C to leave.`);
|
|
1916
2374
|
const controller = new AbortController();
|
|
1917
|
-
const rl =
|
|
2375
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout, prompt: "> " });
|
|
1918
2376
|
let system = opts.system;
|
|
1919
2377
|
void (async () => {
|
|
1920
2378
|
try {
|
|
@@ -1980,6 +2438,20 @@ program.command("skill").description("the AgentSky skill for the agents on this
|
|
|
1980
2438
|
for (const i of installs) console.log(`${i.agent.padEnd(12)} ${verb[i.outcome].padEnd(14)} ${i.path}`);
|
|
1981
2439
|
})
|
|
1982
2440
|
);
|
|
2441
|
+
program.command("setup [steps...]").description("bring this machine's agents to AgentSky: the skill, your plans, your skills and instructions, your tool keys").addHelpText("after", `
|
|
2442
|
+
Steps (all when none is named): ${SETUP_STEPS.join(" | ")}`).option("--key <key>", "a setup key from agentsky.dev/setup").option("--agent <id...>", "only these agents: claude_code | codex | cursor").option("--secret <name...>", "save these tool keys to your vault (asked one by one when omitted)").option("--no-claude", "leave the Claude plan alone").option("--dry-run", "say what would be done; write and send nothing").option("-y, --yes", "do not ask before each step").option("--json", "print progress as one JSON object per line; never asks").action(
|
|
2443
|
+
run(async (steps, opts) => {
|
|
2444
|
+
const unknown = steps.find((step) => !SETUP_STEPS.includes(step));
|
|
2445
|
+
if (unknown) throw new Error(`unknown step ${unknown} \u2014 expected ${SETUP_STEPS.join(" | ")}`);
|
|
2446
|
+
try {
|
|
2447
|
+
await runSetup({ steps, key: opts.key, agents: opts.agent, secrets: opts.secret, claude: opts.claude, dryRun: opts.dryRun, yes: opts.yes, json: opts.json });
|
|
2448
|
+
} catch (error) {
|
|
2449
|
+
if (!opts.json) throw error;
|
|
2450
|
+
console.log(JSON.stringify({ type: "error", code: error instanceof ApiError ? error.code : "failed", message: error instanceof Error ? error.message : String(error) }));
|
|
2451
|
+
process.exit(1);
|
|
2452
|
+
}
|
|
2453
|
+
})
|
|
2454
|
+
);
|
|
1983
2455
|
var secrets = program.command("secrets").description("agent secrets (values write-only)");
|
|
1984
2456
|
secrets.command("set <id> <pairs...>").description("set KEY=VALUE pairs on the agent").action(
|
|
1985
2457
|
run(async (id, pairs) => {
|
|
@@ -2014,8 +2486,8 @@ secrets.command("unset <id> <key>").action(
|
|
|
2014
2486
|
);
|
|
2015
2487
|
program.command("launch").description("scaffold ./agent.toml if absent, create the agent, print its address").option("-f, --file <path>", "agent file", "agent.toml").option("--type <type>", "override the file's harness: hermes | claude_code | codex | openclaw | pi | dsh | kimi_code | opencode").option("--llm <llm>", "override the file's model (must suit the harness)").action(
|
|
2016
2488
|
run(async (opts) => {
|
|
2017
|
-
const path =
|
|
2018
|
-
if (!
|
|
2489
|
+
const path = resolve4(opts.file);
|
|
2490
|
+
if (!existsSync7(path)) {
|
|
2019
2491
|
const name = basename3(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
|
|
2020
2492
|
scaffoldAgentFile(path, name);
|
|
2021
2493
|
console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
|
|
@@ -2047,7 +2519,7 @@ program.command("launch").description("scaffold ./agent.toml if absent, create t
|
|
|
2047
2519
|
);
|
|
2048
2520
|
program.command("deploy").description("re-apply ./agent.toml to the existing agent (idempotent)").option("-f, --file <path>", "agent file", "agent.toml").action(
|
|
2049
2521
|
run(async (opts) => {
|
|
2050
|
-
const path =
|
|
2522
|
+
const path = resolve4(opts.file);
|
|
2051
2523
|
const { file, spec } = readAgentFile(path);
|
|
2052
2524
|
if (!file.id) {
|
|
2053
2525
|
console.log("No agent id in the file yet \u2014 running launch instead.");
|
|
@@ -2124,13 +2596,13 @@ program.command("import").description("import a local Claude Code or Codex sessi
|
|
|
2124
2596
|
console.log(` ${ws}`);
|
|
2125
2597
|
});
|
|
2126
2598
|
console.log();
|
|
2127
|
-
const rl =
|
|
2599
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
2128
2600
|
const answer = await new Promise(
|
|
2129
|
-
(
|
|
2601
|
+
(resolve5) => rl.question(
|
|
2130
2602
|
`Import which session? [1-${candidates.length}, or comma-separated, or "all"]: `,
|
|
2131
2603
|
(a) => {
|
|
2132
2604
|
rl.close();
|
|
2133
|
-
|
|
2605
|
+
resolve5(a.trim());
|
|
2134
2606
|
}
|
|
2135
2607
|
)
|
|
2136
2608
|
);
|
|
@@ -2150,10 +2622,10 @@ program.command("import").description("import a local Claude Code or Codex sessi
|
|
|
2150
2622
|
console.log("The recorded conversation is redacted locally. Local project files and login credentials are not transferred.");
|
|
2151
2623
|
if (!opts.yes) {
|
|
2152
2624
|
if (!process.stdin.isTTY) throw new Error("Use --yes to confirm this transfer in a non-interactive terminal.");
|
|
2153
|
-
const prompt =
|
|
2154
|
-
const answer = await new Promise((
|
|
2625
|
+
const prompt = createInterface4({ input: process.stdin, output: process.stdout });
|
|
2626
|
+
const answer = await new Promise((resolve5) => prompt.question("Upload these selected conversations? [y/N] ", (value) => {
|
|
2155
2627
|
prompt.close();
|
|
2156
|
-
|
|
2628
|
+
resolve5(value);
|
|
2157
2629
|
}));
|
|
2158
2630
|
if (!/^y(?:es)?$/i.test(answer.trim())) {
|
|
2159
2631
|
console.log("Cancelled. Nothing uploaded.");
|
|
@@ -2213,7 +2685,7 @@ files.command("rm <id>").description("delete a file (a session that already rece
|
|
|
2213
2685
|
files.command("download <id>").description("download a session file's bytes").requiredOption("-o, --out <path>", "where to write the bytes").action(
|
|
2214
2686
|
run(async (id, opts) => {
|
|
2215
2687
|
const { bytes } = await downloadFile(id);
|
|
2216
|
-
|
|
2688
|
+
writeFileSync6(opts.out, bytes);
|
|
2217
2689
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
2218
2690
|
})
|
|
2219
2691
|
);
|
package/dist/products.js
CHANGED