@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-MHL5Z6IQ.js";
19
+ } from "./chunk-IC4N2ZPL.js";
20
20
 
21
21
  // src/index.ts
22
- import { existsSync as existsSync6, writeFileSync as writeFileSync5 } from "fs";
23
- import { basename as basename3, resolve as resolve3 } from "path";
24
- import { createInterface as createInterface3 } from "readline";
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.6.0";
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, message) {
73
- super(message);
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 message = `${res.status} ${res.statusText}`;
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
- message = body.error.message;
97
+ message2 = body.error.message;
98
98
  }
99
99
  } catch {
100
100
  }
101
- throw new ApiError(res.status, code, message);
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-GLBJHVIJ.js");
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((resolve4, reject) => {
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
- resolve4(got);
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 message = String(error?.message ?? error?.type ?? "unknown");
1192
- if (error?.retry_status === "retrying") console.error(dim(`retrying: ${message}`));
1193
- else console.error(red(`error: ${message}`));
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((resolve4) => setTimeout(resolve4, opts.intervalMs ?? 3e3));
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 existsSync5, readFileSync as readFileSync7 } from "fs";
1355
- import { homedir as homedir5 } from "os";
1356
- import { join as join6 } from "path";
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
- async function connectChatgpt(opts) {
1442
- const codexHome = process.env.CODEX_HOME || join6(homedir5(), ".codex");
1443
- const path = join6(codexHome, "auth.json");
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 = readFileSync7(path, "utf8");
1448
- let accountId;
1603
+ const raw = readFileSync8(path, "utf8");
1449
1604
  try {
1450
1605
  const parsed = JSON.parse(raw);
1451
- accountId = parsed.tokens?.account_id;
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 message of reading.messages) console.log(message);
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 = createInterface3({ input: process.stdin, output: process.stdout, prompt: "> " });
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 = resolve3(opts.file);
2018
- if (!existsSync6(path)) {
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 = resolve3(opts.file);
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 = createInterface3({ input: process.stdin, output: process.stdout });
2599
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
2128
2600
  const answer = await new Promise(
2129
- (resolve4) => rl.question(
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
- resolve4(a.trim());
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 = createInterface3({ input: process.stdin, output: process.stdout });
2154
- const answer = await new Promise((resolve4) => prompt.question("Upload these selected conversations? [y/N] ", (value) => {
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
- resolve4(value);
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
- writeFileSync5(opts.out, bytes);
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
@@ -8,7 +8,7 @@ import {
8
8
  skillsArraySchema,
9
9
  systemMessageEventSchema,
10
10
  userMessageEventSchema
11
- } from "./chunk-MHL5Z6IQ.js";
11
+ } from "./chunk-IC4N2ZPL.js";
12
12
 
13
13
  // ../../packages/spec/src/compatibility.ts
14
14
  var capabilityInputsSchema = capabilitiesSchema;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  external_exports,
3
3
  messagePartSchema
4
- } from "./chunk-MHL5Z6IQ.js";
4
+ } from "./chunk-IC4N2ZPL.js";
5
5
 
6
6
  // ../../packages/chat/src/stream.ts
7
7
  async function* parseSseStream(body) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentsky/sky",
3
- "version": "2.6.0",
3
+ "version": "2.7.1",
4
4
  "description": "sky — the AgentSky developer CLI: launch and talk to long-lived agents from your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",