@sulala/agent-os 0.1.23 → 0.1.25
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/dashboard-dist/assets/index-BZYG7rCd.js +75 -0
- package/dashboard-dist/assets/index-CAOgf_FY.css +1 -0
- package/dashboard-dist/assets/index-CVI9FAmG.css +1 -0
- package/dashboard-dist/assets/index-DdMu_Z6v.js +75 -0
- package/dashboard-dist/index.html +2 -2
- package/data/agents/crm_hubspot_agent.json +11 -0
- package/data/agents/research_agent.json +1 -1
- package/data/agents/social_media_agent.json +1 -1
- package/data/agents/source_verify_agent.json +10 -0
- package/data/agents/writer_agent.json +1 -1
- package/data/skills/bluesky/SKILL.md +1 -1
- package/data/skills/content-writing/SKILL.md +32 -0
- package/data/skills/date/SKILL.md +1 -1
- package/data/skills/fetch/SKILL.md +1 -1
- package/data/skills/file-search/SKILL.md +1 -1
- package/data/skills/file-stats/SKILL.md +1 -1
- package/data/skills/git/SKILL.md +1 -1
- package/data/skills/hash/SKILL.md +1 -1
- package/data/skills/jq/SKILL.md +1 -1
- package/data/skills/markdown-to-html/SKILL.md +1 -1
- package/data/skills/qr-code/SKILL.md +1 -1
- package/data/skills/rss/SKILL.md +1 -1
- package/data/skills/translate/SKILL.md +1 -1
- package/data/skills/weather/SKILL.md +1 -1
- package/data/skills/web-search/SKILL.md +1 -1
- package/data/skills/webhook/SKILL.md +1 -1
- package/dist/cli.js +234 -69
- package/dist/index.js +168 -57
- package/package.json +1 -1
- package/data/skills/gmail/SKILL.md +0 -55
- package/data/skills/gmail/references/send-email.md +0 -54
- package/data/skills/gmail/scripts/send_email.py +0 -94
- package/data/skills/youtube/SKILL.md +0 -91
- package/data/skills/youtube/config.schema.json +0 -11
- package/data/skills/youtube/package.json +0 -8
- package/data/skills/youtube/references/youtube-upload.md +0 -65
- package/data/skills/youtube/requirements.txt +0 -3
- package/data/skills/youtube/scripts/youtube_upload.js +0 -200
- package/data/skills/youtube/scripts/youtube_upload.py +0 -125
package/dist/cli.js
CHANGED
|
@@ -8451,6 +8451,28 @@ var init_skill_tools = () => {};
|
|
|
8451
8451
|
import { readFile as readFile5, readdir as readdir3, cp, mkdir as mkdir3, writeFile as writeFile3, rm } from "fs/promises";
|
|
8452
8452
|
import { join as join5, resolve as resolve2, basename } from "path";
|
|
8453
8453
|
import { tmpdir } from "os";
|
|
8454
|
+
async function readSkillMeta(skillDir, subEntries) {
|
|
8455
|
+
if (!subEntries.includes(SULALA_META_FILE))
|
|
8456
|
+
return null;
|
|
8457
|
+
try {
|
|
8458
|
+
const raw = await readFile5(join5(skillDir, SULALA_META_FILE), "utf-8");
|
|
8459
|
+
const data = JSON.parse(raw);
|
|
8460
|
+
return typeof data.version === "string" && data.version.trim() !== "" ? { version: data.version.trim() } : null;
|
|
8461
|
+
} catch {
|
|
8462
|
+
return null;
|
|
8463
|
+
}
|
|
8464
|
+
}
|
|
8465
|
+
async function writeSkillMeta(skillId, meta) {
|
|
8466
|
+
const skillDir = join5(getSkillsDir(), skillId);
|
|
8467
|
+
const payload = {};
|
|
8468
|
+
if (meta.version?.trim())
|
|
8469
|
+
payload.version = meta.version.trim();
|
|
8470
|
+
if (meta.source?.trim())
|
|
8471
|
+
payload.source = meta.source.trim();
|
|
8472
|
+
if (Object.keys(payload).length === 0)
|
|
8473
|
+
return;
|
|
8474
|
+
await writeFile3(join5(skillDir, SULALA_META_FILE), JSON.stringify(payload, null, 0), "utf-8");
|
|
8475
|
+
}
|
|
8454
8476
|
async function loadSkill(name) {
|
|
8455
8477
|
const dir = join5(getSkillsDir(), name);
|
|
8456
8478
|
let entries;
|
|
@@ -8693,10 +8715,14 @@ async function listSkills() {
|
|
|
8693
8715
|
continue;
|
|
8694
8716
|
const skillName = doc.name ?? name;
|
|
8695
8717
|
const requiredEnv = await getRequiredEnvForSkill(skillDir, subEntries, doc);
|
|
8718
|
+
const docVersion = doc.version?.trim() || undefined;
|
|
8719
|
+
const meta = await readSkillMeta(skillDir, subEntries);
|
|
8720
|
+
const version = docVersion ?? meta?.version;
|
|
8696
8721
|
results.push({
|
|
8697
8722
|
id: name,
|
|
8698
8723
|
name: skillName,
|
|
8699
8724
|
description: doc.description,
|
|
8725
|
+
version,
|
|
8700
8726
|
tools: (doc.tools ?? []).map((t) => ({ id: t.id, description: t.description })),
|
|
8701
8727
|
required_env: requiredEnv.length ? requiredEnv : undefined,
|
|
8702
8728
|
system: SYSTEM_SKILL_IDS.has(name)
|
|
@@ -8766,7 +8792,7 @@ async function installSystemSkills() {
|
|
|
8766
8792
|
}
|
|
8767
8793
|
return { installed };
|
|
8768
8794
|
}
|
|
8769
|
-
async function installSkillFromUrl(url, explicitId) {
|
|
8795
|
+
async function installSkillFromUrl(url, explicitId, meta) {
|
|
8770
8796
|
const urlLower = url.toLowerCase();
|
|
8771
8797
|
const isStoreSkillContentUrl = urlLower.includes("/api/sulalahub/skills/") && !urlLower.includes("/download") && !urlLower.endsWith(".zip");
|
|
8772
8798
|
const headers = isStoreSkillContentUrl ? { Accept: "application/zip" } : {};
|
|
@@ -8781,31 +8807,35 @@ async function installSkillFromUrl(url, explicitId) {
|
|
|
8781
8807
|
const tmpDir = join5(tmpdir(), `agent-os-skill-${Date.now()}`);
|
|
8782
8808
|
await mkdir3(tmpDir, { recursive: true });
|
|
8783
8809
|
try {
|
|
8810
|
+
let destId;
|
|
8784
8811
|
if (isZip) {
|
|
8785
8812
|
const zipPath = join5(tmpDir, "archive.zip");
|
|
8786
8813
|
await writeFile3(zipPath, new Uint8Array(buf));
|
|
8787
|
-
const
|
|
8788
|
-
const
|
|
8789
|
-
if (
|
|
8790
|
-
const err = await new Response(
|
|
8814
|
+
const proc = Bun.spawn({ cmd: ["unzip", "-q", "-o", zipPath, "-d", tmpDir], stdout: "ignore", stderr: "pipe" });
|
|
8815
|
+
const exit = await proc.exited;
|
|
8816
|
+
if (exit !== 0) {
|
|
8817
|
+
const err = await new Response(proc.stderr).text();
|
|
8791
8818
|
throw new Error(`unzip failed: ${err}`);
|
|
8792
8819
|
}
|
|
8793
|
-
const { id
|
|
8794
|
-
|
|
8795
|
-
await cp(
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8820
|
+
const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.zip");
|
|
8821
|
+
destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
|
|
8822
|
+
await cp(sourcePath, join5(skillsDir, destId), { recursive: true });
|
|
8823
|
+
} else {
|
|
8824
|
+
const tarPath = join5(tmpDir, "archive.tar.gz");
|
|
8825
|
+
await writeFile3(tarPath, new Uint8Array(buf));
|
|
8826
|
+
const proc = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "ignore", stderr: "pipe" });
|
|
8827
|
+
const exit = await proc.exited;
|
|
8828
|
+
if (exit !== 0) {
|
|
8829
|
+
const err = await new Response(proc.stderr).text();
|
|
8830
|
+
throw new Error(`tar extract failed: ${err}`);
|
|
8831
|
+
}
|
|
8832
|
+
const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.tar.gz");
|
|
8833
|
+
destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
|
|
8834
|
+
await cp(sourcePath, join5(skillsDir, destId), { recursive: true });
|
|
8835
|
+
}
|
|
8836
|
+
if (meta?.version?.trim() || meta?.source?.trim()) {
|
|
8837
|
+
await writeSkillMeta(destId, { version: meta.version, source: meta.source ?? (explicitId ? "hub" : undefined) });
|
|
8838
|
+
}
|
|
8809
8839
|
return { id: destId };
|
|
8810
8840
|
} finally {
|
|
8811
8841
|
await rm(tmpDir, { recursive: true, force: true });
|
|
@@ -8864,7 +8894,7 @@ async function installSkillFromSkillMd(buffer, filename, explicitId) {
|
|
|
8864
8894
|
await writeFile3(join5(destDir, "SKILL.md"), text, "utf-8");
|
|
8865
8895
|
return { id };
|
|
8866
8896
|
}
|
|
8867
|
-
var SYSTEM_SKILL_IDS, DEFAULT_SYSTEM_SKILL_IDS;
|
|
8897
|
+
var SYSTEM_SKILL_IDS, DEFAULT_SYSTEM_SKILL_IDS, SULALA_META_FILE = ".sulala-meta.json";
|
|
8868
8898
|
var init_loader = __esm(() => {
|
|
8869
8899
|
init_config();
|
|
8870
8900
|
init_tool_registry();
|
|
@@ -23180,8 +23210,8 @@ function formatLLMErrorForUser(err) {
|
|
|
23180
23210
|
}
|
|
23181
23211
|
function runAgentInner(options) {
|
|
23182
23212
|
return (async () => {
|
|
23183
|
-
const { task, agent, conversationHistory = [] } = options;
|
|
23184
|
-
const maxTurns = agent.limits?.max_turns ?? 10;
|
|
23213
|
+
const { task, agent, conversationHistory = [], maxTurnsOverride } = options;
|
|
23214
|
+
const maxTurns = maxTurnsOverride ?? agent.limits?.max_turns ?? 10;
|
|
23185
23215
|
const maxTokens = agent.limits?.max_tokens;
|
|
23186
23216
|
await ensureWorkspace(agent.id);
|
|
23187
23217
|
await loadSkillsForAgent(agent);
|
|
@@ -23415,8 +23445,8 @@ async function runAgent(options) {
|
|
|
23415
23445
|
return runAgentInner(options);
|
|
23416
23446
|
}
|
|
23417
23447
|
async function runAgentStream(options, onEvent) {
|
|
23418
|
-
const { task, agent, conversationHistory = [] } = options;
|
|
23419
|
-
const maxTurns = agent.limits?.max_turns ?? 10;
|
|
23448
|
+
const { task, agent, conversationHistory = [], maxTurnsOverride } = options;
|
|
23449
|
+
const maxTurns = maxTurnsOverride ?? agent.limits?.max_turns ?? 10;
|
|
23420
23450
|
const maxTokens = agent.limits?.max_tokens;
|
|
23421
23451
|
await ensureWorkspace(agent.id);
|
|
23422
23452
|
await loadSkillsForAgent(agent);
|
|
@@ -25375,44 +25405,87 @@ var require_node_cron = __commonJS((exports) => {
|
|
|
25375
25405
|
});
|
|
25376
25406
|
|
|
25377
25407
|
// src/core/graphs.ts
|
|
25378
|
-
import { readFile as readFile7, readdir as readdir4, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
|
|
25408
|
+
import { readFile as readFile7, readdir as readdir4, writeFile as writeFile4, mkdir as mkdir5, copyFile, unlink as unlink2 } from "fs/promises";
|
|
25379
25409
|
import { join as join9 } from "path";
|
|
25410
|
+
import { existsSync as existsSync3 } from "fs";
|
|
25380
25411
|
function getGraphsDir() {
|
|
25381
25412
|
return process.env.AGENT_OS_GRAPHS_DIR || DEFAULT_GRAPHS_DIR;
|
|
25382
25413
|
}
|
|
25414
|
+
function getSeedGraphsDir() {
|
|
25415
|
+
if (process.env.AGENT_OS_SEED_GRAPHS_DIR)
|
|
25416
|
+
return process.env.AGENT_OS_SEED_GRAPHS_DIR;
|
|
25417
|
+
const fromDist = join9(import.meta.dir, "..", "data", "graphs");
|
|
25418
|
+
const fromSrc = join9(import.meta.dir, "..", "..", "data", "graphs");
|
|
25419
|
+
if (existsSync3(fromDist))
|
|
25420
|
+
return fromDist;
|
|
25421
|
+
if (existsSync3(fromSrc))
|
|
25422
|
+
return fromSrc;
|
|
25423
|
+
return join9(process.cwd(), "data", "graphs");
|
|
25424
|
+
}
|
|
25383
25425
|
async function listGraphs() {
|
|
25384
25426
|
const dir = getGraphsDir();
|
|
25427
|
+
let entries;
|
|
25385
25428
|
try {
|
|
25386
|
-
|
|
25387
|
-
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
|
|
25429
|
+
entries = await readdir4(dir);
|
|
25430
|
+
} catch (err) {
|
|
25431
|
+
if (err.code !== "ENOENT")
|
|
25432
|
+
throw err;
|
|
25433
|
+
entries = [];
|
|
25434
|
+
}
|
|
25435
|
+
if (entries.length === 0) {
|
|
25436
|
+
const seedDir = getSeedGraphsDir();
|
|
25437
|
+
try {
|
|
25438
|
+
const seedEntries = await readdir4(seedDir);
|
|
25439
|
+
await mkdir5(dir, { recursive: true });
|
|
25440
|
+
for (const name of seedEntries) {
|
|
25441
|
+
if (name.endsWith(".json")) {
|
|
25442
|
+
await copyFile(join9(seedDir, name), join9(dir, name));
|
|
25443
|
+
}
|
|
25393
25444
|
}
|
|
25445
|
+
entries = await readdir4(dir);
|
|
25446
|
+
} catch {}
|
|
25447
|
+
}
|
|
25448
|
+
const summaries = [];
|
|
25449
|
+
for (const name of entries) {
|
|
25450
|
+
if (name.endsWith(".json")) {
|
|
25451
|
+
const id = name.replace(/\.graph\.json$/, "").replace(/\.json$/, "");
|
|
25452
|
+
if (id)
|
|
25453
|
+
summaries.push({ id });
|
|
25394
25454
|
}
|
|
25395
|
-
return summaries;
|
|
25396
|
-
} catch (err) {
|
|
25397
|
-
if (err.code === "ENOENT")
|
|
25398
|
-
return [];
|
|
25399
|
-
throw err;
|
|
25400
25455
|
}
|
|
25456
|
+
return summaries;
|
|
25401
25457
|
}
|
|
25402
25458
|
async function loadGraph(id) {
|
|
25403
25459
|
const dir = getGraphsDir();
|
|
25404
25460
|
try {
|
|
25405
|
-
|
|
25461
|
+
let entries;
|
|
25462
|
+
try {
|
|
25463
|
+
entries = await readdir4(dir);
|
|
25464
|
+
} catch (e) {
|
|
25465
|
+
if (e.code !== "ENOENT")
|
|
25466
|
+
throw e;
|
|
25467
|
+
entries = [];
|
|
25468
|
+
}
|
|
25406
25469
|
const file = entries.find((name) => name === `${id}.json` || name === `${id}.graph.json`);
|
|
25407
|
-
if (
|
|
25470
|
+
if (file) {
|
|
25471
|
+
const raw = await readFile7(join9(dir, file), "utf-8");
|
|
25472
|
+
const parsed = JSON.parse(raw);
|
|
25473
|
+
validateGraph(parsed);
|
|
25474
|
+
return parsed;
|
|
25475
|
+
}
|
|
25476
|
+
const seedDir = getSeedGraphsDir();
|
|
25477
|
+
const seedFile = `${id}.json`;
|
|
25478
|
+
try {
|
|
25479
|
+
const raw = await readFile7(join9(seedDir, seedFile), "utf-8");
|
|
25480
|
+
const parsed = JSON.parse(raw);
|
|
25481
|
+
validateGraph(parsed);
|
|
25482
|
+
await mkdir5(dir, { recursive: true });
|
|
25483
|
+
await writeFile4(join9(dir, seedFile), raw, "utf-8");
|
|
25484
|
+
return parsed;
|
|
25485
|
+
} catch {
|
|
25408
25486
|
return null;
|
|
25409
|
-
|
|
25410
|
-
const parsed = JSON.parse(raw);
|
|
25411
|
-
validateGraph(parsed);
|
|
25412
|
-
return parsed;
|
|
25487
|
+
}
|
|
25413
25488
|
} catch (err) {
|
|
25414
|
-
if (err.code === "ENOENT")
|
|
25415
|
-
return null;
|
|
25416
25489
|
console.error("[graphs] Failed to load graph:", err);
|
|
25417
25490
|
return null;
|
|
25418
25491
|
}
|
|
@@ -25424,6 +25497,22 @@ async function saveGraph(graph) {
|
|
|
25424
25497
|
const path = join9(dir, `${graph.id}.json`);
|
|
25425
25498
|
await writeFile4(path, JSON.stringify(graph, null, 2), "utf-8");
|
|
25426
25499
|
}
|
|
25500
|
+
async function deleteGraph(id) {
|
|
25501
|
+
if (!id || typeof id !== "string")
|
|
25502
|
+
throw new Error("Graph id required");
|
|
25503
|
+
const dir = getGraphsDir();
|
|
25504
|
+
let entries;
|
|
25505
|
+
try {
|
|
25506
|
+
entries = await readdir4(dir);
|
|
25507
|
+
} catch (err) {
|
|
25508
|
+
if (err.code !== "ENOENT")
|
|
25509
|
+
throw err;
|
|
25510
|
+
return;
|
|
25511
|
+
}
|
|
25512
|
+
const file = entries.find((name) => name === `${id}.json` || name === `${id}.graph.json`);
|
|
25513
|
+
if (file)
|
|
25514
|
+
await unlink2(join9(dir, file));
|
|
25515
|
+
}
|
|
25427
25516
|
function validateGraph(graph) {
|
|
25428
25517
|
if (!graph || typeof graph !== "object") {
|
|
25429
25518
|
throw new Error("Graph must be an object");
|
|
@@ -25490,7 +25579,7 @@ function getPredecessors(graph) {
|
|
|
25490
25579
|
return pred;
|
|
25491
25580
|
}
|
|
25492
25581
|
async function runGraph(options) {
|
|
25493
|
-
const { graph, input } = options;
|
|
25582
|
+
const { graph, input, max_turns_per_node = DEFAULT_GRAPH_MAX_TURNS_PER_NODE } = options;
|
|
25494
25583
|
const levels = topologicalLevels(graph);
|
|
25495
25584
|
const predecessors = getPredecessors(graph);
|
|
25496
25585
|
const outputs = new Map;
|
|
@@ -25515,7 +25604,11 @@ async function runGraph(options) {
|
|
|
25515
25604
|
const taskInput = preds.length === 0 ? input : preds.map((p) => outputs.get(p) ?? "").filter(Boolean).join(`
|
|
25516
25605
|
|
|
25517
25606
|
`) || input;
|
|
25518
|
-
const result = await runAgent({
|
|
25607
|
+
const result = await runAgent({
|
|
25608
|
+
agent,
|
|
25609
|
+
task: taskInput,
|
|
25610
|
+
maxTurnsOverride: max_turns_per_node
|
|
25611
|
+
});
|
|
25519
25612
|
outputs.set(node.id, result.output || "");
|
|
25520
25613
|
return {
|
|
25521
25614
|
node_id: node.id,
|
|
@@ -25538,7 +25631,7 @@ async function runGraph(options) {
|
|
|
25538
25631
|
};
|
|
25539
25632
|
}
|
|
25540
25633
|
async function runGraphStream(options, onEvent) {
|
|
25541
|
-
const { graph, input } = options;
|
|
25634
|
+
const { graph, input, max_turns_per_node = DEFAULT_GRAPH_MAX_TURNS_PER_NODE } = options;
|
|
25542
25635
|
const levels = topologicalLevels(graph);
|
|
25543
25636
|
const predecessors = getPredecessors(graph);
|
|
25544
25637
|
const outputs = new Map;
|
|
@@ -25564,7 +25657,11 @@ async function runGraphStream(options, onEvent) {
|
|
|
25564
25657
|
const taskInput = preds.length === 0 ? input : preds.map((p) => outputs.get(p) ?? "").filter(Boolean).join(`
|
|
25565
25658
|
|
|
25566
25659
|
`) || input;
|
|
25567
|
-
const result = await runAgent({
|
|
25660
|
+
const result = await runAgent({
|
|
25661
|
+
agent,
|
|
25662
|
+
task: taskInput,
|
|
25663
|
+
maxTurnsOverride: max_turns_per_node
|
|
25664
|
+
});
|
|
25568
25665
|
outputs.set(node.id, result.output || "");
|
|
25569
25666
|
const payload = {
|
|
25570
25667
|
node_id: node.id,
|
|
@@ -25592,7 +25689,7 @@ async function runGraphStream(options, onEvent) {
|
|
|
25592
25689
|
throw err;
|
|
25593
25690
|
}
|
|
25594
25691
|
}
|
|
25595
|
-
var DEFAULT_GRAPHS_DIR;
|
|
25692
|
+
var DEFAULT_GRAPHS_DIR, DEFAULT_GRAPH_MAX_TURNS_PER_NODE = 5;
|
|
25596
25693
|
var init_graphs = __esm(() => {
|
|
25597
25694
|
init_agent_registry();
|
|
25598
25695
|
init_runtime();
|
|
@@ -26028,8 +26125,10 @@ async function handleSkillInstall(req) {
|
|
|
26028
26125
|
return jsonResponse({ error: "Provide path or url" }, 400);
|
|
26029
26126
|
}
|
|
26030
26127
|
const slug = typeof body.slug === "string" && body.slug.trim() !== "" ? body.slug.trim() : undefined;
|
|
26128
|
+
const version2 = typeof body.version === "string" && body.version.trim() !== "" ? body.version.trim() : undefined;
|
|
26129
|
+
const meta = version2 || slug ? { version: version2, source: slug ? "hub" : undefined } : undefined;
|
|
26031
26130
|
try {
|
|
26032
|
-
const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug);
|
|
26131
|
+
const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug, meta);
|
|
26033
26132
|
return jsonResponse({ skill: result }, 201);
|
|
26034
26133
|
} catch (err) {
|
|
26035
26134
|
const msg = errorMessage(err);
|
|
@@ -29609,7 +29708,7 @@ __export(exports_server, {
|
|
|
29609
29708
|
startServer: () => startServer
|
|
29610
29709
|
});
|
|
29611
29710
|
import { join as join13, dirname as dirname2, resolve as resolve4 } from "path";
|
|
29612
|
-
import { mkdirSync, existsSync as
|
|
29711
|
+
import { mkdirSync, existsSync as existsSync4 } from "fs";
|
|
29613
29712
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
29614
29713
|
function isAuthExempt(pathname, method) {
|
|
29615
29714
|
if (method === "OPTIONS")
|
|
@@ -29683,7 +29782,7 @@ function broadcastEvent(event) {
|
|
|
29683
29782
|
}
|
|
29684
29783
|
}
|
|
29685
29784
|
function serveDashboard(pathname) {
|
|
29686
|
-
if (!
|
|
29785
|
+
if (!existsSync4(DASHBOARD_DIST) || !existsSync4(join13(DASHBOARD_DIST, "index.html"))) {
|
|
29687
29786
|
return null;
|
|
29688
29787
|
}
|
|
29689
29788
|
const decoded = decodeURIComponent(pathname);
|
|
@@ -29692,7 +29791,7 @@ function serveDashboard(pathname) {
|
|
|
29692
29791
|
}
|
|
29693
29792
|
const subpath = decoded === "/" ? "index.html" : decoded.slice(1);
|
|
29694
29793
|
const filePath = join13(DASHBOARD_DIST, subpath);
|
|
29695
|
-
if (
|
|
29794
|
+
if (existsSync4(filePath)) {
|
|
29696
29795
|
const file = Bun.file(filePath);
|
|
29697
29796
|
const ext = subpath.split(".").pop() ?? "";
|
|
29698
29797
|
const mime = {
|
|
@@ -29713,7 +29812,7 @@ function serveDashboard(pathname) {
|
|
|
29713
29812
|
});
|
|
29714
29813
|
}
|
|
29715
29814
|
const indexPath = join13(DASHBOARD_DIST, "index.html");
|
|
29716
|
-
if (
|
|
29815
|
+
if (existsSync4(indexPath)) {
|
|
29717
29816
|
return new Response(Bun.file(indexPath), {
|
|
29718
29817
|
headers: { "Content-Type": "text/html" }
|
|
29719
29818
|
});
|
|
@@ -29930,6 +30029,16 @@ function createRoutes() {
|
|
|
29930
30029
|
const msg = e instanceof Error ? e.message : String(e);
|
|
29931
30030
|
return jsonResponse({ error: msg }, 400);
|
|
29932
30031
|
}
|
|
30032
|
+
},
|
|
30033
|
+
DELETE: async (req) => {
|
|
30034
|
+
const id = decodeURIComponent(req.params.id);
|
|
30035
|
+
try {
|
|
30036
|
+
await deleteGraph(id);
|
|
30037
|
+
return Response.json({ ok: true }, { headers: CORS_HEADERS });
|
|
30038
|
+
} catch (e) {
|
|
30039
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
30040
|
+
return jsonResponse({ error: msg }, 400);
|
|
30041
|
+
}
|
|
29933
30042
|
}
|
|
29934
30043
|
},
|
|
29935
30044
|
"/api/memory/write": {
|
|
@@ -30150,7 +30259,7 @@ async function startServer() {
|
|
|
30150
30259
|
await loadPlugins();
|
|
30151
30260
|
await seedAgentsIfEmpty();
|
|
30152
30261
|
const dashboardSecret = await getDashboardSecret();
|
|
30153
|
-
const dashboardMissing = !
|
|
30262
|
+
const dashboardMissing = !existsSync4(DASHBOARD_DIST) || !existsSync4(join13(DASHBOARD_DIST, "index.html"));
|
|
30154
30263
|
if (dashboardMissing) {
|
|
30155
30264
|
console.warn(`[sulala] Dashboard not found at ${DASHBOARD_DIST}. From package root run: cd dashboard && npm run build. If using a global install, reinstall: bun install -g @sulala/agent-os@latest`);
|
|
30156
30265
|
}
|
|
@@ -30239,7 +30348,7 @@ var init_server = __esm(() => {
|
|
|
30239
30348
|
const root = join13(import.meta.dir, "..");
|
|
30240
30349
|
const a = resolve4(join13(root, "dashboard-dist"));
|
|
30241
30350
|
const b = resolve4(join13(root, "dashboard", "dist"));
|
|
30242
|
-
if (
|
|
30351
|
+
if (existsSync4(a) && existsSync4(join13(a, "index.html")))
|
|
30243
30352
|
return a;
|
|
30244
30353
|
return b;
|
|
30245
30354
|
})();
|
|
@@ -30272,8 +30381,8 @@ init_loader();
|
|
|
30272
30381
|
init_agent_registry();
|
|
30273
30382
|
init_runtime();
|
|
30274
30383
|
import { join as join14, dirname as dirname3 } from "path";
|
|
30275
|
-
import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir8, unlink as
|
|
30276
|
-
import { existsSync as
|
|
30384
|
+
import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir8, unlink as unlink3 } from "fs/promises";
|
|
30385
|
+
import { existsSync as existsSync5, readFileSync } from "fs";
|
|
30277
30386
|
var PID_FILE = join14(getAgentOsHome(), "sulala.pid");
|
|
30278
30387
|
var DEFAULT_PORT = 3010;
|
|
30279
30388
|
function openDashboard() {
|
|
@@ -30312,6 +30421,7 @@ Commands:
|
|
|
30312
30421
|
onboard First-time setup: create config, seed agents & skills, open dashboard
|
|
30313
30422
|
update Update package from npm and system agents/skills
|
|
30314
30423
|
run <agent_id> <task> Run an agent with a one-off task
|
|
30424
|
+
skill install <slug> Install a skill from the store by slug (e.g. crm-hubspot)
|
|
30315
30425
|
dashboard-token [--regenerate] Show or regenerate dashboard login token (copy to log in)
|
|
30316
30426
|
|
|
30317
30427
|
Examples:
|
|
@@ -30322,6 +30432,7 @@ Examples:
|
|
|
30322
30432
|
sulala onboard
|
|
30323
30433
|
sulala update
|
|
30324
30434
|
sulala run echo_agent What is 2+2?
|
|
30435
|
+
sulala skill install crm-hubspot
|
|
30325
30436
|
sulala dashboard-token
|
|
30326
30437
|
sulala dashboard-token --regenerate
|
|
30327
30438
|
`);
|
|
@@ -30336,7 +30447,7 @@ async function cmdStart(args) {
|
|
|
30336
30447
|
await mkdir8(getAgentOsHome(), { recursive: true });
|
|
30337
30448
|
const projectRoot = join14(import.meta.dir, "..");
|
|
30338
30449
|
const distEntry = join14(projectRoot, "dist", "index.js");
|
|
30339
|
-
const serverEntry =
|
|
30450
|
+
const serverEntry = existsSync5(distEntry) ? "dist/index.js" : "src/index.ts";
|
|
30340
30451
|
const child = Bun.spawn(["bun", "run", serverEntry], {
|
|
30341
30452
|
cwd: projectRoot,
|
|
30342
30453
|
stdout: "ignore",
|
|
@@ -30353,7 +30464,7 @@ async function cmdStart(args) {
|
|
|
30353
30464
|
await startServer2();
|
|
30354
30465
|
}
|
|
30355
30466
|
async function cmdStop() {
|
|
30356
|
-
if (!
|
|
30467
|
+
if (!existsSync5(PID_FILE)) {
|
|
30357
30468
|
console.error("No PID file found. Is the server running with 'sulala start --daemon'?");
|
|
30358
30469
|
process.exit(1);
|
|
30359
30470
|
}
|
|
@@ -30374,11 +30485,11 @@ async function cmdStop() {
|
|
|
30374
30485
|
process.exit(1);
|
|
30375
30486
|
}
|
|
30376
30487
|
}
|
|
30377
|
-
await
|
|
30488
|
+
await unlink3(PID_FILE).catch(() => {});
|
|
30378
30489
|
console.log("Sulala server stopped.");
|
|
30379
30490
|
}
|
|
30380
30491
|
async function startServerDaemonIfNeeded() {
|
|
30381
|
-
if (
|
|
30492
|
+
if (existsSync5(PID_FILE)) {
|
|
30382
30493
|
try {
|
|
30383
30494
|
const pidStr = await readFile10(PID_FILE, "utf-8");
|
|
30384
30495
|
const pid = parseInt(pidStr.trim(), 10);
|
|
@@ -30391,7 +30502,7 @@ async function startServerDaemonIfNeeded() {
|
|
|
30391
30502
|
await mkdir8(getAgentOsHome(), { recursive: true });
|
|
30392
30503
|
const projectRoot = join14(import.meta.dir, "..");
|
|
30393
30504
|
const distEntry = join14(projectRoot, "dist", "index.js");
|
|
30394
|
-
const serverEntry =
|
|
30505
|
+
const serverEntry = existsSync5(distEntry) ? "dist/index.js" : "src/index.ts";
|
|
30395
30506
|
const child = Bun.spawn(["bun", "run", serverEntry], {
|
|
30396
30507
|
cwd: projectRoot,
|
|
30397
30508
|
stdout: "ignore",
|
|
@@ -30408,7 +30519,7 @@ async function cmdOnboard() {
|
|
|
30408
30519
|
await mkdir8(home, { recursive: true });
|
|
30409
30520
|
await mkdir8(dirname3(getMemoryDbPath()), { recursive: true });
|
|
30410
30521
|
const configPath = join14(home, "config.json");
|
|
30411
|
-
if (!
|
|
30522
|
+
if (!existsSync5(configPath)) {
|
|
30412
30523
|
const dashboardSecret = generateDashboardSecret();
|
|
30413
30524
|
await writeFile6(configPath, JSON.stringify({
|
|
30414
30525
|
provider: null,
|
|
@@ -30471,7 +30582,7 @@ async function cmdUpdate() {
|
|
|
30471
30582
|
console.warn("Could not update package from npm (run 'bun install -g @sulala/agent-os@latest' manually):", err.trim() || out.trim());
|
|
30472
30583
|
}
|
|
30473
30584
|
const dbPath = getMemoryDbPath();
|
|
30474
|
-
if (!
|
|
30585
|
+
if (!existsSync5(dbPath)) {
|
|
30475
30586
|
console.log("No database found. Run 'sulala onboard' to set up agents and skills.");
|
|
30476
30587
|
return;
|
|
30477
30588
|
}
|
|
@@ -30511,7 +30622,7 @@ async function cmdRun(args) {
|
|
|
30511
30622
|
process.exit(1);
|
|
30512
30623
|
}
|
|
30513
30624
|
const dbPath = getMemoryDbPath();
|
|
30514
|
-
if (
|
|
30625
|
+
if (existsSync5(dbPath)) {
|
|
30515
30626
|
setAgentStore(new MemoryStore(dbPath));
|
|
30516
30627
|
}
|
|
30517
30628
|
const agent = await getAgent(agentId);
|
|
@@ -30528,6 +30639,57 @@ async function cmdRun(args) {
|
|
|
30528
30639
|
process.exit(1);
|
|
30529
30640
|
}
|
|
30530
30641
|
}
|
|
30642
|
+
async function cmdSkill(args) {
|
|
30643
|
+
const sub = args[0]?.toLowerCase();
|
|
30644
|
+
const rest = args.slice(1);
|
|
30645
|
+
if (sub === "install") {
|
|
30646
|
+
await cmdSkillInstall(rest);
|
|
30647
|
+
return;
|
|
30648
|
+
}
|
|
30649
|
+
if (!sub || sub === "help" || sub === "-h" || sub === "--help") {
|
|
30650
|
+
console.log("Usage: sulala skill install <slug>");
|
|
30651
|
+
console.log(" Install a skill from the store by slug (e.g. crm-hubspot).");
|
|
30652
|
+
console.log(" Installs the latest version; you can pin a specific version in your skill config if needed.");
|
|
30653
|
+
console.log("");
|
|
30654
|
+
console.log("Example: sulala skill install crm-hubspot");
|
|
30655
|
+
return;
|
|
30656
|
+
}
|
|
30657
|
+
console.error(`Unknown subcommand: skill ${sub}`);
|
|
30658
|
+
console.error("Usage: sulala skill install <slug>");
|
|
30659
|
+
process.exit(1);
|
|
30660
|
+
}
|
|
30661
|
+
async function cmdSkillInstall(args) {
|
|
30662
|
+
const slug = args[0]?.trim();
|
|
30663
|
+
if (!slug) {
|
|
30664
|
+
console.error("Usage: sulala skill install <slug>");
|
|
30665
|
+
console.error("Example: sulala skill install crm-hubspot");
|
|
30666
|
+
process.exit(1);
|
|
30667
|
+
}
|
|
30668
|
+
const { skills, storeBase } = await getStoreRegistry();
|
|
30669
|
+
if (!storeBase) {
|
|
30670
|
+
console.error("Could not reach the skills store. Check config (skills_registry_url) or run 'sulala onboard' first.");
|
|
30671
|
+
process.exit(1);
|
|
30672
|
+
}
|
|
30673
|
+
const entry = skills.find((s) => s.slug === slug);
|
|
30674
|
+
if (!entry) {
|
|
30675
|
+
console.error(`Skill '${slug}' not found in the store. Check the slug at hub.sulala.ai or install from the dashboard (Skills page).`);
|
|
30676
|
+
process.exit(1);
|
|
30677
|
+
}
|
|
30678
|
+
const downloadUrl = entry.downloadUrl ?? `${storeBase}/api/sulalahub/skills/${encodeURIComponent(slug)}/download`;
|
|
30679
|
+
const version2 = entry.version;
|
|
30680
|
+
try {
|
|
30681
|
+
const { id } = await installSkillFromUrl(downloadUrl, slug, {
|
|
30682
|
+
version: version2 ?? undefined,
|
|
30683
|
+
source: "hub"
|
|
30684
|
+
});
|
|
30685
|
+
const verStr = version2 ? ` (v${version2})` : "";
|
|
30686
|
+
console.log(`Installed ${id}${verStr}. Add it to your agent in the dashboard (Edit agent \u2192 Skills).`);
|
|
30687
|
+
} catch (err) {
|
|
30688
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
30689
|
+
console.error("Install failed:", msg);
|
|
30690
|
+
process.exit(1);
|
|
30691
|
+
}
|
|
30692
|
+
}
|
|
30531
30693
|
async function main() {
|
|
30532
30694
|
if (!process.versions.bun) {
|
|
30533
30695
|
console.error("Sulala CLI requires Bun. Use: bun run src/cli.ts");
|
|
@@ -30557,6 +30719,9 @@ async function main() {
|
|
|
30557
30719
|
case "run":
|
|
30558
30720
|
await cmdRun(rest);
|
|
30559
30721
|
break;
|
|
30722
|
+
case "skill":
|
|
30723
|
+
await cmdSkill(rest);
|
|
30724
|
+
break;
|
|
30560
30725
|
case "dashboard-token":
|
|
30561
30726
|
await cmdDashboardToken(rest);
|
|
30562
30727
|
break;
|