@agentsky/sky 2.23.0 → 2.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HDPDY77W.js → chunk-277AZSSG.js} +52 -31
- package/dist/{chunk-V5XLV37X.js → chunk-RAFHOB5S.js} +1 -1
- package/dist/{chunk-HQPOCQRQ.js → chunk-UHMKEYJN.js} +18 -3
- package/dist/index.js +445 -127
- package/dist/{src-UMT6FHLG.js → src-FB5IETJP.js} +1 -1
- package/dist/{trace-ZRU624EY.js → trace-CFHCOGKB.js} +2 -2
- package/dist/{trace-library-QOQUAL5H.js → trace-library-2VHTJCOY.js} +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,12 +4,13 @@ import {
|
|
|
4
4
|
api,
|
|
5
5
|
apiBytes,
|
|
6
6
|
apiForm,
|
|
7
|
+
apiRaw,
|
|
7
8
|
downloadFileTo,
|
|
8
9
|
postEvents,
|
|
9
10
|
resolveSessionRef,
|
|
10
11
|
streamEvents,
|
|
11
12
|
uploadFile
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UHMKEYJN.js";
|
|
13
14
|
import {
|
|
14
15
|
CLI_VERSION,
|
|
15
16
|
USER_AGENT,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
currentUniverse,
|
|
21
22
|
loadConfig,
|
|
22
23
|
saveConfig
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-RAFHOB5S.js";
|
|
24
25
|
import {
|
|
25
26
|
AGENT_TYPES,
|
|
26
27
|
MODEL_SUBSCRIPTION_PLAN_PHRASE,
|
|
@@ -42,17 +43,17 @@ import {
|
|
|
42
43
|
skillRefSchema,
|
|
43
44
|
splitMcpServers,
|
|
44
45
|
supersededBy
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-277AZSSG.js";
|
|
46
47
|
import "./chunk-MLKGABMK.js";
|
|
47
48
|
|
|
48
49
|
// src/index.ts
|
|
49
|
-
import { existsSync as
|
|
50
|
+
import { existsSync as existsSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
|
|
50
51
|
import { basename as basename9, resolve as resolve9 } from "path";
|
|
51
52
|
import { createInterface as createInterface6 } from "readline";
|
|
52
53
|
import { Command } from "commander";
|
|
53
54
|
|
|
54
55
|
// src/files.ts
|
|
55
|
-
import { readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
56
|
+
import { existsSync, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
56
57
|
import { basename as basename2, extname, join as join2 } from "path";
|
|
57
58
|
|
|
58
59
|
// src/resumable-upload.ts
|
|
@@ -250,6 +251,16 @@ function eventsFor(text, files2, system, interactionMode2) {
|
|
|
250
251
|
if (system?.trim()) events.push({ type: "system.message", content: [{ type: "text", text: system }] });
|
|
251
252
|
return events;
|
|
252
253
|
}
|
|
254
|
+
function isFileId(ref) {
|
|
255
|
+
return /^file_[A-Za-z0-9]+$/.test(ref);
|
|
256
|
+
}
|
|
257
|
+
async function attachment(ref, opts = {}) {
|
|
258
|
+
if (opts.byId || isFileId(ref) && !existsSync(ref)) {
|
|
259
|
+
if (!isFileId(ref)) throw new Error(`--file-id takes a stored file's id (file_\u2026), got ${JSON.stringify(ref)}`);
|
|
260
|
+
return { file: await api("GET", `/files/${encodeURIComponent(ref)}`), uploaded: false };
|
|
261
|
+
}
|
|
262
|
+
return { file: await uploadLocalFile(ref), uploaded: true };
|
|
263
|
+
}
|
|
253
264
|
|
|
254
265
|
// src/goal.ts
|
|
255
266
|
var CHECK_SEPARATOR = " :: ";
|
|
@@ -494,9 +505,9 @@ function renderContextPage(page) {
|
|
|
494
505
|
}
|
|
495
506
|
|
|
496
507
|
// src/agent-file.ts
|
|
497
|
-
import { existsSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
508
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
498
509
|
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
499
|
-
import { parse as parseToml
|
|
510
|
+
import { parse as parseToml } from "smol-toml";
|
|
500
511
|
var fileInstructionSchema = external_exports.object({
|
|
501
512
|
name: external_exports.string(),
|
|
502
513
|
content: external_exports.string().optional(),
|
|
@@ -512,7 +523,8 @@ var agentFileSchema = external_exports.object({
|
|
|
512
523
|
type: external_exports.enum(AGENT_TYPES).optional(),
|
|
513
524
|
llm: external_exports.string().optional(),
|
|
514
525
|
reasoning_effort: external_exports.enum(REASONING_EFFORT_VALUES).optional(),
|
|
515
|
-
capabilities: capabilitiesSchema.optional(),
|
|
526
|
+
capabilities: capabilitiesSchema.removeDefault().optional(),
|
|
527
|
+
// omitted = leave the agent's as they are
|
|
516
528
|
prompt: external_exports.string().optional(),
|
|
517
529
|
prompt_file: external_exports.string().optional(),
|
|
518
530
|
instructions: external_exports.array(fileInstructionSchema).optional(),
|
|
@@ -569,13 +581,40 @@ function readAgentFile(path) {
|
|
|
569
581
|
return { file, spec };
|
|
570
582
|
}
|
|
571
583
|
function stampId(path, id) {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
584
|
+
writeFileSync2(path, stampIdText(readFileSync3(path, "utf8"), id));
|
|
585
|
+
}
|
|
586
|
+
function stampIdText(text, id) {
|
|
587
|
+
const out = [];
|
|
588
|
+
let topLevel = true;
|
|
589
|
+
let openString = null;
|
|
590
|
+
let stamped = false;
|
|
591
|
+
let nameAt = -1;
|
|
592
|
+
for (const line of text.split("\n")) {
|
|
593
|
+
if (openString) {
|
|
594
|
+
if (line.includes(openString)) openString = null;
|
|
595
|
+
out.push(line);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
if (/^\s*\[/.test(line)) topLevel = false;
|
|
599
|
+
if (topLevel && /^\s*(id|slug)\s*=/.test(line)) {
|
|
600
|
+
if (/^\s*id\s*=/.test(line) && !stamped) {
|
|
601
|
+
out.push(`id = ${JSON.stringify(id)}`);
|
|
602
|
+
stamped = true;
|
|
603
|
+
}
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (topLevel && nameAt < 0 && /^\s*name\s*=/.test(line)) nameAt = out.length;
|
|
607
|
+
const opener = /=\s*("""|''')/.exec(line);
|
|
608
|
+
if (opener && !line.slice(opener.index + opener[0].length).includes(opener[1])) openString = opener[1];
|
|
609
|
+
out.push(line);
|
|
610
|
+
}
|
|
611
|
+
if (!stamped) out.splice(nameAt + 1, 0, `id = ${JSON.stringify(id)}`);
|
|
612
|
+
const next = out.join("\n");
|
|
613
|
+
if (parseToml(next).id !== id) throw new Error("could not stamp the agent id into the file");
|
|
614
|
+
return next;
|
|
576
615
|
}
|
|
577
616
|
function scaffoldAgentFile(path, name) {
|
|
578
|
-
if (
|
|
617
|
+
if (existsSync2(path)) throw new Error(`${path} already exists`);
|
|
579
618
|
writeFileSync2(
|
|
580
619
|
path,
|
|
581
620
|
`# agent.toml \u2014 everything the create wizard can set. Only \`name\` is required.
|
|
@@ -596,6 +635,38 @@ You are ${name}, a helpful long-lived agent.
|
|
|
596
635
|
`
|
|
597
636
|
);
|
|
598
637
|
}
|
|
638
|
+
function deployPatch(spec) {
|
|
639
|
+
return {
|
|
640
|
+
displayName: spec.displayName,
|
|
641
|
+
...spec.agentType ? { agentType: spec.agentType } : {},
|
|
642
|
+
...spec.llm ? { llm: spec.llm } : {},
|
|
643
|
+
...spec.capabilities ? { capabilities: spec.capabilities } : {},
|
|
644
|
+
...spec.reasoningEffort ? { reasoningEffort: spec.reasoningEffort } : {},
|
|
645
|
+
...spec.skills ? { skills: spec.skills } : {},
|
|
646
|
+
...spec.hiringPolicy ? { hiringPolicy: spec.hiringPolicy } : {},
|
|
647
|
+
...spec.environmentId ? { environmentId: spec.environmentId } : {}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function deployReport(id, spec, before, after, promptChanged) {
|
|
651
|
+
if (spec.agentType && after.agentType !== spec.agentType) {
|
|
652
|
+
throw new Error(`${id} is still ${after.agentType}: the API did not apply type = "${spec.agentType}"`);
|
|
653
|
+
}
|
|
654
|
+
if (spec.llm && after.llm !== spec.llm) {
|
|
655
|
+
throw new Error(`${id} still runs ${after.llm}: the API did not apply llm = "${spec.llm}"`);
|
|
656
|
+
}
|
|
657
|
+
const changed = [];
|
|
658
|
+
if (promptChanged) changed.push("prompt");
|
|
659
|
+
if (after.version !== before.version) changed.push("settings");
|
|
660
|
+
if (!changed.length) return [`${id} is up to date \u2014 nothing to deploy (version ${after.version})`];
|
|
661
|
+
const lines = [`Deployed ${id}: ${changed.join(" and ")} updated (version ${before.version} \u2192 ${after.version})`];
|
|
662
|
+
if (after.agentType !== before.agentType || after.llm !== before.llm) {
|
|
663
|
+
lines.push(` now ${after.agentType} \xB7 ${after.llm} for new sessions (was ${before.agentType} \xB7 ${before.llm})`);
|
|
664
|
+
}
|
|
665
|
+
if (spec.skills && after.version !== before.version) {
|
|
666
|
+
lines.push(` Skills apply to new sessions; running sessions keep the skills they started with \u2014 start one with \`sky session create ${id}\`.`);
|
|
667
|
+
}
|
|
668
|
+
return lines;
|
|
669
|
+
}
|
|
599
670
|
|
|
600
671
|
// src/catalog.ts
|
|
601
672
|
var bare = (handle) => handle.replace(/^@/, "");
|
|
@@ -662,7 +733,7 @@ function renderCatalogAgent(agent2) {
|
|
|
662
733
|
|
|
663
734
|
// src/clone.ts
|
|
664
735
|
import { spawn } from "child_process";
|
|
665
|
-
import { existsSync as
|
|
736
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
666
737
|
import { homedir } from "os";
|
|
667
738
|
import { basename as basename3, join as join3 } from "path";
|
|
668
739
|
import { createInterface } from "readline";
|
|
@@ -768,9 +839,9 @@ function detectHarness(explicit) {
|
|
|
768
839
|
}
|
|
769
840
|
return explicit;
|
|
770
841
|
}
|
|
771
|
-
if (
|
|
772
|
-
if (
|
|
773
|
-
if (
|
|
842
|
+
if (existsSync3(claudeConfigRoot())) return "claude_code";
|
|
843
|
+
if (existsSync3(join3(homedir(), ".codex"))) return "codex";
|
|
844
|
+
if (existsSync3(join3(homedir(), ".openclaw"))) return "openclaw";
|
|
774
845
|
throw new Error(
|
|
775
846
|
"no local harness found (~/.claude, ~/.codex, ~/.openclaw) \u2014 pass --harness to override"
|
|
776
847
|
);
|
|
@@ -1028,20 +1099,20 @@ async function clone(opts) {
|
|
|
1028
1099
|
}
|
|
1029
1100
|
|
|
1030
1101
|
// src/import-local.ts
|
|
1031
|
-
import { closeSync as closeSync3, existsSync as
|
|
1102
|
+
import { closeSync as closeSync3, existsSync as existsSync6, openSync as openSync3, readdirSync as readdirSync4, readFileSync as readFileSync8, readSync as readSync3, statSync as statSync5 } from "fs";
|
|
1032
1103
|
import { homedir as homedir4 } from "os";
|
|
1033
1104
|
import { join as join7 } from "path";
|
|
1034
1105
|
import { createHash as createHash3 } from "crypto";
|
|
1035
1106
|
|
|
1036
1107
|
// src/import-files.ts
|
|
1037
1108
|
import { isUtf8 } from "buffer";
|
|
1038
|
-
import { existsSync as
|
|
1109
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
1039
1110
|
import { homedir as homedir3 } from "os";
|
|
1040
1111
|
import { dirname as dirname4, extname as extname2, isAbsolute, join as join5, relative as relative2, resolve as resolve4, sep as sep2, basename as basename5 } from "path";
|
|
1041
1112
|
|
|
1042
1113
|
// src/import-workspace.ts
|
|
1043
1114
|
import { spawnSync } from "child_process";
|
|
1044
|
-
import { existsSync as
|
|
1115
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, realpathSync, statSync as statSync3 } from "fs";
|
|
1045
1116
|
import { homedir as homedir2 } from "os";
|
|
1046
1117
|
import { basename as basename4, dirname as dirname3, join as join4, relative, sep } from "path";
|
|
1047
1118
|
import { parse as parseToml2 } from "smol-toml";
|
|
@@ -1270,7 +1341,7 @@ function skillFiles(skill) {
|
|
|
1270
1341
|
else if (entry2.isFile()) out.push({ path, name: `skills/${skill.name}/${relative(skill.dir, path).split(sep).join("/")}` });
|
|
1271
1342
|
}
|
|
1272
1343
|
};
|
|
1273
|
-
if (
|
|
1344
|
+
if (existsSync4(skill.dir)) walk(skill.dir, 0);
|
|
1274
1345
|
return out;
|
|
1275
1346
|
}
|
|
1276
1347
|
function codexThreadName(sessionId, home = homedir2()) {
|
|
@@ -1396,13 +1467,13 @@ function touchedPaths(records, cwd, home = homedir3()) {
|
|
|
1396
1467
|
}
|
|
1397
1468
|
function gitRoot(dir) {
|
|
1398
1469
|
for (let d = dir; ; d = dirname4(d)) {
|
|
1399
|
-
if (
|
|
1470
|
+
if (existsSync5(join5(d, ".git"))) return d;
|
|
1400
1471
|
if (d === dirname4(d)) return null;
|
|
1401
1472
|
}
|
|
1402
1473
|
}
|
|
1403
1474
|
function instructionPaths(cwd, harness2, home = homedir3()) {
|
|
1404
1475
|
const out = [];
|
|
1405
|
-
if (cwd &&
|
|
1476
|
+
if (cwd && existsSync5(cwd)) {
|
|
1406
1477
|
const root = gitRoot(cwd) ?? cwd;
|
|
1407
1478
|
for (let d = resolve4(cwd); ; d = dirname4(d)) {
|
|
1408
1479
|
for (const name of INSTRUCTION_FILES) out.push(join5(d, name));
|
|
@@ -1925,7 +1996,7 @@ function sniffTitle(filePath) {
|
|
|
1925
1996
|
function listClaudeCodeSessions(limit) {
|
|
1926
1997
|
const root = process.env.CLAUDE_CONFIG_DIR ?? join7(homedir4(), ".claude");
|
|
1927
1998
|
const projectsDir = join7(root, "projects");
|
|
1928
|
-
if (!
|
|
1999
|
+
if (!existsSync6(projectsDir)) return [];
|
|
1929
2000
|
const sessions = [];
|
|
1930
2001
|
try {
|
|
1931
2002
|
for (const encoded of readdirSync4(projectsDir)) {
|
|
@@ -1951,7 +2022,7 @@ function listClaudeCodeSessions(limit) {
|
|
|
1951
2022
|
let discoveryError;
|
|
1952
2023
|
const subagentFiles = [];
|
|
1953
2024
|
const subagentDir = join7(projectDir, sessionId, "subagents");
|
|
1954
|
-
if (
|
|
2025
|
+
if (existsSync6(subagentDir)) {
|
|
1955
2026
|
try {
|
|
1956
2027
|
const subFiles = readdirSync4(subagentDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
1957
2028
|
for (const f of subFiles) {
|
|
@@ -2229,7 +2300,7 @@ async function importSession(opts) {
|
|
|
2229
2300
|
}
|
|
2230
2301
|
const result = await response.json();
|
|
2231
2302
|
const finalTraceId = result.traceId ?? traceId;
|
|
2232
|
-
const environment = opts.files === false || opts.environment === false || !cwd || !
|
|
2303
|
+
const environment = opts.files === false || opts.environment === false || !cwd || !existsSync6(cwd) ? null : await uploadEnvironment(base, key, finalTraceId, cwd, resourceLines, opts.onProgress).catch((error) => ({ error: error instanceof Error ? error.message : String(error) }));
|
|
2233
2304
|
return {
|
|
2234
2305
|
traceId: finalTraceId,
|
|
2235
2306
|
url: `${base}/playground/new?import=${encodeURIComponent(finalTraceId)}`,
|
|
@@ -2342,7 +2413,7 @@ import { hostname } from "os";
|
|
|
2342
2413
|
import { spawn as spawn2 } from "child_process";
|
|
2343
2414
|
|
|
2344
2415
|
// src/mcp-register.ts
|
|
2345
|
-
import { existsSync as
|
|
2416
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
2346
2417
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
2347
2418
|
import { homedir as homedir6 } from "os";
|
|
2348
2419
|
import { join as join10 } from "path";
|
|
@@ -2350,7 +2421,7 @@ import { parse as parseToml3 } from "smol-toml";
|
|
|
2350
2421
|
import { isMap, parseDocument } from "yaml";
|
|
2351
2422
|
|
|
2352
2423
|
// src/local-agents.ts
|
|
2353
|
-
import { existsSync as
|
|
2424
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2354
2425
|
import { homedir as homedir5 } from "os";
|
|
2355
2426
|
import { join as join8 } from "path";
|
|
2356
2427
|
function localAgents(env = process.env, home = homedir5()) {
|
|
@@ -2364,7 +2435,7 @@ function localAgents(env = process.env, home = homedir5()) {
|
|
|
2364
2435
|
];
|
|
2365
2436
|
}
|
|
2366
2437
|
function detectLocalAgents(env = process.env, home = homedir5()) {
|
|
2367
|
-
return localAgents(env, home).filter((agent2) =>
|
|
2438
|
+
return localAgents(env, home).filter((agent2) => existsSync7(agent2.root));
|
|
2368
2439
|
}
|
|
2369
2440
|
function selectLocalAgents(ids, env = process.env, home = homedir5()) {
|
|
2370
2441
|
if (!ids?.length) return detectLocalAgents(env, home);
|
|
@@ -2487,6 +2558,12 @@ Details: \`${REFERENCES_DIR}/workspaces.md\` in this skill's folder.
|
|
|
2487
2558
|
cheaper or different agent to get around a refusal.
|
|
2488
2559
|
- \`get_usage\` shows the balance; check it before starting several cloud sessions
|
|
2489
2560
|
at once so the person is told once, up front, instead of after the first fails.
|
|
2561
|
+
- Anything else the person can do on agentsky.dev or in the Mac app (rename or
|
|
2562
|
+
archive a thread, edit a Library file or skill, change an agent's model, invite
|
|
2563
|
+
a member, \u2026) is an API operation: find it with \`search_api\`, read its
|
|
2564
|
+
parameters with \`describe_api\`, and run it with \`call_api\`. It acts as the
|
|
2565
|
+
person with their own key, so make only the change they asked for. Where MCP
|
|
2566
|
+
is missing, \`sky api search <words>\` and \`sky api <METHOD> <path>\` do the same.
|
|
2490
2567
|
|
|
2491
2568
|
## How to pick
|
|
2492
2569
|
|
|
@@ -2874,7 +2951,7 @@ function installSkill(agent2, opts = {}) {
|
|
|
2874
2951
|
var MCP_SERVER_NAME = SKILL_NAME;
|
|
2875
2952
|
function detectHermes(env = process.env, home = homedir6()) {
|
|
2876
2953
|
const root = env.HERMES_HOME || join10(home, ".hermes");
|
|
2877
|
-
return
|
|
2954
|
+
return existsSync8(root) ? { id: "hermes", name: "Hermes", root } : null;
|
|
2878
2955
|
}
|
|
2879
2956
|
function detectMcpClients(env = process.env, home = homedir6()) {
|
|
2880
2957
|
const hermes = detectHermes(env, home);
|
|
@@ -3034,7 +3111,7 @@ function refreshMcpRegistrations(opts, clients = detectMcpClients(), runner = sy
|
|
|
3034
3111
|
}
|
|
3035
3112
|
|
|
3036
3113
|
// src/shell-env.ts
|
|
3037
|
-
import { appendFileSync, existsSync as
|
|
3114
|
+
import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
3038
3115
|
import { homedir as homedir7 } from "os";
|
|
3039
3116
|
import { basename as basename6, dirname as dirname5, join as join11 } from "path";
|
|
3040
3117
|
var TOKEN_ENV = "AGENTSKY_API_KEY";
|
|
@@ -3050,13 +3127,13 @@ function shellStartupFiles(env = process.env, home = homedir7(), platform = proc
|
|
|
3050
3127
|
const shell = basename6(env.SHELL ?? "");
|
|
3051
3128
|
const files2 = [];
|
|
3052
3129
|
const zshenv = join11(env.ZDOTDIR || home, ".zshenv");
|
|
3053
|
-
if (shell === "zsh" || platform === "darwin" ||
|
|
3130
|
+
if (shell === "zsh" || platform === "darwin" || existsSync9(zshenv)) files2.push({ path: zshenv, kind: "sh" });
|
|
3054
3131
|
const bashrc = join11(home, ".bashrc");
|
|
3055
|
-
if (shell === "bash" ||
|
|
3132
|
+
if (shell === "bash" || existsSync9(bashrc)) files2.push({ path: bashrc, kind: "sh" });
|
|
3056
3133
|
const bashProfile = join11(home, ".bash_profile");
|
|
3057
|
-
if (
|
|
3134
|
+
if (existsSync9(bashProfile)) files2.push({ path: bashProfile, kind: "sh" });
|
|
3058
3135
|
const fish = join11(env.XDG_CONFIG_HOME || join11(home, ".config"), "fish");
|
|
3059
|
-
if (shell === "fish" ||
|
|
3136
|
+
if (shell === "fish" || existsSync9(fish)) files2.push({ path: join11(fish, "conf.d", "agentsky.fish"), kind: "fish" });
|
|
3060
3137
|
return files2;
|
|
3061
3138
|
}
|
|
3062
3139
|
function writeShellEnv(token, opts = {}) {
|
|
@@ -3072,7 +3149,7 @@ function writeShellEnv(token, opts = {}) {
|
|
|
3072
3149
|
writeFileSync6(fish, `${header2}${token ? `set -gx ${TOKEN_ENV} ${quoted(token)}` : `set -e ${TOKEN_ENV}`}
|
|
3073
3150
|
`, { mode: 384 });
|
|
3074
3151
|
for (const file of startup) {
|
|
3075
|
-
const existing =
|
|
3152
|
+
const existing = existsSync9(file.path) ? readFileSync11(file.path, "utf8") : "";
|
|
3076
3153
|
if (existing.includes(HOOK_MARK)) continue;
|
|
3077
3154
|
const line = file.kind === "fish" ? `${HOOK_MARK}
|
|
3078
3155
|
test -f ${quoted(fish)}; and source ${quoted(fish)}
|
|
@@ -3443,12 +3520,42 @@ function clip(value, max = 120) {
|
|
|
3443
3520
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
3444
3521
|
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
3445
3522
|
}
|
|
3446
|
-
function
|
|
3523
|
+
function createEventRenderer(out = process.stdout) {
|
|
3524
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3525
|
+
let openStream = null;
|
|
3526
|
+
const closeLine = () => {
|
|
3527
|
+
if (openStream === null) return;
|
|
3528
|
+
out.write("\n");
|
|
3529
|
+
openStream = null;
|
|
3530
|
+
};
|
|
3531
|
+
return (event, data) => {
|
|
3532
|
+
const id = typeof data.id === "string" ? data.id : void 0;
|
|
3533
|
+
if (id) {
|
|
3534
|
+
if (seen.has(id)) return;
|
|
3535
|
+
seen.add(id);
|
|
3536
|
+
}
|
|
3537
|
+
if (event !== "agent.message") {
|
|
3538
|
+
if (event && event !== "keepalive") closeLine();
|
|
3539
|
+
renderOther(event, data);
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
const parts = Array.isArray(data.parts) ? data.parts : [];
|
|
3543
|
+
const streamId = typeof parts[0]?.stream_id === "string" ? parts[0].stream_id : null;
|
|
3544
|
+
const text = String(data.text ?? "");
|
|
3545
|
+
if (streamId === null) {
|
|
3546
|
+
closeLine();
|
|
3547
|
+
out.write(`${text}
|
|
3548
|
+
`);
|
|
3549
|
+
} else {
|
|
3550
|
+
if (openStream !== streamId) closeLine();
|
|
3551
|
+
out.write(text);
|
|
3552
|
+
openStream = streamId;
|
|
3553
|
+
}
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
function renderOther(event, data) {
|
|
3447
3557
|
const part = data.part;
|
|
3448
3558
|
switch (event) {
|
|
3449
|
-
case "agent.message":
|
|
3450
|
-
console.log(String(data.text ?? ""));
|
|
3451
|
-
break;
|
|
3452
3559
|
case "agent.reasoning":
|
|
3453
3560
|
console.log(dim(`\xB7 ${clip(part?.text ?? "")}`));
|
|
3454
3561
|
break;
|
|
@@ -3480,11 +3587,12 @@ function renderEvent(event, data) {
|
|
|
3480
3587
|
}
|
|
3481
3588
|
|
|
3482
3589
|
// src/session-read.ts
|
|
3483
|
-
var
|
|
3590
|
+
var TURN_STARTS = /* @__PURE__ */ new Set(["user.message", "goal.defined", "goal.iteration"]);
|
|
3591
|
+
var TURN_TYPES = [...TURN_STARTS, "agent.message", "session.status_idle", "session.status_terminated", "session.error"];
|
|
3484
3592
|
function latestTurn(events) {
|
|
3485
3593
|
let start = 0;
|
|
3486
3594
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
3487
|
-
if (events[index].type
|
|
3595
|
+
if (TURN_STARTS.has(events[index].type)) {
|
|
3488
3596
|
start = index + 1;
|
|
3489
3597
|
break;
|
|
3490
3598
|
}
|
|
@@ -3531,7 +3639,7 @@ import { createInterface as createInterface3 } from "readline";
|
|
|
3531
3639
|
|
|
3532
3640
|
// src/setup-local.ts
|
|
3533
3641
|
import { isUtf8 as isUtf83 } from "buffer";
|
|
3534
|
-
import { existsSync as
|
|
3642
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync12, statSync as statSync7 } from "fs";
|
|
3535
3643
|
import { homedir as homedir8 } from "os";
|
|
3536
3644
|
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join13, resolve as resolve6 } from "path";
|
|
3537
3645
|
import { parse as parseToml4 } from "smol-toml";
|
|
@@ -3572,7 +3680,7 @@ function readLocalSkills(agents) {
|
|
|
3572
3680
|
const skills2 = [];
|
|
3573
3681
|
const ignored = [];
|
|
3574
3682
|
for (const agent2 of agents) {
|
|
3575
|
-
if (!
|
|
3683
|
+
if (!existsSync10(agent2.skillsDir)) continue;
|
|
3576
3684
|
for (const entry2 of readdirSync6(agent2.skillsDir, { withFileTypes: true })) {
|
|
3577
3685
|
const dir = join13(agent2.skillsDir, entry2.name);
|
|
3578
3686
|
let isDir2 = entry2.isDirectory();
|
|
@@ -3583,7 +3691,7 @@ function readLocalSkills(agents) {
|
|
|
3583
3691
|
isDir2 = false;
|
|
3584
3692
|
}
|
|
3585
3693
|
}
|
|
3586
|
-
if (!isDir2 || !
|
|
3694
|
+
if (!isDir2 || !existsSync10(join13(dir, "SKILL.md")) || isInstalledSkill(dir)) continue;
|
|
3587
3695
|
const name = frontmatterName(readFileSync12(join13(dir, "SKILL.md"), "utf8"));
|
|
3588
3696
|
if (!name) {
|
|
3589
3697
|
ignored.push({ agent: agent2.id, dir, reason: "SKILL.md has no name" });
|
|
@@ -3690,7 +3798,7 @@ function findLocalSecrets(agents, home = homedir8()) {
|
|
|
3690
3798
|
|
|
3691
3799
|
// src/subscriptions.ts
|
|
3692
3800
|
import { spawn as spawn3 } from "child_process";
|
|
3693
|
-
import { existsSync as
|
|
3801
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";
|
|
3694
3802
|
import { homedir as homedir9 } from "os";
|
|
3695
3803
|
import { join as join14 } from "path";
|
|
3696
3804
|
import { createInterface as createInterface2 } from "readline";
|
|
@@ -3789,7 +3897,7 @@ async function connectClaude() {
|
|
|
3789
3897
|
}
|
|
3790
3898
|
function codexSignIn(env = process.env, home = homedir9()) {
|
|
3791
3899
|
const path = join14(env.CODEX_HOME || join14(home, ".codex"), "auth.json");
|
|
3792
|
-
if (!
|
|
3900
|
+
if (!existsSync11(path)) {
|
|
3793
3901
|
throw new ApiError(404, "no_codex_auth", `no ${path} \u2014 run \`codex login\` first`);
|
|
3794
3902
|
}
|
|
3795
3903
|
const raw = readFileSync13(path, "utf8");
|
|
@@ -3802,7 +3910,7 @@ function codexSignIn(env = process.env, home = homedir9()) {
|
|
|
3802
3910
|
}
|
|
3803
3911
|
function cursorSignIn(env = process.env, home = homedir9()) {
|
|
3804
3912
|
const path = join14(env.XDG_CONFIG_HOME || join14(home, ".config"), "cursor", "auth.json");
|
|
3805
|
-
if (!
|
|
3913
|
+
if (!existsSync11(path)) {
|
|
3806
3914
|
throw new ApiError(404, "no_cursor_auth", `no ${path} \u2014 run \`cursor-agent login\` first`);
|
|
3807
3915
|
}
|
|
3808
3916
|
const raw = readFileSync13(path, "utf8");
|
|
@@ -3924,7 +4032,9 @@ async function subscriptionUsage(name, account) {
|
|
|
3924
4032
|
return usage;
|
|
3925
4033
|
}
|
|
3926
4034
|
function renderSubscriptionUsage(name, usage) {
|
|
3927
|
-
const
|
|
4035
|
+
const provider = managedProviderFor(name);
|
|
4036
|
+
const label = MODEL_SUBSCRIPTION_PROVIDER_NAME[provider];
|
|
4037
|
+
if (usage.status === "unavailable" && (usage.reason === "unsupported" || provider === "anthropic" && usage.reason === "permission")) return [`${label}: usage isn't available for this account type`];
|
|
3928
4038
|
if (usage.status === "unavailable") return [`${label}: usage unavailable (${usage.reason ?? "unknown"})`];
|
|
3929
4039
|
const head = [`${label}${usage.plan ? ` ${usage.plan}` : ""}`, usage.allowed === false ? "limit reached" : null].filter(Boolean).join(" \xB7 ");
|
|
3930
4040
|
const rows = usage.windows.map(
|
|
@@ -4399,12 +4509,25 @@ async function getSkillUsage(since) {
|
|
|
4399
4509
|
const qs = since ? `?since=${encodeURIComponent(since)}` : "";
|
|
4400
4510
|
return api("GET", `/skills/usage${qs}`);
|
|
4401
4511
|
}
|
|
4512
|
+
function nextSkills(current, skillId, verb) {
|
|
4513
|
+
const has = current.some((ref) => ref.type === "skill" && ref.skillId === skillId);
|
|
4514
|
+
if (verb === "attach") return has ? null : [...current, { type: "skill", skillId, version: "latest" }];
|
|
4515
|
+
return has ? current.filter((ref) => !(ref.type === "skill" && ref.skillId === skillId)) : null;
|
|
4516
|
+
}
|
|
4517
|
+
async function setAgentSkill(agentId, skillId, verb) {
|
|
4518
|
+
const path = `/agents/${encodeURIComponent(agentId)}`;
|
|
4519
|
+
const { agent: agent2 } = await api("GET", path);
|
|
4520
|
+
const skills2 = nextSkills(agent2.skills ?? [], skillId, verb);
|
|
4521
|
+
if (!skills2) return "unchanged";
|
|
4522
|
+
await api("PATCH", path, { skills: skills2, expectedVersion: agent2.version });
|
|
4523
|
+
return "changed";
|
|
4524
|
+
}
|
|
4402
4525
|
|
|
4403
4526
|
// src/task-export.ts
|
|
4404
4527
|
import { createHash as createHash6 } from "crypto";
|
|
4405
4528
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync8 } from "fs";
|
|
4406
4529
|
import { dirname as dirname8, join as join17 } from "path";
|
|
4407
|
-
import { stringify as
|
|
4530
|
+
import { stringify as stringifyToml } from "smol-toml";
|
|
4408
4531
|
var TEXT_MAX = 2e4;
|
|
4409
4532
|
function scrub(text, names2) {
|
|
4410
4533
|
let out = redactPlainText(text).text;
|
|
@@ -4582,7 +4705,7 @@ Write your deliverables under /workspace/outputs.
|
|
|
4582
4705
|
`);
|
|
4583
4706
|
out.set(
|
|
4584
4707
|
"task.toml",
|
|
4585
|
-
|
|
4708
|
+
stringifyToml({
|
|
4586
4709
|
metadata: {
|
|
4587
4710
|
name: `agentsky-${idHash}`,
|
|
4588
4711
|
tier: opts.tier,
|
|
@@ -5159,6 +5282,80 @@ function renderLibraryArtifact(artifact2) {
|
|
|
5159
5282
|
return `${artifact2.id} ${artifact2.name} ${version}${artifact2.shared ? " shared" : ""}`;
|
|
5160
5283
|
}
|
|
5161
5284
|
|
|
5285
|
+
// src/api-command.ts
|
|
5286
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
|
|
5287
|
+
var API_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
5288
|
+
function readData(data, stdin = () => readFileSync16(0, "utf8")) {
|
|
5289
|
+
if (data === void 0) return void 0;
|
|
5290
|
+
const raw = data === "-" ? stdin() : data.startsWith("@") ? readFileSync16(data.slice(1), "utf8") : data;
|
|
5291
|
+
try {
|
|
5292
|
+
JSON.parse(raw);
|
|
5293
|
+
} catch {
|
|
5294
|
+
throw new Error("--data must be JSON (inline, @file or - for stdin)");
|
|
5295
|
+
}
|
|
5296
|
+
return raw;
|
|
5297
|
+
}
|
|
5298
|
+
function requestPath(path, query = []) {
|
|
5299
|
+
if (!path.startsWith("/") || path.startsWith("//")) throw new Error("path must start with /, e.g. /threads");
|
|
5300
|
+
const clean = path.replace(/^\/v1(?=\/)/, "");
|
|
5301
|
+
const params = new URLSearchParams();
|
|
5302
|
+
for (const pair of query) {
|
|
5303
|
+
const at = pair.indexOf("=");
|
|
5304
|
+
if (at <= 0) throw new Error(`--query needs key=value, got "${pair}"`);
|
|
5305
|
+
params.append(pair.slice(0, at), pair.slice(at + 1));
|
|
5306
|
+
}
|
|
5307
|
+
const sep3 = clean.includes("?") ? "&" : "?";
|
|
5308
|
+
const encoded = params.toString();
|
|
5309
|
+
return encoded ? `${clean}${sep3}${encoded}` : clean;
|
|
5310
|
+
}
|
|
5311
|
+
function matchOperations(doc, words) {
|
|
5312
|
+
const needles = words.map((w) => w.toLowerCase());
|
|
5313
|
+
const out = [];
|
|
5314
|
+
for (const [path, item] of Object.entries(doc.paths ?? {})) {
|
|
5315
|
+
for (const [method, op] of Object.entries(item)) {
|
|
5316
|
+
if (!API_METHODS.includes(method.toUpperCase())) continue;
|
|
5317
|
+
const hay = `${method} ${path} ${op.summary ?? ""} ${op.operationId ?? ""}`.toLowerCase();
|
|
5318
|
+
if (needles.every((n) => hay.includes(n))) out.push({ method: method.toUpperCase(), path, summary: op.summary, operationId: op.operationId });
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
return out.sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method));
|
|
5322
|
+
}
|
|
5323
|
+
async function searchApi(words) {
|
|
5324
|
+
const res = await apiRaw("GET", "/openapi.json", { auth: false });
|
|
5325
|
+
if (!res.ok) throw new Error(`could not read the API reference (${res.status})`);
|
|
5326
|
+
const ops = matchOperations(await res.json(), words);
|
|
5327
|
+
if (!ops.length) {
|
|
5328
|
+
console.log("no operations match");
|
|
5329
|
+
return;
|
|
5330
|
+
}
|
|
5331
|
+
for (const op of ops) console.log(`${op.method.padEnd(6)} ${op.path}${op.summary ? ` \u2014 ${op.summary}` : ""}`);
|
|
5332
|
+
}
|
|
5333
|
+
async function callApi(method, path, opts) {
|
|
5334
|
+
const verb = method.toUpperCase();
|
|
5335
|
+
if (!API_METHODS.includes(verb)) throw new Error(`method must be one of ${API_METHODS.join(", ")} or "search"`);
|
|
5336
|
+
const body = readData(opts.data);
|
|
5337
|
+
if (body !== void 0 && verb === "GET") throw new Error("a GET takes no --data; use --query");
|
|
5338
|
+
const res = await apiRaw(verb, requestPath(path, opts.query), { body, idempotencyKey: opts.idempotencyKey, universe: opts.workspace });
|
|
5339
|
+
const type = res.headers.get("content-type") ?? "";
|
|
5340
|
+
if (opts.output) {
|
|
5341
|
+
writeFileSync9(opts.output, new Uint8Array(await res.arrayBuffer()));
|
|
5342
|
+
console.error(`${res.status} \u2192 ${opts.output}`);
|
|
5343
|
+
} else if (/json/.test(type)) {
|
|
5344
|
+
const text = await res.text();
|
|
5345
|
+
let printed = text;
|
|
5346
|
+
try {
|
|
5347
|
+
printed = JSON.stringify(JSON.parse(text), null, 2);
|
|
5348
|
+
} catch {
|
|
5349
|
+
}
|
|
5350
|
+
(res.ok ? console.log : console.error)(printed);
|
|
5351
|
+
} else if (/^text\/|xml|csv|markdown/.test(type) || !type) {
|
|
5352
|
+
(res.ok ? console.log : console.error)(await res.text());
|
|
5353
|
+
} else {
|
|
5354
|
+
console.error(`${res.status}: ${type} body (${res.headers.get("content-length") ?? "?"} bytes); pass --output <file> to save it`);
|
|
5355
|
+
}
|
|
5356
|
+
if (!res.ok && res.status !== 304) process.exitCode = 1;
|
|
5357
|
+
}
|
|
5358
|
+
|
|
5162
5359
|
// src/billing-plans.ts
|
|
5163
5360
|
var readCodingPlan = () => api("GET", "/billing/coding-plan");
|
|
5164
5361
|
function joinCodingPlan(dollars2, requestId) {
|
|
@@ -5233,8 +5430,19 @@ function wholeNumber2(flag, value) {
|
|
|
5233
5430
|
return Number(value.trim());
|
|
5234
5431
|
}
|
|
5235
5432
|
|
|
5433
|
+
// src/workspace-flag.ts
|
|
5434
|
+
function workspaceFlagIsCommands(argv, commandPath) {
|
|
5435
|
+
let at = 0;
|
|
5436
|
+
let matched = 0;
|
|
5437
|
+
for (; at < argv.length && matched < commandPath.length; at += 1) {
|
|
5438
|
+
if (argv[at] === commandPath[matched]) matched += 1;
|
|
5439
|
+
}
|
|
5440
|
+
if (matched < commandPath.length) return false;
|
|
5441
|
+
return argv.slice(at).some((arg) => arg === "--workspace" || arg.startsWith("--workspace="));
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5236
5444
|
// src/harness.ts
|
|
5237
|
-
import { existsSync as
|
|
5445
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "fs";
|
|
5238
5446
|
import { dirname as dirname9, join as join18, resolve as resolve8 } from "path";
|
|
5239
5447
|
var HARNESS_FILE = "harness.json";
|
|
5240
5448
|
function harnessFilePath(dir) {
|
|
@@ -5242,11 +5450,11 @@ function harnessFilePath(dir) {
|
|
|
5242
5450
|
}
|
|
5243
5451
|
function readHarnessFile(dir) {
|
|
5244
5452
|
const path = harnessFilePath(dir);
|
|
5245
|
-
if (!
|
|
5453
|
+
if (!existsSync12(path)) {
|
|
5246
5454
|
throw new Error(`no ${HARNESS_FILE} found in ${resolve8(dir)} \u2014 run \`sky harness init\` first`);
|
|
5247
5455
|
}
|
|
5248
5456
|
try {
|
|
5249
|
-
return JSON.parse(
|
|
5457
|
+
return JSON.parse(readFileSync17(path, "utf8"));
|
|
5250
5458
|
} catch (err) {
|
|
5251
5459
|
throw new Error(`could not parse ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
5252
5460
|
}
|
|
@@ -5273,11 +5481,11 @@ var INIT_COMMENT = `# harness.json \u2014 workspace harness profile skeleton
|
|
|
5273
5481
|
`;
|
|
5274
5482
|
function harnessInit(dir) {
|
|
5275
5483
|
const path = harnessFilePath(dir);
|
|
5276
|
-
if (
|
|
5484
|
+
if (existsSync12(path)) {
|
|
5277
5485
|
throw new Error(`${path} already exists \u2014 delete it to re-init`);
|
|
5278
5486
|
}
|
|
5279
5487
|
mkdirSync6(dirname9(path), { recursive: true });
|
|
5280
|
-
|
|
5488
|
+
writeFileSync10(path, `${JSON.stringify(INIT_SKELETON, null, 2)}
|
|
5281
5489
|
`);
|
|
5282
5490
|
console.log(`Created ${path}`);
|
|
5283
5491
|
console.log(INIT_COMMENT);
|
|
@@ -5410,9 +5618,16 @@ function printModelBilling(session2) {
|
|
|
5410
5618
|
}
|
|
5411
5619
|
var program = new Command();
|
|
5412
5620
|
program.name("sky").description("Launch and talk to long-lived agents \u2014 agentsky.dev").version(CLI_VERSION).option("--workspace <slug>", "workspace to act in (overrides sky universe switch and SKY_UNIVERSE; alias of --universe on sub-commands)");
|
|
5413
|
-
program.hook("preAction", () => {
|
|
5621
|
+
program.hook("preAction", (_root, action) => {
|
|
5414
5622
|
const workspace2 = program.opts().workspace;
|
|
5415
|
-
if (workspace2)
|
|
5623
|
+
if (!workspace2) return;
|
|
5624
|
+
const path = [];
|
|
5625
|
+
for (let command = action; command && command !== program; command = command.parent) path.unshift(command.name());
|
|
5626
|
+
if (action.options.some((option) => option.long === "--workspace") && workspaceFlagIsCommands(process.argv.slice(2), path)) {
|
|
5627
|
+
action.setOptionValue("workspace", workspace2);
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
process.env.SKY_WORKSPACE = workspace2;
|
|
5416
5631
|
});
|
|
5417
5632
|
var lastProgressDecile = -1;
|
|
5418
5633
|
function uploadProgress(sent, total) {
|
|
@@ -5447,6 +5662,9 @@ auth.command("whoami").description("who the current token acts as").action(
|
|
|
5447
5662
|
console.log(
|
|
5448
5663
|
`${who.user.email ?? "unknown"} \xB7 universe ${who.universe.slug}${who.universe.isPersonal ? " (default)" : ""} \xB7 scopes ${who.scopes.join(",")}`
|
|
5449
5664
|
);
|
|
5665
|
+
if (who.credential_universe) {
|
|
5666
|
+
console.log(` this key is fixed to workspace ${who.credential_universe}: it cannot act in other workspaces or create new ones (make an account-wide key with \`sky keys create\`, or \`sky auth login\`)`);
|
|
5667
|
+
}
|
|
5450
5668
|
})
|
|
5451
5669
|
);
|
|
5452
5670
|
auth.command("connect <provider>").description("connect a model subscription: claude (setup-token) or chatgpt (Codex sign-in); --browser signs any plan in on its own page").option("-y, --yes", "skip the consent prompt").option("--browser", "sign in on the provider's page instead of using this machine's sign-in (claude, chatgpt, cursor, grok, muse)").option("--account <id>", "with --browser: re-sign an additional account, or `new` to add one").option("--label <label>", "with --browser: name the account").option("--no-open", "with --browser: print the sign-in link without opening it").action(
|
|
@@ -5583,6 +5801,12 @@ workspace.command("list").action(
|
|
|
5583
5801
|
})
|
|
5584
5802
|
);
|
|
5585
5803
|
workspace.command("delete <slug>").description("permanently delete a workspace you own and everything in it (asks you to type the slug)").option("-y, --yes", "skip typing the slug").action(run(deleteWorkspaceCommand));
|
|
5804
|
+
async function renameWorkspaceCommand(slug, name) {
|
|
5805
|
+
const { universe: renamed } = await api("PATCH", `/universes/${encodeURIComponent(slug)}`, { name: name.join(" ") });
|
|
5806
|
+
console.log(`${renamed.slug} is now "${renamed.name}"`);
|
|
5807
|
+
}
|
|
5808
|
+
universe.command("rename <slug> <name...>").description("rename a workspace you own (its slug stays)").action(run(renameWorkspaceCommand));
|
|
5809
|
+
workspace.command("rename <slug> <name...>").description("rename a workspace you own (alias of universe rename)").action(run(renameWorkspaceCommand));
|
|
5586
5810
|
var policy = program.command("policy").description("policy rules: tool calls, spend caps, call counts, capabilities");
|
|
5587
5811
|
async function policyScope(opts) {
|
|
5588
5812
|
return opts.session ? { session: await resolveSessionRef(opts.session) } : { slug: await workspaceSlug(opts.universe) };
|
|
@@ -5693,7 +5917,7 @@ agent.command("destroy <id>").description("delete the spec; requires all session
|
|
|
5693
5917
|
console.log(`Destroyed ${id}`);
|
|
5694
5918
|
})
|
|
5695
5919
|
);
|
|
5696
|
-
agent.command("message <id> [text]").description("send one message to the default session and stream the turn until idle").option("--detach", "don't follow the stream; exit once the message is accepted").option("--file <path...>", "attach local files (uploaded first, sent as document blocks)").option("--image <path...>", "attach local images (uploaded first, sent as image blocks)").option("--system <text>", "per-turn system text sent alongside the message").option("--plan", "plan mode: the agent inspects and answers with a plan, then waits for you to choose to implement it").option("--build", "build mode for this turn, overriding a session left in plan mode").action(
|
|
5920
|
+
agent.command("message <id> [text]").description("send one message to the default session and stream the turn until idle").option("--detach", "don't follow the stream; exit once the message is accepted").option("--file <path...>", "attach local files (uploaded first, sent as document blocks); a file_\u2026 id attaches that stored file").option("--file-id <id...>", "attach files already uploaded (`sky files upload`), by id, without uploading again").option("--image <path...>", "attach local images (uploaded first, sent as image blocks)").option("--system <text>", "per-turn system text sent alongside the message").option("--plan", "plan mode: the agent inspects and answers with a plan, then waits for you to choose to implement it").option("--build", "build mode for this turn, overriding a session left in plan mode").action(
|
|
5697
5921
|
run(
|
|
5698
5922
|
async (id, text, opts) => sendAndFollow(await resolveSessionRef(id), text ?? "", opts)
|
|
5699
5923
|
)
|
|
@@ -5807,7 +6031,7 @@ session.command("delete <id>").description("permanently delete the session, its
|
|
|
5807
6031
|
console.log(`Deleted ${id}`);
|
|
5808
6032
|
})
|
|
5809
6033
|
);
|
|
5810
|
-
session.command("message <ref> [text]").description("send one message (session id, or agent id = its default session)").option("--detach", "don't follow the stream; exit once the message is accepted").option("--file <path...>", "attach local files (uploaded first, sent as document blocks)").option("--image <path...>", "attach local images (uploaded first, sent as image blocks)").option("--system <text>", "per-turn system text sent alongside the message").option("--plan", "plan mode: the agent inspects and answers with a plan, then waits for you to choose to implement it").option("--build", "build mode for this turn, overriding a session left in plan mode").action(
|
|
6034
|
+
session.command("message <ref> [text]").description("send one message (session id, or agent id = its default session)").option("--detach", "don't follow the stream; exit once the message is accepted").option("--file <path...>", "attach local files (uploaded first, sent as document blocks); a file_\u2026 id attaches that stored file").option("--file-id <id...>", "attach files already uploaded (`sky files upload`), by id, without uploading again").option("--image <path...>", "attach local images (uploaded first, sent as image blocks)").option("--system <text>", "per-turn system text sent alongside the message").option("--plan", "plan mode: the agent inspects and answers with a plan, then waits for you to choose to implement it").option("--build", "build mode for this turn, overriding a session left in plan mode").action(
|
|
5811
6035
|
run(
|
|
5812
6036
|
async (ref, text, opts) => sendAndFollow(await resolveSessionRef(ref), text ?? "", opts)
|
|
5813
6037
|
)
|
|
@@ -6000,7 +6224,7 @@ sessionWorkspace.command("cat <ref> <path>").description("print a file (up to 1
|
|
|
6000
6224
|
run(async (ref, path, opts) => {
|
|
6001
6225
|
const bytes = await readFile(await resolveSessionRef(ref), path);
|
|
6002
6226
|
if (opts.out) {
|
|
6003
|
-
|
|
6227
|
+
writeFileSync11(opts.out, bytes);
|
|
6004
6228
|
console.error(`saved ${bytes.length} bytes to ${opts.out}`);
|
|
6005
6229
|
} else process.stdout.write(bytes);
|
|
6006
6230
|
})
|
|
@@ -6017,7 +6241,7 @@ sessionWorkspace.command("patch <ref> [repo]").description("the work in a reposi
|
|
|
6017
6241
|
console.error(`${meta.repo}: ${meta.commits.length} commit${meta.commits.length === 1 ? "" : "s"} since ${meta.base_branch || "the imported copy"} (${meta.base_commit.slice(0, 12)})`);
|
|
6018
6242
|
if (!patch.length) console.error("nothing changed");
|
|
6019
6243
|
else if (opts.out) {
|
|
6020
|
-
|
|
6244
|
+
writeFileSync11(opts.out, patch);
|
|
6021
6245
|
console.error(`wrote ${opts.out} \u2014 apply with: git apply ${opts.out}`);
|
|
6022
6246
|
} else process.stdout.write(patch);
|
|
6023
6247
|
})
|
|
@@ -6071,9 +6295,10 @@ function interactionMode(opts) {
|
|
|
6071
6295
|
}
|
|
6072
6296
|
async function uploadAttachments(opts) {
|
|
6073
6297
|
const files2 = [];
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6298
|
+
const refs = [...opts.image ?? [], ...opts.file ?? []].map((ref) => ({ ref, byId: false }));
|
|
6299
|
+
for (const { ref, byId } of [...refs, ...(opts.fileId ?? []).map((ref2) => ({ ref: ref2, byId: true }))]) {
|
|
6300
|
+
const { file, uploaded } = await attachment(ref, { byId });
|
|
6301
|
+
console.error(uploaded ? `uploaded ${file.filename} \u2192 ${file.id}` : `attaching ${file.id} (${file.filename})`);
|
|
6077
6302
|
files2.push(file);
|
|
6078
6303
|
}
|
|
6079
6304
|
return files2;
|
|
@@ -6090,9 +6315,10 @@ async function sendAndFollow(sessionId, text, opts) {
|
|
|
6090
6315
|
const controller = new AbortController();
|
|
6091
6316
|
let opened = null;
|
|
6092
6317
|
if (!opts.detach) {
|
|
6318
|
+
const render = createEventRenderer();
|
|
6093
6319
|
opened = (async () => {
|
|
6094
6320
|
for await (const frame of streamEvents(sessionId, controller.signal)) {
|
|
6095
|
-
|
|
6321
|
+
render(frame.event, frame.data);
|
|
6096
6322
|
if (turnOver(frame)) {
|
|
6097
6323
|
controller.abort();
|
|
6098
6324
|
break;
|
|
@@ -6113,10 +6339,11 @@ async function chatLoop(sessionId, opts = {}) {
|
|
|
6113
6339
|
const controller = new AbortController();
|
|
6114
6340
|
const rl = createInterface6({ input: process.stdin, output: process.stdout, prompt: "> " });
|
|
6115
6341
|
let system = opts.system;
|
|
6342
|
+
const render = createEventRenderer();
|
|
6116
6343
|
void (async () => {
|
|
6117
6344
|
try {
|
|
6118
6345
|
for await (const frame of streamEvents(sessionId, controller.signal)) {
|
|
6119
|
-
|
|
6346
|
+
render(frame.event, frame.data);
|
|
6120
6347
|
if (frame.event === "session.status_terminated") rl.close();
|
|
6121
6348
|
else if (frame.event === "session.status_idle") rl.prompt();
|
|
6122
6349
|
}
|
|
@@ -6223,10 +6450,32 @@ secrets.command("unset <id> <key>").action(
|
|
|
6223
6450
|
console.log(`unset ${key}`);
|
|
6224
6451
|
})
|
|
6225
6452
|
);
|
|
6226
|
-
|
|
6453
|
+
async function launchFromFile(path, spec, opts) {
|
|
6454
|
+
const { vcpus, memoryMb, ...specBody } = spec;
|
|
6455
|
+
const body = {
|
|
6456
|
+
...specBody,
|
|
6457
|
+
...opts.type ? { agentType: opts.type } : {},
|
|
6458
|
+
...opts.llm ? { llm: opts.llm } : {}
|
|
6459
|
+
};
|
|
6460
|
+
const { agent: created } = await api("POST", "/agents", body);
|
|
6461
|
+
const { session: session2 } = await api("POST", "/sessions", {
|
|
6462
|
+
agent: created.id,
|
|
6463
|
+
...opts.start ? {} : { provision: "deferred" },
|
|
6464
|
+
...vcpus !== void 0 ? { vcpus } : {},
|
|
6465
|
+
...memoryMb !== void 0 ? { memoryMb } : {}
|
|
6466
|
+
});
|
|
6467
|
+
stampId(path, created.id);
|
|
6468
|
+
console.log(`Launched ${created.id} (${created.agentType} \xB7 ${created.llm})`);
|
|
6469
|
+
printModelBilling(session2);
|
|
6470
|
+
console.log(opts.start ? " its first session is starting now" : " its first session starts on the first message");
|
|
6471
|
+
if (!spec.llm && !opts.llm) console.log(` model: ${created.llm}, the ${created.agentType} default \u2014 set llm = "\u2026" in ${basename9(path)} and run \`sky deploy\` to change it`);
|
|
6472
|
+
console.log(` sky agent message ${created.id} "hello"`);
|
|
6473
|
+
console.log(` sky agent chat ${created.id}`);
|
|
6474
|
+
}
|
|
6475
|
+
program.command("launch").description("scaffold ./agent.toml if absent, create the agent and its first session (idle until the first message), 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)").option("--start", "boot the first session now instead of on the first message").action(
|
|
6227
6476
|
run(async (opts) => {
|
|
6228
6477
|
const path = resolve9(opts.file);
|
|
6229
|
-
if (!
|
|
6478
|
+
if (!existsSync13(path)) {
|
|
6230
6479
|
const name = basename9(process.cwd()).replace(/[^a-zA-Z0-9 _-]/g, "") || "my-agent";
|
|
6231
6480
|
scaffoldAgentFile(path, name);
|
|
6232
6481
|
console.log(`Scaffolded ${opts.file} \u2014 edit it anytime; re-apply with \`sky deploy\`.`);
|
|
@@ -6236,55 +6485,27 @@ program.command("launch").description("scaffold ./agent.toml if absent, create t
|
|
|
6236
6485
|
console.log(`${file.id} already launched \u2014 use \`sky deploy\` to re-apply.`);
|
|
6237
6486
|
return;
|
|
6238
6487
|
}
|
|
6239
|
-
|
|
6240
|
-
const body = {
|
|
6241
|
-
...specBody,
|
|
6242
|
-
...opts.type ? { agentType: opts.type } : {},
|
|
6243
|
-
...opts.llm ? { llm: opts.llm } : {}
|
|
6244
|
-
};
|
|
6245
|
-
const { agent: created } = await api("POST", "/agents", body);
|
|
6246
|
-
console.log("Starting its runtime\u2026");
|
|
6247
|
-
const { session: session2 } = await api("POST", "/sessions", {
|
|
6248
|
-
agent: created.id,
|
|
6249
|
-
...vcpus !== void 0 ? { vcpus } : {},
|
|
6250
|
-
...memoryMb !== void 0 ? { memoryMb } : {}
|
|
6251
|
-
});
|
|
6252
|
-
stampId(path, created.id);
|
|
6253
|
-
console.log(`Launched ${created.id} (${created.agentType} \xB7 ${created.llm})`);
|
|
6254
|
-
printModelBilling(session2);
|
|
6255
|
-
console.log(` sky agent message ${created.id} "hello"`);
|
|
6256
|
-
console.log(` sky agent chat ${created.id}`);
|
|
6488
|
+
await launchFromFile(path, spec, opts);
|
|
6257
6489
|
})
|
|
6258
6490
|
);
|
|
6259
|
-
program.command("deploy").description("re-apply ./agent.toml to the existing agent (idempotent)").option("-f, --file <path>", "agent file", "agent.toml").action(
|
|
6491
|
+
program.command("deploy").description("re-apply ./agent.toml to the existing agent (idempotent: an unchanged file changes nothing)").option("-f, --file <path>", "agent file", "agent.toml").action(
|
|
6260
6492
|
run(async (opts) => {
|
|
6261
6493
|
const path = resolve9(opts.file);
|
|
6262
6494
|
const { file, spec } = readAgentFile(path);
|
|
6263
6495
|
if (!file.id) {
|
|
6264
6496
|
console.log("No agent id in the file yet \u2014 running launch instead.");
|
|
6265
|
-
|
|
6266
|
-
const { agent: created } = await api("POST", "/agents", specBody);
|
|
6267
|
-
await api("POST", "/sessions", {
|
|
6268
|
-
agent: created.id,
|
|
6269
|
-
...vcpus !== void 0 ? { vcpus } : {},
|
|
6270
|
-
...memoryMb !== void 0 ? { memoryMb } : {}
|
|
6271
|
-
});
|
|
6272
|
-
stampId(path, created.id);
|
|
6273
|
-
console.log(`Launched ${created.id}`);
|
|
6497
|
+
await launchFromFile(path, spec, {});
|
|
6274
6498
|
return;
|
|
6275
6499
|
}
|
|
6500
|
+
const ref = encodeURIComponent(file.id);
|
|
6501
|
+
const { agent: before } = await api("GET", `/agents/${ref}`);
|
|
6502
|
+
let promptChanged = false;
|
|
6276
6503
|
if (spec.prompt !== void 0) {
|
|
6277
|
-
await api("PUT", `/agents/${
|
|
6278
|
-
|
|
6279
|
-
});
|
|
6504
|
+
const saved = await api("PUT", `/agents/${ref}/prompt`, { prompt: spec.prompt });
|
|
6505
|
+
promptChanged = saved.unchanged === false;
|
|
6280
6506
|
}
|
|
6281
|
-
await api("PATCH", `/agents/${
|
|
6282
|
-
|
|
6283
|
-
...spec.capabilities ? { capabilities: spec.capabilities } : {},
|
|
6284
|
-
...spec.reasoningEffort ? { reasoningEffort: spec.reasoningEffort } : {},
|
|
6285
|
-
...spec.skills ? { skills: spec.skills } : {}
|
|
6286
|
-
});
|
|
6287
|
-
console.log(`Deployed ${file.id} (prompt + settings re-applied)`);
|
|
6507
|
+
const { agent: after } = await api("PATCH", `/agents/${ref}`, deployPatch(spec));
|
|
6508
|
+
for (const line of deployReport(file.id, spec, before, after, promptChanged)) console.log(line);
|
|
6288
6509
|
})
|
|
6289
6510
|
);
|
|
6290
6511
|
program.command("clone").description("clone your local agent (Claude Code, \u2026) into the cloud; re-run to sync its prompt").option("--harness <type>", "claude_code | codex | openclaw (default: detected)").option("--new", "always launch a fresh agent, even if this machine is linked").option("--dry-run", "print the plan and send nothing").option("-y, --yes", "skip the confirmation").option("--open", "open the agent page when done").action(
|
|
@@ -6525,14 +6746,14 @@ task.command("get <task> <path>").description("read one task file; prints it, or
|
|
|
6525
6746
|
run(async (taskId, path, opts) => {
|
|
6526
6747
|
const bytes = await readTaskFile(taskId, path);
|
|
6527
6748
|
if (opts.out) {
|
|
6528
|
-
|
|
6749
|
+
writeFileSync11(opts.out, bytes);
|
|
6529
6750
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
6530
6751
|
} else process.stdout.write(bytes);
|
|
6531
6752
|
})
|
|
6532
6753
|
);
|
|
6533
6754
|
task.command("put <task> <path> <local>").description("write a local file to <path> in the task's files (every session of the task sees it)").action(
|
|
6534
6755
|
run(async (taskId, path, local) => {
|
|
6535
|
-
const { file, conflict } = await writeTaskFile(taskId, path,
|
|
6756
|
+
const { file, conflict } = await writeTaskFile(taskId, path, readFileSync18(local), mediaTypeFor(local));
|
|
6536
6757
|
console.log(`${renderTaskFile(file)}${conflict ? " (replaced a version you had not seen)" : ""}`);
|
|
6537
6758
|
})
|
|
6538
6759
|
);
|
|
@@ -6605,7 +6826,7 @@ task.command("verdict <task> <did-the-job>").description("record whether the tas
|
|
|
6605
6826
|
console.log(`Recorded verdict on task ${updated.id}: did_the_job=${value === "yes"}`);
|
|
6606
6827
|
})
|
|
6607
6828
|
);
|
|
6608
|
-
var skills = program.command("skills").description("the workspace's skill store: upload a skill, then attach it to an agent
|
|
6829
|
+
var skills = program.command("skills").description("the workspace's skill store: upload a skill, then attach it to an agent (sky skills attach, or [[skills]] in agent.toml)");
|
|
6609
6830
|
skills.command("upload <dir>").description("upload a skill directory (SKILL.md at its root) as a new skill").option("--name <name>", "display name (default: the directory's name)").option("--json", "print the stored skill as JSON").action(
|
|
6610
6831
|
run(async (dir, opts) => {
|
|
6611
6832
|
const { skill } = await uploadSkill(dir, opts.name);
|
|
@@ -6613,7 +6834,7 @@ skills.command("upload <dir>").description("upload a skill directory (SKILL.md a
|
|
|
6613
6834
|
console.log(JSON.stringify(skill));
|
|
6614
6835
|
return;
|
|
6615
6836
|
}
|
|
6616
|
-
console.log(`Uploaded ${skill.id} (${skill.displayName ?? dir}). Attach it in agent.toml:`);
|
|
6837
|
+
console.log(`Uploaded ${skill.id} (${skill.displayName ?? dir}). Attach it: sky skills attach ${skill.id} --agent <id>, or in agent.toml:`);
|
|
6617
6838
|
console.log(` [[skills]]
|
|
6618
6839
|
type = "skill"
|
|
6619
6840
|
skillId = "${skill.id}"`);
|
|
@@ -6626,6 +6847,18 @@ skills.command("list").description("the workspace's stored skills").action(
|
|
|
6626
6847
|
for (const skill of list) console.log(`${skill.id} ${skill.displayName ?? ""}`);
|
|
6627
6848
|
})
|
|
6628
6849
|
);
|
|
6850
|
+
for (const [verb, blurb] of [
|
|
6851
|
+
["attach", "add a stored skill to an agent; its new sessions load it"],
|
|
6852
|
+
["detach", "remove a skill from an agent; its new sessions no longer load it"]
|
|
6853
|
+
]) {
|
|
6854
|
+
skills.command(`${verb} <skill>`).description(blurb).requiredOption("--agent <id>", "the agent").action(
|
|
6855
|
+
run(async (skillId, opts) => {
|
|
6856
|
+
const outcome = await setAgentSkill(opts.agent, skillId, verb);
|
|
6857
|
+
if (outcome === "unchanged") console.log(`${skillId} is ${verb === "attach" ? "already" : "not"} attached to ${opts.agent}`);
|
|
6858
|
+
else console.log(`${verb === "attach" ? "Attached" : "Detached"} ${skillId} ${verb === "attach" ? "to" : "from"} ${opts.agent} \u2014 new sessions ${verb === "attach" ? "load it" : "no longer load it"}; running sessions keep the skills they started with`);
|
|
6859
|
+
})
|
|
6860
|
+
);
|
|
6861
|
+
}
|
|
6629
6862
|
skills.command("usage").description("view and miss counts per skill name in this workspace").option("--since <iso>", `start date (ISO 8601); default is the last 30 days`).option("--json", "print JSON").action(
|
|
6630
6863
|
run(async (opts) => {
|
|
6631
6864
|
const summary = await getSkillUsage(opts.since);
|
|
@@ -6684,7 +6917,7 @@ library.command("list").description("your Library, newest first").option("--sear
|
|
|
6684
6917
|
);
|
|
6685
6918
|
library.command("upload <path>").description("save a file from this computer to your Library").option("--workspace <key>", "place it in this workspace").action(
|
|
6686
6919
|
run(async (path, opts) => {
|
|
6687
|
-
const bytes =
|
|
6920
|
+
const bytes = readFileSync18(path);
|
|
6688
6921
|
const { artifact: artifact2 } = await uploadToLibrary({ bytes, name: basename9(path), mediaType: "application/octet-stream" }, `sky-upload-${crypto.randomUUID()}`, opts.workspace);
|
|
6689
6922
|
console.log(renderLibraryArtifact(artifact2));
|
|
6690
6923
|
})
|
|
@@ -6715,7 +6948,7 @@ library.command("place <id> <workspace>").description('place your artifact in a
|
|
|
6715
6948
|
library.command("download <id> <out>").description("save one version's bytes to a file").option("--version <n>", "a saved version (the current one when omitted)").action(
|
|
6716
6949
|
run(async (id, out, opts) => {
|
|
6717
6950
|
const { bytes } = await downloadLibraryArtifact(id, opts.version ? Number(opts.version) : void 0);
|
|
6718
|
-
|
|
6951
|
+
writeFileSync11(out, bytes);
|
|
6719
6952
|
console.log(`wrote ${bytes.length} bytes to ${out}`);
|
|
6720
6953
|
})
|
|
6721
6954
|
);
|
|
@@ -6736,7 +6969,7 @@ artifact.command("get <id> <path>").description("read one file of an artifact; p
|
|
|
6736
6969
|
run(async (id, path, opts) => {
|
|
6737
6970
|
const bytes = await readArtifactFile(id, path);
|
|
6738
6971
|
if (opts.out) {
|
|
6739
|
-
|
|
6972
|
+
writeFileSync11(opts.out, bytes);
|
|
6740
6973
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
6741
6974
|
} else process.stdout.write(bytes);
|
|
6742
6975
|
})
|
|
@@ -6835,6 +7068,12 @@ for (const [verb, blurb] of [
|
|
|
6835
7068
|
})
|
|
6836
7069
|
);
|
|
6837
7070
|
}
|
|
7071
|
+
routine.command("rm <id>").description("delete a routine: it never runs again; the sessions it started are kept").action(
|
|
7072
|
+
run(async (id) => {
|
|
7073
|
+
await api("DELETE", `/routines/${encodeURIComponent(id)}`);
|
|
7074
|
+
console.log(`Deleted routine ${id}. It will not run again; sessions it started are kept.`);
|
|
7075
|
+
})
|
|
7076
|
+
);
|
|
6838
7077
|
routine.command("runs [id]").description("every fire attempt, newest first \u2014 of one routine, or all of them").option("--failed", "only runs that failed to start").option("--limit <n>", "how many (default 20)").action(
|
|
6839
7078
|
run(async (id, opts) => {
|
|
6840
7079
|
const params = new URLSearchParams({ limit: opts.limit ?? "20" });
|
|
@@ -7052,7 +7291,7 @@ usageOptions(
|
|
|
7052
7291
|
run(async (opts) => {
|
|
7053
7292
|
const { bytes } = await apiBytes("GET", `/billing/usage/export${usageQuery(opts)}`);
|
|
7054
7293
|
if (opts.out) {
|
|
7055
|
-
|
|
7294
|
+
writeFileSync11(opts.out, bytes);
|
|
7056
7295
|
console.log(`Wrote ${bytes.length} bytes to ${opts.out}`);
|
|
7057
7296
|
} else {
|
|
7058
7297
|
process.stdout.write(bytes);
|
|
@@ -7591,6 +7830,85 @@ help.command("get <slug>").description("read a help article by its slug").option
|
|
|
7591
7830
|
console.log(article.body);
|
|
7592
7831
|
})
|
|
7593
7832
|
);
|
|
7833
|
+
program.command("api <method> [path...]").description("call any AgentSky API operation (GET|POST|PUT|PATCH|DELETE <path>), or `sky api search <words\u2026>` to find one").option("-d, --data <json>", "JSON body: inline, @file, or - for stdin").option("-q, --query <key=value>", "query parameter (repeatable)", (v, acc) => [...acc, v], []).option("--workspace <slug>", "send the request in this workspace (X-Universe)").option("--idempotency-key <key>", "retry-safe key for a POST").option("-o, --output <file>", "save the body to a file (for downloads)").action(
|
|
7834
|
+
run(async (method, rest, opts) => {
|
|
7835
|
+
if (method.toLowerCase() === "search") return searchApi(rest);
|
|
7836
|
+
if (rest.length !== 1) throw new Error("usage: sky api <METHOD> <path> (one path), or sky api search <words\u2026>");
|
|
7837
|
+
return callApi(method, rest[0], opts);
|
|
7838
|
+
})
|
|
7839
|
+
);
|
|
7840
|
+
agent.command("edit <id>").description("change an agent's name, engine, model or thinking level (as its Library folder does)").option("--name <name>", "new name").option("--engine <agentType>", "engine, e.g. CLAUDE_CODE, CODEX, HERMES").option("--model <llm>", "model id, e.g. anthropic/claude-sonnet-4-5").option("--thinking <effort>", 'reasoning effort, or "default" to clear the pin').action(
|
|
7841
|
+
run(async (id, opts) => {
|
|
7842
|
+
const body = {
|
|
7843
|
+
...opts.name ? { displayName: opts.name } : {},
|
|
7844
|
+
...opts.engine ? { agentType: opts.engine } : {},
|
|
7845
|
+
...opts.model ? { llm: opts.model } : {},
|
|
7846
|
+
...opts.thinking ? { reasoningEffort: opts.thinking === "default" ? null : opts.thinking } : {}
|
|
7847
|
+
};
|
|
7848
|
+
if (!Object.keys(body).length) throw new Error("pass at least one of --name, --engine, --model, --thinking");
|
|
7849
|
+
const { agent: a } = await api("PATCH", `/agents/${encodeURIComponent(id)}`, body);
|
|
7850
|
+
console.log(`${a.id} \u2014 ${a.displayName} \xB7 ${a.agentType} \xB7 ${a.llm}${a.reasoningEffort ? ` \xB7 ${a.reasoningEffort}` : ""}`);
|
|
7851
|
+
})
|
|
7852
|
+
);
|
|
7853
|
+
skills.command("revisions <skillId>").description("a skill's saved versions, newest first").option("--json", "print as JSON").action(
|
|
7854
|
+
run(async (skillId, opts) => {
|
|
7855
|
+
const page = await api("GET", `/skills/${encodeURIComponent(skillId)}/revisions`);
|
|
7856
|
+
const rows = page.revisions ?? [];
|
|
7857
|
+
if (opts.json) return void console.log(JSON.stringify(rows, null, 2));
|
|
7858
|
+
if (!rows.length) return void console.log("no versions");
|
|
7859
|
+
for (const r of rows) console.log(`${r.id} ${r.createdAt ?? r.created_at ?? ""}`);
|
|
7860
|
+
})
|
|
7861
|
+
);
|
|
7862
|
+
skills.command("cat <skillId> <revisionId> <path>").description("print one file (e.g. SKILL.md) of a skill version").action(
|
|
7863
|
+
run(async (skillId, revisionId, path) => {
|
|
7864
|
+
const res = await apiRaw("GET", `/skills/${encodeURIComponent(skillId)}/revisions/${encodeURIComponent(revisionId)}/content?path=${encodeURIComponent(path)}`);
|
|
7865
|
+
if (!res.ok) throw new ApiError(res.status, "http_error", await res.text() || `${res.status}`);
|
|
7866
|
+
process.stdout.write(await res.text());
|
|
7867
|
+
})
|
|
7868
|
+
);
|
|
7869
|
+
skills.command("rm <skillId>").description("delete a skill from the workspace").action(
|
|
7870
|
+
run(async (skillId) => {
|
|
7871
|
+
await api("DELETE", `/skills/${encodeURIComponent(skillId)}`);
|
|
7872
|
+
console.log(`Deleted ${skillId}`);
|
|
7873
|
+
})
|
|
7874
|
+
);
|
|
7875
|
+
var notes = program.command("notes").description("memory notes in the Library's memory stores: list, read, write (every save keeps a version)");
|
|
7876
|
+
notes.command("stores").description("the workspace's memory stores").action(
|
|
7877
|
+
run(async () => {
|
|
7878
|
+
const { memory_stores: stores } = await api("GET", "/memory_stores?limit=100");
|
|
7879
|
+
if (!stores.length) return void console.log("no memory stores");
|
|
7880
|
+
for (const st of stores) console.log(`${st.id} ${st.name}`);
|
|
7881
|
+
})
|
|
7882
|
+
);
|
|
7883
|
+
notes.command("list <storeId> [folder]").description("the notes in a store, optionally under a folder ending in /").action(
|
|
7884
|
+
run(async (storeId, folder) => {
|
|
7885
|
+
const q = folder ? `&path_prefix=${encodeURIComponent(folder)}` : "";
|
|
7886
|
+
const { memories } = await api("GET", `/memory_stores/${encodeURIComponent(storeId)}/memories?limit=100${q}`);
|
|
7887
|
+
if (!memories.length) return void console.log("no notes");
|
|
7888
|
+
for (const n of memories) console.log(`${n.id} ${n.path}`);
|
|
7889
|
+
})
|
|
7890
|
+
);
|
|
7891
|
+
notes.command("read <storeId> <noteId>").description("print one note").action(
|
|
7892
|
+
run(async (storeId, noteId) => {
|
|
7893
|
+
const { memory: memory2 } = await api("GET", `/memory_stores/${encodeURIComponent(storeId)}/memories/${encodeURIComponent(noteId)}`);
|
|
7894
|
+
process.stdout.write(memory2.content.endsWith("\n") ? memory2.content : `${memory2.content}
|
|
7895
|
+
`);
|
|
7896
|
+
})
|
|
7897
|
+
);
|
|
7898
|
+
notes.command("save <storeId> <file>").description("write a note from a local file: --path creates one, --note saves a new version of one").option("--path <path>", "path of a new note, e.g. /projects/launch.md").option("--note <noteId>", "the note to update").action(
|
|
7899
|
+
run(async (storeId, file, opts) => {
|
|
7900
|
+
if (!opts.path === !opts.note) throw new Error("pass exactly one of --path (new note) or --note (update)");
|
|
7901
|
+
const content = readFileSync18(file, "utf8");
|
|
7902
|
+
const target = `/memory_stores/${encodeURIComponent(storeId)}/memories${opts.note ? `/${encodeURIComponent(opts.note)}` : ""}`;
|
|
7903
|
+
const { memory: memory2 } = await api("POST", target, { content, ...opts.path ? { path: opts.path } : {} });
|
|
7904
|
+
console.log(`Saved ${memory2.path} (${memory2.id})`);
|
|
7905
|
+
})
|
|
7906
|
+
);
|
|
7907
|
+
workspace.command("context").description("what every session in the workspace loads at start: AGENTS.md, MEMORY.md, indexes, your member page").action(
|
|
7908
|
+
run(async () => {
|
|
7909
|
+
console.log(JSON.stringify(await api("GET", "/workspace-context"), null, 2));
|
|
7910
|
+
})
|
|
7911
|
+
);
|
|
7594
7912
|
var vault = program.command("vault").description("vaults: credentials a session is granted when it starts (values write-only)");
|
|
7595
7913
|
vault.command("list").description("the workspace's vaults").action(
|
|
7596
7914
|
run(async () => {
|
|
@@ -7680,7 +7998,7 @@ harness.command("connect").description("run an ACP agent (the command after --)
|
|
|
7680
7998
|
}));
|
|
7681
7999
|
var traceCmd = program.command("trace").description("correlate this machine's agent work with the cloud sessions it starts (metadata only unless you choose content)");
|
|
7682
8000
|
traceCmd.command("start").description("start a trace: a grant for this machine, a root span, and the spool the hooks write to").option("--name <name>", "a name for the task").option("--source <source>", "how this machine reports itself", "claude-code").option("--hooks [settings]", "also install the Claude Code hooks (default: ./.claude/settings.local.json)").option("--capture <scopes>", "also send content, redacted on this machine first: any of prompts,tool_output,files").option("--file <path>", "a file the files scope may send (repeatable)", collect).option("--dir <path>", "a folder whose files the files scope may send when a file tool touches them (repeatable)", collect).action(run(async (opts) => {
|
|
7683
|
-
const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-
|
|
8001
|
+
const { readState, writeState, record, installClaudeHooks, parseContentScopes, chosenPaths, fileEvent } = await import("./trace-CFHCOGKB.js");
|
|
7684
8002
|
if (readState()) throw new Error("a trace is already running here \u2014 `sky trace stop` it first");
|
|
7685
8003
|
const scopes = parseContentScopes(opts.capture);
|
|
7686
8004
|
const chosen = chosenPaths(opts.file ?? [], opts.dir ?? []);
|
|
@@ -7715,7 +8033,7 @@ traceCmd.command("start").description("start a trace: a grant for this machine,
|
|
|
7715
8033
|
}));
|
|
7716
8034
|
traceCmd.command("hook").description("record one Claude Code hook event read from stdin (run by the installed hooks; never fails, prints nothing)").action(async () => {
|
|
7717
8035
|
try {
|
|
7718
|
-
const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-
|
|
8036
|
+
const { readState, record, flush, grantSender, eventsForClaudeHook, contentForClaudeHook, clearState } = await import("./trace-CFHCOGKB.js");
|
|
7719
8037
|
const chunks = [];
|
|
7720
8038
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
7721
8039
|
const input = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
@@ -7728,14 +8046,14 @@ traceCmd.command("hook").description("record one Claude Code hook event read fro
|
|
|
7728
8046
|
}
|
|
7729
8047
|
});
|
|
7730
8048
|
traceCmd.command("flush").description("send what the spool holds, retrying with backoff").action(run(async () => {
|
|
7731
|
-
const { readState, flush, grantSender } = await import("./trace-
|
|
8049
|
+
const { readState, flush, grantSender } = await import("./trace-CFHCOGKB.js");
|
|
7732
8050
|
const state = readState();
|
|
7733
8051
|
if (!state) throw new Error("no trace is running here");
|
|
7734
8052
|
const outcome = await flush(grantSender(state), { attempts: 4 });
|
|
7735
8053
|
console.log(`sent ${outcome.sent}, refused ${outcome.refused}, given up ${outcome.expired}, still pending ${outcome.pending}${outcome.stopped ? ` (stopped: ${outcome.stopped})` : ""}`);
|
|
7736
8054
|
}));
|
|
7737
8055
|
traceCmd.command("status").description("the running trace and its spool").action(run(async () => {
|
|
7738
|
-
const { readState, spoolStatus } = await import("./trace-
|
|
8056
|
+
const { readState, spoolStatus } = await import("./trace-CFHCOGKB.js");
|
|
7739
8057
|
const state = readState();
|
|
7740
8058
|
if (!state) {
|
|
7741
8059
|
console.log("No trace is running here.");
|
|
@@ -7745,7 +8063,7 @@ traceCmd.command("status").description("the running trace and its spool").action
|
|
|
7745
8063
|
console.log(`${state.trace_id} as ${state.source}: ${spool.pending} pending, ${spool.dropped_full} dropped (spool full), ${spool.dropped_expired} given up, ${spool.refused} refused`);
|
|
7746
8064
|
}));
|
|
7747
8065
|
traceCmd.command("show [id]").description("the trace bundle: spans, the sessions they reach, cost counted once per session").option("--json", "print the bundle as JSON").action(run(async (id, opts) => {
|
|
7748
|
-
const { readState } = await import("./trace-
|
|
8066
|
+
const { readState } = await import("./trace-CFHCOGKB.js");
|
|
7749
8067
|
const ref = id ?? readState()?.trace_id;
|
|
7750
8068
|
if (!ref) throw new Error("name a trace, or start one here");
|
|
7751
8069
|
const bundle = await api("GET", `/traces/${ref}`);
|
|
@@ -7772,7 +8090,7 @@ traceCmd.command("show [id]").description("the trace bundle: spans, the sessions
|
|
|
7772
8090
|
for (const gap of bundle.gaps) console.log(` gap: ${gap.span_id} ${gap.reason}`);
|
|
7773
8091
|
}));
|
|
7774
8092
|
traceCmd.command("stop").description("end the root span, send what is left, and revoke this machine's grant").action(run(async () => {
|
|
7775
|
-
const { readState, record, flush, grantSender, clearState } = await import("./trace-
|
|
8093
|
+
const { readState, record, flush, grantSender, clearState } = await import("./trace-CFHCOGKB.js");
|
|
7776
8094
|
const state = readState();
|
|
7777
8095
|
if (!state) throw new Error("no trace is running here");
|
|
7778
8096
|
if (state.root_span) record(() => [{ span_id: state.root_span, kind: "end", outcome: "completed" }]);
|
|
@@ -7782,7 +8100,7 @@ traceCmd.command("stop").description("end the root span, send what is left, and
|
|
|
7782
8100
|
console.log(`Stopped ${state.trace_id}${outcome.pending ? ` (${outcome.pending} events could not be sent)` : ""}`);
|
|
7783
8101
|
}));
|
|
7784
8102
|
traceCmd.command("sync [payload]").description("upload this machine's agent sessions (Claude Code, Codex, Cursor, Gemini CLI and every other agent `--agents` lists) to the matching workspace's trace library, redacted here first").option("--enable", "turn sync on for this machine").option("--disable", "turn sync off; nothing more is uploaded").option("--route <match=workspace>", "send sessions whose folder path contains <match> to <workspace> (repeatable; replaces the saved rules)", collect).option("--default <workspace>", "where a session no rule matches goes").option("--keep-local", "keep sessions no rule matches on this machine (the default)").option("--session <ref>", "only this session: its id or transcript path (repeatable)", collect).option("--import <export>", "add a downloaded cloud export (claude.ai, ChatGPT, Gemini, Grok, DeepSeek \u2026: its .zip, folder or file) so sync uploads it (repeatable)", collect).option("--agents", "list the agents sync reads and which are on this machine").option("--full", "re-read every session, not only files that changed").option("--limit <n>", "at most this many changed sessions this run", (v) => Number.parseInt(v, 10)).option("--dry-run", "show what would be uploaded without uploading").option("--hook", "run from a Claude Code SessionEnd hook or Codex notify: upload that session in the background and return at once").option("--install-schedule", "also sweep every 15 minutes (macOS launchd)").option("--status", "what sync has done on this machine").option("--quiet", "print only failures").action(async (payload, opts) => {
|
|
7785
|
-
const lib = await import("./trace-library-
|
|
8103
|
+
const lib = await import("./trace-library-2VHTJCOY.js");
|
|
7786
8104
|
if (opts.hook) {
|
|
7787
8105
|
try {
|
|
7788
8106
|
let raw = payload ?? "";
|
|
@@ -7854,11 +8172,11 @@ traceCmd.command("sync [payload]").description("upload this machine's agent sess
|
|
|
7854
8172
|
console.log(`Sweeping every 15 minutes (${plist}); a full re-read runs weekly. Log: ${log}`);
|
|
7855
8173
|
}
|
|
7856
8174
|
if (opts.status) {
|
|
7857
|
-
const { readFileSync:
|
|
8175
|
+
const { readFileSync: readFileSync19 } = await import("fs");
|
|
7858
8176
|
const { join: join19 } = await import("path");
|
|
7859
8177
|
let state = { sessions: {} };
|
|
7860
8178
|
try {
|
|
7861
|
-
state = JSON.parse(
|
|
8179
|
+
state = JSON.parse(readFileSync19(join19(lib.syncConfigPath(), "..", "trace-sync-state.json"), "utf8"));
|
|
7862
8180
|
} catch {
|
|
7863
8181
|
}
|
|
7864
8182
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -7891,7 +8209,7 @@ traceCmd.command("sync [payload]").description("upload this machine's agent sess
|
|
|
7891
8209
|
})();
|
|
7892
8210
|
});
|
|
7893
8211
|
traceCmd.command("search <query...>").description("search every session in a workspace's trace library (literal substring)").option("--workspace <slug>", "the workspace to search (default: the current one)").option("--limit <n>", "hits per page", (v) => Number.parseInt(v, 10), 20).option("--cursor <cursor>", "the next page of an earlier search").option("--json", "print the page as JSON").action(run(async (words, opts) => {
|
|
7894
|
-
const { libraryRequest } = await import("./trace-library-
|
|
8212
|
+
const { libraryRequest } = await import("./trace-library-2VHTJCOY.js");
|
|
7895
8213
|
const workspace2 = opts.workspace ?? currentUniverse();
|
|
7896
8214
|
if (!workspace2) throw new Error("name a workspace with --workspace");
|
|
7897
8215
|
const params = new URLSearchParams({ q: words.join(" "), scope: "traces", limit: String(opts.limit), ...opts.cursor ? { cursor: opts.cursor } : {} });
|
|
@@ -7908,7 +8226,7 @@ traceCmd.command("search <query...>").description("search every session in a wor
|
|
|
7908
8226
|
if (page.nextCursor) console.log(`More: sky trace search ${JSON.stringify(words.join(" "))} --workspace ${workspace2} --cursor ${page.nextCursor}`);
|
|
7909
8227
|
}));
|
|
7910
8228
|
traceCmd.command("library").description("list the sessions in a workspace's trace library, most recently updated first").option("--workspace <slug>", "the workspace (default: the current one)").option("--limit <n>", "how many", (v) => Number.parseInt(v, 10), 20).option("--json", "print the page as JSON").action(run(async (opts) => {
|
|
7911
|
-
const { libraryRequest } = await import("./trace-library-
|
|
8229
|
+
const { libraryRequest } = await import("./trace-library-2VHTJCOY.js");
|
|
7912
8230
|
const workspace2 = opts.workspace ?? currentUniverse();
|
|
7913
8231
|
if (!workspace2) throw new Error("name a workspace with --workspace");
|
|
7914
8232
|
const page = await libraryRequest("GET", `/trace-library?limit=${opts.limit}`, workspace2);
|
|
@@ -8214,7 +8532,7 @@ session.command("code-review <id>").description("run the code-review assistant o
|
|
|
8214
8532
|
}
|
|
8215
8533
|
})
|
|
8216
8534
|
);
|
|
8217
|
-
session.command("snapshot <id>").description("read or revoke the public share link for a session").requiredOption("--thread <threadId>", "the thread/comparison that owns this session").option("--revoke", "revoke the snapshot link instead of reading it").option("--json", "print as JSON").action(
|
|
8535
|
+
session.command("snapshot <id>").usage("<id> --thread <threadId> [--revoke] [--json]").description("read or revoke the public share link for a session; --thread is required").requiredOption("--thread <threadId>", "(required) the thread/comparison that owns this session").option("--revoke", "revoke the snapshot link instead of reading it").option("--json", "print as JSON").action(
|
|
8218
8536
|
run(async (id, opts) => {
|
|
8219
8537
|
const path = `/sessions/${encodeURIComponent(id)}/snapshot?thread=${encodeURIComponent(opts.thread)}`;
|
|
8220
8538
|
const result = await api(opts.revoke ? "DELETE" : "GET", path);
|