@sulala/agent-os 0.1.19 → 0.1.20
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/README.md +111 -37
- package/dashboard-dist/assets/index-B3C3mWft.js +72 -0
- package/dashboard-dist/index.html +1 -1
- package/dist/cli.js +155 -59
- package/dist/index.js +150 -51
- package/package.json +4 -2
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/png" href="/logo_dark.png" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Sulala Agent Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-B3C3mWft.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-Cdtyuhuu.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,8 @@ __export(exports_config, {
|
|
|
43
43
|
getSkillsDir: () => getSkillsDir,
|
|
44
44
|
getSkillConfigPath: () => getSkillConfigPath,
|
|
45
45
|
getSeedSkillsDir: () => getSeedSkillsDir,
|
|
46
|
+
getMemoryDbPath: () => getMemoryDbPath,
|
|
47
|
+
getDefaultModelForAvailableProvider: () => getDefaultModelForAvailableProvider,
|
|
46
48
|
getDashboardSecretFromConfig: () => getDashboardSecretFromConfig,
|
|
47
49
|
getDashboardSecret: () => getDashboardSecret,
|
|
48
50
|
getConfigsDir: () => getConfigsDir,
|
|
@@ -53,7 +55,7 @@ __export(exports_config, {
|
|
|
53
55
|
});
|
|
54
56
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
55
57
|
import { existsSync } from "fs";
|
|
56
|
-
import { join, resolve, relative } from "path";
|
|
58
|
+
import { join, resolve, relative, dirname } from "path";
|
|
57
59
|
import { randomBytes } from "crypto";
|
|
58
60
|
function getAgentOsHome() {
|
|
59
61
|
return process.env.AGENT_OS_HOME || DEFAULT_HOME;
|
|
@@ -61,6 +63,14 @@ function getAgentOsHome() {
|
|
|
61
63
|
function getConfigPath() {
|
|
62
64
|
return join(getAgentOsHome(), "config.json");
|
|
63
65
|
}
|
|
66
|
+
function getMemoryDbPath() {
|
|
67
|
+
if (process.env.AGENT_MEMORY_DB_PATH)
|
|
68
|
+
return process.env.AGENT_MEMORY_DB_PATH;
|
|
69
|
+
const agentsDir = process.env.AGENT_OS_AGENTS_DIR;
|
|
70
|
+
if (agentsDir)
|
|
71
|
+
return join(dirname(agentsDir), "database.db");
|
|
72
|
+
return join(getAgentOsHome(), "database.db");
|
|
73
|
+
}
|
|
64
74
|
async function readConfig() {
|
|
65
75
|
const path = getConfigPath();
|
|
66
76
|
try {
|
|
@@ -120,6 +130,22 @@ async function readConfig() {
|
|
|
120
130
|
}
|
|
121
131
|
return {};
|
|
122
132
|
}
|
|
133
|
+
async function getDefaultModelForAvailableProvider() {
|
|
134
|
+
const config = await readConfig();
|
|
135
|
+
const googleKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY?.trim() || config.google_api_key?.trim();
|
|
136
|
+
if (googleKey)
|
|
137
|
+
return DEFAULT_MODEL_BY_PROVIDER.google;
|
|
138
|
+
const anthropicKey = process.env.ANTHROPIC_API_KEY?.trim() || config.anthropic_api_key?.trim();
|
|
139
|
+
if (anthropicKey)
|
|
140
|
+
return DEFAULT_MODEL_BY_PROVIDER.anthropic;
|
|
141
|
+
const openrouterKey = process.env.OPENROUTER_API_KEY?.trim() || config.openrouter_api_key?.trim() || (config.provider === "openrouter" ? config.api_key?.trim() : undefined);
|
|
142
|
+
if (openrouterKey)
|
|
143
|
+
return DEFAULT_MODEL_BY_PROVIDER.openrouter;
|
|
144
|
+
const openaiKey = process.env.OPENAI_API_KEY?.trim() || config.openai_api_key?.trim() || (config.provider === "openai" ? config.api_key?.trim() : undefined);
|
|
145
|
+
if (openaiKey)
|
|
146
|
+
return DEFAULT_MODEL_BY_PROVIDER.openai;
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
123
149
|
async function writeConfig(updates) {
|
|
124
150
|
const current = await readConfig();
|
|
125
151
|
const merged = {
|
|
@@ -269,9 +295,15 @@ function resolveInWorkspace(workspaceDir, relativePath) {
|
|
|
269
295
|
}
|
|
270
296
|
return resolved;
|
|
271
297
|
}
|
|
272
|
-
var DEFAULT_HOME, DEFAULT_SKILLS_REGISTRY_URL = "https://hub.sulala.ai/api/sulalahub/registry";
|
|
298
|
+
var DEFAULT_HOME, DEFAULT_MODEL_BY_PROVIDER, DEFAULT_SKILLS_REGISTRY_URL = "https://hub.sulala.ai/api/sulalahub/registry";
|
|
273
299
|
var init_config = __esm(() => {
|
|
274
300
|
DEFAULT_HOME = join(process.env.HOME || process.env.USERPROFILE || "~", ".agent-os");
|
|
301
|
+
DEFAULT_MODEL_BY_PROVIDER = {
|
|
302
|
+
google: "gemini-2.0-flash",
|
|
303
|
+
anthropic: "claude-sonnet-4-6",
|
|
304
|
+
openrouter: "openai/gpt-4o-mini",
|
|
305
|
+
openai: "gpt-4o-mini"
|
|
306
|
+
};
|
|
275
307
|
});
|
|
276
308
|
|
|
277
309
|
// src/db/memory-store.ts
|
|
@@ -629,7 +661,7 @@ function getSeedAgentsDir() {
|
|
|
629
661
|
return fromSrc;
|
|
630
662
|
return join2(process.cwd(), "data", "agents");
|
|
631
663
|
}
|
|
632
|
-
async function insertSeedAgentFromFile(seedDir, name, skipIfExists) {
|
|
664
|
+
async function insertSeedAgentFromFile(seedDir, name, skipIfExists, modelOverride) {
|
|
633
665
|
if (!agentStore)
|
|
634
666
|
return false;
|
|
635
667
|
const path = join2(seedDir, name);
|
|
@@ -640,10 +672,12 @@ async function insertSeedAgentFromFile(seedDir, name, skipIfExists) {
|
|
|
640
672
|
return false;
|
|
641
673
|
if (skipIfExists && agentStore.getAgentById(id))
|
|
642
674
|
return false;
|
|
675
|
+
const seedModel = String(parsed.model ?? "").trim();
|
|
676
|
+
const model = modelOverride?.trim() || seedModel || "gpt-4o-mini";
|
|
643
677
|
const payload = {
|
|
644
678
|
id: String(parsed.id).trim(),
|
|
645
679
|
name: String(parsed.name ?? "").trim(),
|
|
646
|
-
model
|
|
680
|
+
model,
|
|
647
681
|
description: parsed.description != null ? String(parsed.description) : undefined,
|
|
648
682
|
personality: parsed.personality != null ? String(parsed.personality) : undefined,
|
|
649
683
|
skills: ensureMemoryInSkills(Array.isArray(parsed.skills) ? parsed.skills : undefined),
|
|
@@ -676,11 +710,12 @@ async function seedAgentsIfEmpty() {
|
|
|
676
710
|
} catch {
|
|
677
711
|
return;
|
|
678
712
|
}
|
|
713
|
+
const modelOverride = await getDefaultModelForAvailableProvider();
|
|
679
714
|
for (const name of entries) {
|
|
680
715
|
if (!name.endsWith(".json"))
|
|
681
716
|
continue;
|
|
682
717
|
try {
|
|
683
|
-
await insertSeedAgentFromFile(seedDir, name, false);
|
|
718
|
+
await insertSeedAgentFromFile(seedDir, name, false, modelOverride);
|
|
684
719
|
} catch (err) {
|
|
685
720
|
console.error(`[agent-registry] Failed to seed ${name}:`, err);
|
|
686
721
|
}
|
|
@@ -696,12 +731,23 @@ async function installSystemAgents() {
|
|
|
696
731
|
} catch (err) {
|
|
697
732
|
throw new Error(`Seed directory not found: ${seedDir}`);
|
|
698
733
|
}
|
|
734
|
+
const modelOverride = await getDefaultModelForAvailableProvider();
|
|
699
735
|
let installed = 0;
|
|
700
736
|
for (const name of entries) {
|
|
701
737
|
if (!name.endsWith(".json"))
|
|
702
738
|
continue;
|
|
703
739
|
try {
|
|
704
|
-
const
|
|
740
|
+
const path = join2(seedDir, name);
|
|
741
|
+
const raw = await readFile2(path, "utf-8");
|
|
742
|
+
const parsed = JSON.parse(raw);
|
|
743
|
+
const id = typeof parsed?.id === "string" ? parsed.id.trim() : null;
|
|
744
|
+
if (id && modelOverride) {
|
|
745
|
+
const existing = agentStore.getAgentById(id);
|
|
746
|
+
if (existing) {
|
|
747
|
+
await updateAgent(id, { model: modelOverride });
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const added = await insertSeedAgentFromFile(seedDir, name, true, modelOverride);
|
|
705
751
|
if (added)
|
|
706
752
|
installed += 1;
|
|
707
753
|
} catch (err) {
|
|
@@ -15622,13 +15668,20 @@ var exports_server = {};
|
|
|
15622
15668
|
__export(exports_server, {
|
|
15623
15669
|
startServer: () => startServer
|
|
15624
15670
|
});
|
|
15625
|
-
import { join as join13, dirname, resolve as resolve4 } from "path";
|
|
15671
|
+
import { join as join13, dirname as dirname2, resolve as resolve4 } from "path";
|
|
15626
15672
|
import { mkdirSync, existsSync as existsSync3 } from "fs";
|
|
15673
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
15627
15674
|
function isAuthExempt(pathname, method) {
|
|
15628
15675
|
if (method === "OPTIONS")
|
|
15629
15676
|
return true;
|
|
15630
15677
|
if (method === "GET" && pathname === "/health")
|
|
15631
15678
|
return true;
|
|
15679
|
+
if (method === "GET" && pathname === "/api/bootstrap/dashboard-token")
|
|
15680
|
+
return true;
|
|
15681
|
+
if (method === "GET" && pathname === "/api/bootstrap/workspace-status")
|
|
15682
|
+
return true;
|
|
15683
|
+
if (method === "POST" && pathname === "/api/bootstrap/setup-workspace")
|
|
15684
|
+
return true;
|
|
15632
15685
|
if (method === "POST" && pathname === "/api/channels/telegram/webhook")
|
|
15633
15686
|
return true;
|
|
15634
15687
|
if (method === "POST" && pathname === "/api/channels/slack/webhook")
|
|
@@ -15958,6 +16011,43 @@ function createRoutes() {
|
|
|
15958
16011
|
"/api/conversations/summarize": {
|
|
15959
16012
|
POST: (req) => handleConversationSummarize(req, memoryStore)
|
|
15960
16013
|
},
|
|
16014
|
+
"/api/bootstrap/dashboard-token": {
|
|
16015
|
+
GET: async () => {
|
|
16016
|
+
const config = await readConfig();
|
|
16017
|
+
if (config.onboarding_completed === true) {
|
|
16018
|
+
return Response.json({ error: "Onboarding already completed. Use the login page." }, { status: 403, headers: CORS_HEADERS });
|
|
16019
|
+
}
|
|
16020
|
+
const token = await getDashboardSecret();
|
|
16021
|
+
return jsonResponse({ token });
|
|
16022
|
+
}
|
|
16023
|
+
},
|
|
16024
|
+
"/api/bootstrap/workspace-status": {
|
|
16025
|
+
GET: async () => {
|
|
16026
|
+
try {
|
|
16027
|
+
await mkdir7(getAgentOsHome(), { recursive: true });
|
|
16028
|
+
await mkdir7(dirname2(getMemoryDbPath()), { recursive: true });
|
|
16029
|
+
await loadAgents();
|
|
16030
|
+
return jsonResponse({ ready: true });
|
|
16031
|
+
} catch (err) {
|
|
16032
|
+
const msg = errorMessage(err);
|
|
16033
|
+
return jsonResponse({ ready: false, error: msg }, 200);
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
},
|
|
16037
|
+
"/api/bootstrap/setup-workspace": {
|
|
16038
|
+
POST: async () => {
|
|
16039
|
+
try {
|
|
16040
|
+
await mkdir7(getAgentOsHome(), { recursive: true });
|
|
16041
|
+
await mkdir7(dirname2(getMemoryDbPath()), { recursive: true });
|
|
16042
|
+
await seedAgentsIfEmpty();
|
|
16043
|
+
const { installed } = await installSystemAgents();
|
|
16044
|
+
return jsonResponse({ ok: true, installed });
|
|
16045
|
+
} catch (err) {
|
|
16046
|
+
const msg = errorMessage(err);
|
|
16047
|
+
return jsonResponse({ error: msg }, 400);
|
|
16048
|
+
}
|
|
16049
|
+
}
|
|
16050
|
+
},
|
|
15961
16051
|
"/api/settings": {
|
|
15962
16052
|
GET: (req) => handleSettings(req),
|
|
15963
16053
|
PUT: (req) => handleSettings(req)
|
|
@@ -16111,53 +16201,62 @@ async function startServer() {
|
|
|
16111
16201
|
if (dashboardMissing) {
|
|
16112
16202
|
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`);
|
|
16113
16203
|
}
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
if (
|
|
16125
|
-
|
|
16204
|
+
let server;
|
|
16205
|
+
try {
|
|
16206
|
+
server = Bun.serve({
|
|
16207
|
+
port: PORT,
|
|
16208
|
+
hostname: HOST,
|
|
16209
|
+
idleTimeout: 120,
|
|
16210
|
+
routes: wrapRouteHandlers(createRoutes(), dashboardSecret),
|
|
16211
|
+
fetch(req, server2) {
|
|
16212
|
+
const url = new URL(req.url);
|
|
16213
|
+
if (req.method === "GET" && url.pathname === "/api/events") {
|
|
16214
|
+
if (dashboardSecret) {
|
|
16215
|
+
const token = url.searchParams.get("token")?.trim() ?? getTokenFromRequest(req);
|
|
16216
|
+
if (token !== dashboardSecret) {
|
|
16217
|
+
return authUnauthorizedResponse();
|
|
16218
|
+
}
|
|
16126
16219
|
}
|
|
16220
|
+
if (server2.upgrade(req, { data: undefined }))
|
|
16221
|
+
return;
|
|
16222
|
+
return Response.json({ error: "Upgrade failed" }, { status: 500, headers: CORS_HEADERS });
|
|
16127
16223
|
}
|
|
16128
|
-
if (
|
|
16129
|
-
|
|
16130
|
-
|
|
16131
|
-
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
hint: "From the sulala package root run: cd dashboard && npm run build",
|
|
16141
|
-
hint_global: "If you installed globally, reinstall to get the dashboard: bun install -g @sulala/agent-os@latest"
|
|
16142
|
-
}, { status: 404, headers: CORS_HEADERS });
|
|
16224
|
+
if (req.method === "GET" && !url.pathname.startsWith("/api/")) {
|
|
16225
|
+
const dashboardResponse = serveDashboard(url.pathname);
|
|
16226
|
+
if (dashboardResponse)
|
|
16227
|
+
return dashboardResponse;
|
|
16228
|
+
if (url.pathname === "/" || url.pathname === "") {
|
|
16229
|
+
return Response.json({
|
|
16230
|
+
error: "Dashboard not built",
|
|
16231
|
+
path: DASHBOARD_DIST,
|
|
16232
|
+
hint: "From the sulala package root run: cd dashboard && npm run build",
|
|
16233
|
+
hint_global: "If you installed globally, reinstall to get the dashboard: bun install -g @sulala/agent-os@latest"
|
|
16234
|
+
}, { status: 404, headers: CORS_HEADERS });
|
|
16235
|
+
}
|
|
16143
16236
|
}
|
|
16144
|
-
|
|
16145
|
-
return Response.json({ error: "Not found" }, { status: 404, headers: CORS_HEADERS });
|
|
16146
|
-
},
|
|
16147
|
-
error(error) {
|
|
16148
|
-
console.error(error);
|
|
16149
|
-
return Response.json({ error: "Internal Server Error" }, { status: 500, headers: CORS_HEADERS });
|
|
16150
|
-
},
|
|
16151
|
-
websocket: {
|
|
16152
|
-
open(ws) {
|
|
16153
|
-
wsClients.add(ws);
|
|
16237
|
+
return Response.json({ error: "Not found" }, { status: 404, headers: CORS_HEADERS });
|
|
16154
16238
|
},
|
|
16155
|
-
|
|
16156
|
-
|
|
16239
|
+
error(error) {
|
|
16240
|
+
console.error(error);
|
|
16241
|
+
return Response.json({ error: "Internal Server Error" }, { status: 500, headers: CORS_HEADERS });
|
|
16157
16242
|
},
|
|
16158
|
-
|
|
16243
|
+
websocket: {
|
|
16244
|
+
open(ws) {
|
|
16245
|
+
wsClients.add(ws);
|
|
16246
|
+
},
|
|
16247
|
+
close(ws) {
|
|
16248
|
+
wsClients.delete(ws);
|
|
16249
|
+
},
|
|
16250
|
+
message() {}
|
|
16251
|
+
}
|
|
16252
|
+
});
|
|
16253
|
+
} catch (err) {
|
|
16254
|
+
const e = err;
|
|
16255
|
+
if (e?.code === "EADDRINUSE" || e?.errno === 48) {
|
|
16256
|
+
throw new Error(`Port ${PORT} is already in use. Stop the other process (e.g. sulala stop) or use a different port: PORT=3011 sulala start`);
|
|
16159
16257
|
}
|
|
16160
|
-
|
|
16258
|
+
throw err;
|
|
16259
|
+
}
|
|
16161
16260
|
console.log(`Agent OS server running at ${server.url}`);
|
|
16162
16261
|
startTelegramPolling(memoryStore);
|
|
16163
16262
|
}
|
|
@@ -16203,8 +16302,8 @@ var init_server = __esm(() => {
|
|
|
16203
16302
|
"tool.completed"
|
|
16204
16303
|
];
|
|
16205
16304
|
wsClients = new Set;
|
|
16206
|
-
MEMORY_DB_PATH =
|
|
16207
|
-
mkdirSync(
|
|
16305
|
+
MEMORY_DB_PATH = getMemoryDbPath();
|
|
16306
|
+
mkdirSync(dirname2(MEMORY_DB_PATH), { recursive: true });
|
|
16208
16307
|
memoryStore = new MemoryStore(MEMORY_DB_PATH);
|
|
16209
16308
|
setAgentStore(memoryStore);
|
|
16210
16309
|
for (const type of EVENT_TYPES) {
|
|
@@ -16219,8 +16318,8 @@ init_agent_registry();
|
|
|
16219
16318
|
init_loader();
|
|
16220
16319
|
init_agent_registry();
|
|
16221
16320
|
init_runtime();
|
|
16222
|
-
import { join as join14, dirname as
|
|
16223
|
-
import { readFile as readFile10, writeFile as writeFile6, mkdir as
|
|
16321
|
+
import { join as join14, dirname as dirname3 } from "path";
|
|
16322
|
+
import { readFile as readFile10, writeFile as writeFile6, mkdir as mkdir8, unlink as unlink2 } from "fs/promises";
|
|
16224
16323
|
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
16225
16324
|
var PID_FILE = join14(getAgentOsHome(), "sulala.pid");
|
|
16226
16325
|
var DEFAULT_PORT = 3010;
|
|
@@ -16281,7 +16380,7 @@ async function cmdVersion() {
|
|
|
16281
16380
|
async function cmdStart(args) {
|
|
16282
16381
|
const daemon = args.includes("--daemon");
|
|
16283
16382
|
if (daemon) {
|
|
16284
|
-
await
|
|
16383
|
+
await mkdir8(getAgentOsHome(), { recursive: true });
|
|
16285
16384
|
const projectRoot = join14(import.meta.dir, "..");
|
|
16286
16385
|
const distEntry = join14(projectRoot, "dist", "index.js");
|
|
16287
16386
|
const serverEntry = existsSync4(distEntry) ? "dist/index.js" : "src/index.ts";
|
|
@@ -16325,9 +16424,6 @@ async function cmdStop() {
|
|
|
16325
16424
|
await unlink2(PID_FILE).catch(() => {});
|
|
16326
16425
|
console.log("Sulala server stopped.");
|
|
16327
16426
|
}
|
|
16328
|
-
function getMemoryDbPath() {
|
|
16329
|
-
return process.env.AGENT_MEMORY_DB_PATH ?? join14(getAgentOsHome(), "database.db");
|
|
16330
|
-
}
|
|
16331
16427
|
async function startServerDaemonIfNeeded() {
|
|
16332
16428
|
if (existsSync4(PID_FILE)) {
|
|
16333
16429
|
try {
|
|
@@ -16339,7 +16435,7 @@ async function startServerDaemonIfNeeded() {
|
|
|
16339
16435
|
}
|
|
16340
16436
|
} catch {}
|
|
16341
16437
|
}
|
|
16342
|
-
await
|
|
16438
|
+
await mkdir8(getAgentOsHome(), { recursive: true });
|
|
16343
16439
|
const projectRoot = join14(import.meta.dir, "..");
|
|
16344
16440
|
const distEntry = join14(projectRoot, "dist", "index.js");
|
|
16345
16441
|
const serverEntry = existsSync4(distEntry) ? "dist/index.js" : "src/index.ts";
|
|
@@ -16356,8 +16452,8 @@ async function startServerDaemonIfNeeded() {
|
|
|
16356
16452
|
}
|
|
16357
16453
|
async function cmdOnboard() {
|
|
16358
16454
|
const home = getAgentOsHome();
|
|
16359
|
-
await
|
|
16360
|
-
await
|
|
16455
|
+
await mkdir8(home, { recursive: true });
|
|
16456
|
+
await mkdir8(dirname3(getMemoryDbPath()), { recursive: true });
|
|
16361
16457
|
const configPath = join14(home, "config.json");
|
|
16362
16458
|
if (!existsSync4(configPath)) {
|
|
16363
16459
|
const dashboardSecret = generateDashboardSecret();
|