@agentsky/sky 2.5.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
|
{
|
|
@@ -402,6 +402,62 @@ You are ${name}, a helpful long-lived agent.
|
|
|
402
402
|
);
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
// src/catalog.ts
|
|
406
|
+
var bare = (handle) => handle.replace(/^@/, "");
|
|
407
|
+
var optionalAuth = () => ({ auth: Boolean(apiToken()) });
|
|
408
|
+
function catalogQuery(filter) {
|
|
409
|
+
const params = new URLSearchParams();
|
|
410
|
+
if (filter.text?.trim()) params.set("q", filter.text.trim());
|
|
411
|
+
if (filter.category) params.set("category", filter.category);
|
|
412
|
+
const query = params.toString();
|
|
413
|
+
return query ? `?${query}` : "";
|
|
414
|
+
}
|
|
415
|
+
function listingBody(agentId, options) {
|
|
416
|
+
return {
|
|
417
|
+
agent_id: agentId,
|
|
418
|
+
handle: bare(options.handle),
|
|
419
|
+
tagline: options.tagline,
|
|
420
|
+
...options.description ? { description: options.description } : {},
|
|
421
|
+
...options.category?.length ? { categories: options.category } : {},
|
|
422
|
+
...options.does?.length ? { does: options.does } : {},
|
|
423
|
+
...options.doesNot?.length ? { does_not: options.doesNot } : {},
|
|
424
|
+
...options.example?.length ? { examples: options.example } : {}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
async function listCatalog(filter = {}) {
|
|
428
|
+
return (await api("GET", `/catalog/agents${catalogQuery(filter)}`, void 0, optionalAuth())).agents;
|
|
429
|
+
}
|
|
430
|
+
async function showCatalogAgent(handle) {
|
|
431
|
+
return (await api("GET", `/catalog/agents/${encodeURIComponent(bare(handle))}`, void 0, optionalAuth())).agent;
|
|
432
|
+
}
|
|
433
|
+
async function listCategories() {
|
|
434
|
+
return (await api("GET", "/catalog/categories", void 0, { auth: false })).categories;
|
|
435
|
+
}
|
|
436
|
+
async function publishListing(agentId, options) {
|
|
437
|
+
return (await api("POST", "/catalog/listings", listingBody(agentId, options))).agent;
|
|
438
|
+
}
|
|
439
|
+
async function removeListing(handle) {
|
|
440
|
+
await api("DELETE", `/catalog/listings/${encodeURIComponent(bare(handle))}`);
|
|
441
|
+
}
|
|
442
|
+
var uses = (agent2) => `${agent2.stats.uses} ${agent2.stats.uses === 1 ? "use" : "uses"}`;
|
|
443
|
+
var mine = (agent2) => agent2.visibility === "workspace" ? " (yours)" : "";
|
|
444
|
+
function renderCatalogAgent(agent2) {
|
|
445
|
+
const section = (title, lines) => lines.length ? [`${title}:`, ...lines.map((line) => ` - ${line}`)] : [];
|
|
446
|
+
return [
|
|
447
|
+
`@${agent2.handle} ${agent2.name}${mine(agent2)}`,
|
|
448
|
+
agent2.tagline,
|
|
449
|
+
...agent2.description && agent2.description !== agent2.tagline ? ["", agent2.description] : [],
|
|
450
|
+
"",
|
|
451
|
+
`Categories: ${agent2.categories.join(", ") || "none"}`,
|
|
452
|
+
`Used: ${uses(agent2)}`,
|
|
453
|
+
...section("Does", agent2.does),
|
|
454
|
+
...section("Does not", agent2.does_not),
|
|
455
|
+
...section("Examples", agent2.examples),
|
|
456
|
+
"",
|
|
457
|
+
`Hire it: sky session create @${agent2.handle} --message "\u2026"`
|
|
458
|
+
].join("\n");
|
|
459
|
+
}
|
|
460
|
+
|
|
405
461
|
// src/clone.ts
|
|
406
462
|
import { spawn } from "child_process";
|
|
407
463
|
import { existsSync as existsSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -1009,7 +1065,7 @@ async function login(opts) {
|
|
|
1009
1065
|
}
|
|
1010
1066
|
async function browserLogin(verifier, challenge) {
|
|
1011
1067
|
const state = b64url(randomBytes(16));
|
|
1012
|
-
const code = await new Promise((
|
|
1068
|
+
const code = await new Promise((resolve5, reject) => {
|
|
1013
1069
|
const server = createServer((req, res) => {
|
|
1014
1070
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
1015
1071
|
if (url.pathname !== "/callback") {
|
|
@@ -1022,7 +1078,7 @@ async function browserLogin(verifier, challenge) {
|
|
|
1022
1078
|
if (url.searchParams.get("state") !== state || !got) {
|
|
1023
1079
|
reject(new Error("login callback state mismatch"));
|
|
1024
1080
|
} else {
|
|
1025
|
-
|
|
1081
|
+
resolve5(got);
|
|
1026
1082
|
}
|
|
1027
1083
|
server.close();
|
|
1028
1084
|
});
|
|
@@ -1132,9 +1188,9 @@ function renderEvent(event, data) {
|
|
|
1132
1188
|
break;
|
|
1133
1189
|
case "session.error": {
|
|
1134
1190
|
const error = data.error;
|
|
1135
|
-
const
|
|
1136
|
-
if (error?.retry_status === "retrying") console.error(dim(`retrying: ${
|
|
1137
|
-
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}`));
|
|
1138
1194
|
break;
|
|
1139
1195
|
}
|
|
1140
1196
|
case "session.status_terminated":
|
|
@@ -1186,10 +1242,21 @@ async function readTurn(sessionId, opts = {}) {
|
|
|
1186
1242
|
for (; ; ) {
|
|
1187
1243
|
const reading = latestTurn(await history(sessionId));
|
|
1188
1244
|
if (reading.finished || !opts.wait || Date.now() >= deadline) return reading;
|
|
1189
|
-
await new Promise((
|
|
1245
|
+
await new Promise((resolve5) => setTimeout(resolve5, opts.intervalMs ?? 3e3));
|
|
1190
1246
|
}
|
|
1191
1247
|
}
|
|
1192
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
|
+
|
|
1193
1260
|
// src/skill.ts
|
|
1194
1261
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
1195
1262
|
import { join as join5 } from "path";
|
|
@@ -1280,6 +1347,9 @@ function installedVersion(dir) {
|
|
|
1280
1347
|
return null;
|
|
1281
1348
|
}
|
|
1282
1349
|
}
|
|
1350
|
+
function isInstalledSkill(dir) {
|
|
1351
|
+
return installedVersion(dir) !== null;
|
|
1352
|
+
}
|
|
1283
1353
|
function installSkill(agent2, opts = {}) {
|
|
1284
1354
|
const dir = join5(agent2.skillsDir, SKILL_NAME);
|
|
1285
1355
|
const previous = installedVersion(dir);
|
|
@@ -1293,11 +1363,154 @@ function installSkill(agent2, opts = {}) {
|
|
|
1293
1363
|
return { agent: agent2.id, path: dir, outcome };
|
|
1294
1364
|
}
|
|
1295
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
|
+
|
|
1296
1509
|
// src/subscriptions.ts
|
|
1297
1510
|
import { spawn as spawn3 } from "child_process";
|
|
1298
|
-
import { existsSync as
|
|
1299
|
-
import { homedir as
|
|
1300
|
-
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";
|
|
1301
1514
|
import { createInterface as createInterface2 } from "readline";
|
|
1302
1515
|
var ALIASES = {
|
|
1303
1516
|
claude: "anthropic",
|
|
@@ -1382,20 +1595,35 @@ async function connectClaude() {
|
|
|
1382
1595
|
const expiry = sub.expiresAt ? ` (expires ~${sub.expiresAt.slice(0, 10)})` : "";
|
|
1383
1596
|
console.log(`Connected Claude subscription${expiry}. Powers new eligible agents.`);
|
|
1384
1597
|
}
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
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)) {
|
|
1389
1601
|
throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
|
|
1390
1602
|
}
|
|
1391
|
-
const raw =
|
|
1392
|
-
let accountId;
|
|
1603
|
+
const raw = readFileSync8(path, "utf8");
|
|
1393
1604
|
try {
|
|
1394
1605
|
const parsed = JSON.parse(raw);
|
|
1395
|
-
|
|
1606
|
+
return { path, raw, account: parsed.tokens?.account_id };
|
|
1396
1607
|
} catch {
|
|
1397
1608
|
throw new ApiError(400, "invalid_codex_auth", `${path} isn't valid JSON \u2014 re-run \`codex login\``);
|
|
1398
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();
|
|
1399
1627
|
console.log(`Found Codex sign-in at ${path}${accountId ? ` (account ${accountId})` : ""}.`);
|
|
1400
1628
|
if (!opts.yes) {
|
|
1401
1629
|
const ok = await confirm2("Upload it so your cloud Codex agents run on your ChatGPT plan?");
|
|
@@ -1452,6 +1680,292 @@ async function disconnect(name, opts) {
|
|
|
1452
1680
|
}
|
|
1453
1681
|
}
|
|
1454
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
|
+
|
|
1455
1969
|
// src/index.ts
|
|
1456
1970
|
function printModelBilling(session2) {
|
|
1457
1971
|
if (session2.modelBilling !== "account") return;
|
|
@@ -1647,10 +2161,10 @@ session.command("create <agent>").description("create a session (starts provisio
|
|
|
1647
2161
|
console.log(JSON.stringify(created));
|
|
1648
2162
|
return;
|
|
1649
2163
|
}
|
|
1650
|
-
console.log(`Created ${created.id} (${created.
|
|
2164
|
+
console.log(`Created ${created.id} (${created.agentId ?? "-"} \xB7 ${created.status})${created.default ? " [default]" : ""}`);
|
|
1651
2165
|
printModelBilling(created);
|
|
1652
2166
|
for (const r of created.resources ?? []) console.log(` ${renderResource(r)}`);
|
|
1653
|
-
console.log(` sky
|
|
2167
|
+
console.log(` sky session message ${created.id} "hello"`);
|
|
1654
2168
|
}
|
|
1655
2169
|
)
|
|
1656
2170
|
);
|
|
@@ -1664,7 +2178,7 @@ session.command("list [agent]").action(
|
|
|
1664
2178
|
}
|
|
1665
2179
|
for (const s of sessions) {
|
|
1666
2180
|
console.log(
|
|
1667
|
-
`${s.id.padEnd(24)} ${(s.
|
|
2181
|
+
`${s.id.padEnd(24)} ${(s.agentId ?? "-").padEnd(20)} ${(s.title ?? "").padEnd(16)} ${s.status}${s.default ? " *" : ""}`
|
|
1668
2182
|
);
|
|
1669
2183
|
}
|
|
1670
2184
|
})
|
|
@@ -1676,7 +2190,7 @@ session.command("show <id>").action(
|
|
|
1676
2190
|
`/sessions/${encodeURIComponent(id)}`
|
|
1677
2191
|
);
|
|
1678
2192
|
console.log(`${s.id}${s.default ? " [default]" : ""}`);
|
|
1679
|
-
console.log(` agent ${s.
|
|
2193
|
+
console.log(` agent ${s.agentId ?? "-"}`);
|
|
1680
2194
|
console.log(` title ${s.title ?? "-"}`);
|
|
1681
2195
|
console.log(` status ${s.status}`);
|
|
1682
2196
|
console.log(` model ${s.agentType} \xB7 ${s.llm}`);
|
|
@@ -1733,7 +2247,7 @@ session.command("read <ref>").description("what the agent has said since the las
|
|
|
1733
2247
|
console.log(JSON.stringify(reading));
|
|
1734
2248
|
return;
|
|
1735
2249
|
}
|
|
1736
|
-
for (const
|
|
2250
|
+
for (const message2 of reading.messages) console.log(message2);
|
|
1737
2251
|
for (const error of reading.errors) console.error(`error: ${error}`);
|
|
1738
2252
|
if (!reading.finished) console.error("The agent is still working. Run this again to read more.");
|
|
1739
2253
|
})
|
|
@@ -1858,7 +2372,7 @@ async function sendAndFollow(sessionId, text, opts) {
|
|
|
1858
2372
|
async function chatLoop(sessionId, opts = {}) {
|
|
1859
2373
|
console.log(`Chatting with ${sessionId} \u2014 Ctrl-C to leave.`);
|
|
1860
2374
|
const controller = new AbortController();
|
|
1861
|
-
const rl =
|
|
2375
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout, prompt: "> " });
|
|
1862
2376
|
let system = opts.system;
|
|
1863
2377
|
void (async () => {
|
|
1864
2378
|
try {
|
|
@@ -1892,12 +2406,10 @@ async function chatLoop(sessionId, opts = {}) {
|
|
|
1892
2406
|
await new Promise(() => {
|
|
1893
2407
|
});
|
|
1894
2408
|
}
|
|
1895
|
-
program.command("catalog").description("the agents that can be started by handle")
|
|
2409
|
+
var catalog = program.command("catalog").description("the agents that can be started by handle");
|
|
2410
|
+
catalog.command("search [words...]").description("find an agent for a piece of work; no words lists every agent; works without logging in").option("--category <category>", "only this category (see `sky catalog categories`)").option("--json", "print the full listings as JSON").action(
|
|
1896
2411
|
run(async (words, opts) => {
|
|
1897
|
-
const
|
|
1898
|
-
if (words.length) query.set("q", words.join(" "));
|
|
1899
|
-
if (opts.category) query.set("category", opts.category);
|
|
1900
|
-
const { agents } = await api("GET", `/catalog/agents${query.size ? `?${query}` : ""}`);
|
|
2412
|
+
const agents = await listCatalog({ text: words.join(" "), category: opts.category });
|
|
1901
2413
|
if (opts.json) {
|
|
1902
2414
|
console.log(JSON.stringify(agents));
|
|
1903
2415
|
return;
|
|
@@ -1926,6 +2438,20 @@ program.command("skill").description("the AgentSky skill for the agents on this
|
|
|
1926
2438
|
for (const i of installs) console.log(`${i.agent.padEnd(12)} ${verb[i.outcome].padEnd(14)} ${i.path}`);
|
|
1927
2439
|
})
|
|
1928
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
|
+
);
|
|
1929
2455
|
var secrets = program.command("secrets").description("agent secrets (values write-only)");
|
|
1930
2456
|
secrets.command("set <id> <pairs...>").description("set KEY=VALUE pairs on the agent").action(
|
|
1931
2457
|
run(async (id, pairs) => {
|
|
@@ -1960,8 +2486,8 @@ secrets.command("unset <id> <key>").action(
|
|
|
1960
2486
|
);
|
|
1961
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(
|
|
1962
2488
|
run(async (opts) => {
|
|
1963
|
-
const path =
|
|
1964
|
-
if (!
|
|
2489
|
+
const path = resolve4(opts.file);
|
|
2490
|
+
if (!existsSync7(path)) {
|
|
1965
2491
|
const name = basename3(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
|
|
1966
2492
|
scaffoldAgentFile(path, name);
|
|
1967
2493
|
console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
|
|
@@ -1993,7 +2519,7 @@ program.command("launch").description("scaffold ./agent.toml if absent, create t
|
|
|
1993
2519
|
);
|
|
1994
2520
|
program.command("deploy").description("re-apply ./agent.toml to the existing agent (idempotent)").option("-f, --file <path>", "agent file", "agent.toml").action(
|
|
1995
2521
|
run(async (opts) => {
|
|
1996
|
-
const path =
|
|
2522
|
+
const path = resolve4(opts.file);
|
|
1997
2523
|
const { file, spec } = readAgentFile(path);
|
|
1998
2524
|
if (!file.id) {
|
|
1999
2525
|
console.log("No agent id in the file yet \u2014 running launch instead.");
|
|
@@ -2070,13 +2596,13 @@ program.command("import").description("import a local Claude Code or Codex sessi
|
|
|
2070
2596
|
console.log(` ${ws}`);
|
|
2071
2597
|
});
|
|
2072
2598
|
console.log();
|
|
2073
|
-
const rl =
|
|
2599
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
2074
2600
|
const answer = await new Promise(
|
|
2075
|
-
(
|
|
2601
|
+
(resolve5) => rl.question(
|
|
2076
2602
|
`Import which session? [1-${candidates.length}, or comma-separated, or "all"]: `,
|
|
2077
2603
|
(a) => {
|
|
2078
2604
|
rl.close();
|
|
2079
|
-
|
|
2605
|
+
resolve5(a.trim());
|
|
2080
2606
|
}
|
|
2081
2607
|
)
|
|
2082
2608
|
);
|
|
@@ -2096,10 +2622,10 @@ program.command("import").description("import a local Claude Code or Codex sessi
|
|
|
2096
2622
|
console.log("The recorded conversation is redacted locally. Local project files and login credentials are not transferred.");
|
|
2097
2623
|
if (!opts.yes) {
|
|
2098
2624
|
if (!process.stdin.isTTY) throw new Error("Use --yes to confirm this transfer in a non-interactive terminal.");
|
|
2099
|
-
const prompt =
|
|
2100
|
-
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) => {
|
|
2101
2627
|
prompt.close();
|
|
2102
|
-
|
|
2628
|
+
resolve5(value);
|
|
2103
2629
|
}));
|
|
2104
2630
|
if (!/^y(?:es)?$/i.test(answer.trim())) {
|
|
2105
2631
|
console.log("Cancelled. Nothing uploaded.");
|
|
@@ -2159,7 +2685,7 @@ files.command("rm <id>").description("delete a file (a session that already rece
|
|
|
2159
2685
|
files.command("download <id>").description("download a session file's bytes").requiredOption("-o, --out <path>", "where to write the bytes").action(
|
|
2160
2686
|
run(async (id, opts) => {
|
|
2161
2687
|
const { bytes } = await downloadFile(id);
|
|
2162
|
-
|
|
2688
|
+
writeFileSync6(opts.out, bytes);
|
|
2163
2689
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
2164
2690
|
})
|
|
2165
2691
|
);
|
|
@@ -2302,6 +2828,30 @@ for (const [verb, method, suffix, blurb] of [
|
|
|
2302
2828
|
})
|
|
2303
2829
|
);
|
|
2304
2830
|
}
|
|
2831
|
+
catalog.command("show <handle>").description("what one agent does, and how to hire it").option("--json", "print the raw listing").action(
|
|
2832
|
+
run(async (handle, opts) => {
|
|
2833
|
+
const agent2 = await showCatalogAgent(handle);
|
|
2834
|
+
console.log(opts.json ? JSON.stringify(agent2, null, 2) : renderCatalogAgent(agent2));
|
|
2835
|
+
})
|
|
2836
|
+
);
|
|
2837
|
+
catalog.command("categories").description("the categories a listing can carry").action(
|
|
2838
|
+
run(async () => {
|
|
2839
|
+
for (const category of await listCategories()) console.log(`${category.id.padEnd(10)} ${category.label}`);
|
|
2840
|
+
})
|
|
2841
|
+
);
|
|
2842
|
+
catalog.command("publish <agent>").description("list one of your agents under a handle, visible to your workspace; publishing again replaces the listing").requiredOption("--handle <handle>", "the @handle people hire it by: 2 to 40 lower-case letters, digits or underscores").requiredOption("--tagline <text>", "one line on what it is for").option("--description <text>", "a longer description").option("--category <id>", "a category; repeat for several", collect).option("--does <text>", "something it does; repeat for several", collect).option("--does-not <text>", "something it does not do; repeat for several", collect).option("--example <text>", "an example request; repeat for several", collect).action(
|
|
2843
|
+
run(async (agentId, opts) => {
|
|
2844
|
+
const agent2 = await publishListing(agentId, opts);
|
|
2845
|
+
console.log(`Listed @${agent2.handle} (${agent2.name})`);
|
|
2846
|
+
console.log(` sky session create @${agent2.handle} --message "\u2026"`);
|
|
2847
|
+
})
|
|
2848
|
+
);
|
|
2849
|
+
catalog.command("unpublish <handle>").description("take down one of your workspace's listings; the agent itself is untouched").action(
|
|
2850
|
+
run(async (handle) => {
|
|
2851
|
+
await removeListing(handle);
|
|
2852
|
+
console.log(`Removed @${handle.replace(/^@/, "")} from the catalog`);
|
|
2853
|
+
})
|
|
2854
|
+
);
|
|
2305
2855
|
program.command("config").description("show the CLI config location and current values").action(() => {
|
|
2306
2856
|
const config = loadConfig();
|
|
2307
2857
|
console.log(`config: ${configPath()}`);
|
package/dist/products.js
CHANGED