@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/dist/index.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
  // node_modules/yaml/dist/nodes/identity.js
@@ -11388,7 +11420,7 @@ function getSeedAgentsDir() {
11388
11420
  return fromSrc;
11389
11421
  return join2(process.cwd(), "data", "agents");
11390
11422
  }
11391
- async function insertSeedAgentFromFile(seedDir, name, skipIfExists) {
11423
+ async function insertSeedAgentFromFile(seedDir, name, skipIfExists, modelOverride) {
11392
11424
  if (!agentStore)
11393
11425
  return false;
11394
11426
  const path = join2(seedDir, name);
@@ -11399,10 +11431,12 @@ async function insertSeedAgentFromFile(seedDir, name, skipIfExists) {
11399
11431
  return false;
11400
11432
  if (skipIfExists && agentStore.getAgentById(id))
11401
11433
  return false;
11434
+ const seedModel = String(parsed.model ?? "").trim();
11435
+ const model = modelOverride?.trim() || seedModel || "gpt-4o-mini";
11402
11436
  const payload = {
11403
11437
  id: String(parsed.id).trim(),
11404
11438
  name: String(parsed.name ?? "").trim(),
11405
- model: String(parsed.model ?? "").trim(),
11439
+ model,
11406
11440
  description: parsed.description != null ? String(parsed.description) : undefined,
11407
11441
  personality: parsed.personality != null ? String(parsed.personality) : undefined,
11408
11442
  skills: ensureMemoryInSkills(Array.isArray(parsed.skills) ? parsed.skills : undefined),
@@ -11435,11 +11469,12 @@ async function seedAgentsIfEmpty() {
11435
11469
  } catch {
11436
11470
  return;
11437
11471
  }
11472
+ const modelOverride = await getDefaultModelForAvailableProvider();
11438
11473
  for (const name of entries) {
11439
11474
  if (!name.endsWith(".json"))
11440
11475
  continue;
11441
11476
  try {
11442
- await insertSeedAgentFromFile(seedDir, name, false);
11477
+ await insertSeedAgentFromFile(seedDir, name, false, modelOverride);
11443
11478
  } catch (err) {
11444
11479
  console.error(`[agent-registry] Failed to seed ${name}:`, err);
11445
11480
  }
@@ -11455,12 +11490,23 @@ async function installSystemAgents() {
11455
11490
  } catch (err) {
11456
11491
  throw new Error(`Seed directory not found: ${seedDir}`);
11457
11492
  }
11493
+ const modelOverride = await getDefaultModelForAvailableProvider();
11458
11494
  let installed = 0;
11459
11495
  for (const name of entries) {
11460
11496
  if (!name.endsWith(".json"))
11461
11497
  continue;
11462
11498
  try {
11463
- const added = await insertSeedAgentFromFile(seedDir, name, true);
11499
+ const path = join2(seedDir, name);
11500
+ const raw = await readFile2(path, "utf-8");
11501
+ const parsed = JSON.parse(raw);
11502
+ const id = typeof parsed?.id === "string" ? parsed.id.trim() : null;
11503
+ if (id && modelOverride) {
11504
+ const existing = agentStore.getAgentById(id);
11505
+ if (existing) {
11506
+ await updateAgent(id, { model: modelOverride });
11507
+ }
11508
+ }
11509
+ const added = await insertSeedAgentFromFile(seedDir, name, true, modelOverride);
11464
11510
  if (added)
11465
11511
  installed += 1;
11466
11512
  } catch (err) {
@@ -15520,8 +15566,9 @@ async function loadPlugins() {
15520
15566
 
15521
15567
  // src/server.ts
15522
15568
  init_config();
15523
- import { join as join13, dirname, resolve as resolve4 } from "path";
15569
+ import { join as join13, dirname as dirname2, resolve as resolve4 } from "path";
15524
15570
  import { mkdirSync, existsSync as existsSync3 } from "fs";
15571
+ import { mkdir as mkdir7 } from "fs/promises";
15525
15572
  var PORT = parseInt(process.env.PORT ?? "3010", 10);
15526
15573
  var DASHBOARD_DIST = (() => {
15527
15574
  const root = join13(import.meta.dir, "..");
@@ -15537,6 +15584,12 @@ function isAuthExempt(pathname, method) {
15537
15584
  return true;
15538
15585
  if (method === "GET" && pathname === "/health")
15539
15586
  return true;
15587
+ if (method === "GET" && pathname === "/api/bootstrap/dashboard-token")
15588
+ return true;
15589
+ if (method === "GET" && pathname === "/api/bootstrap/workspace-status")
15590
+ return true;
15591
+ if (method === "POST" && pathname === "/api/bootstrap/setup-workspace")
15592
+ return true;
15540
15593
  if (method === "POST" && pathname === "/api/channels/telegram/webhook")
15541
15594
  return true;
15542
15595
  if (method === "POST" && pathname === "/api/channels/slack/webhook")
@@ -15600,8 +15653,8 @@ var EVENT_TYPES = [
15600
15653
  "tool.completed"
15601
15654
  ];
15602
15655
  var wsClients = new Set;
15603
- var MEMORY_DB_PATH = process.env.AGENT_MEMORY_DB_PATH ?? join13(getAgentOsHome(), "database.db");
15604
- mkdirSync(dirname(MEMORY_DB_PATH), { recursive: true });
15656
+ var MEMORY_DB_PATH = getMemoryDbPath();
15657
+ mkdirSync(dirname2(MEMORY_DB_PATH), { recursive: true });
15605
15658
  var memoryStore = new MemoryStore(MEMORY_DB_PATH);
15606
15659
  setAgentStore(memoryStore);
15607
15660
  function broadcastEvent(event) {
@@ -15884,6 +15937,43 @@ function createRoutes() {
15884
15937
  "/api/conversations/summarize": {
15885
15938
  POST: (req) => handleConversationSummarize(req, memoryStore)
15886
15939
  },
15940
+ "/api/bootstrap/dashboard-token": {
15941
+ GET: async () => {
15942
+ const config = await readConfig();
15943
+ if (config.onboarding_completed === true) {
15944
+ return Response.json({ error: "Onboarding already completed. Use the login page." }, { status: 403, headers: CORS_HEADERS });
15945
+ }
15946
+ const token = await getDashboardSecret();
15947
+ return jsonResponse({ token });
15948
+ }
15949
+ },
15950
+ "/api/bootstrap/workspace-status": {
15951
+ GET: async () => {
15952
+ try {
15953
+ await mkdir7(getAgentOsHome(), { recursive: true });
15954
+ await mkdir7(dirname2(getMemoryDbPath()), { recursive: true });
15955
+ await loadAgents();
15956
+ return jsonResponse({ ready: true });
15957
+ } catch (err) {
15958
+ const msg = errorMessage(err);
15959
+ return jsonResponse({ ready: false, error: msg }, 200);
15960
+ }
15961
+ }
15962
+ },
15963
+ "/api/bootstrap/setup-workspace": {
15964
+ POST: async () => {
15965
+ try {
15966
+ await mkdir7(getAgentOsHome(), { recursive: true });
15967
+ await mkdir7(dirname2(getMemoryDbPath()), { recursive: true });
15968
+ await seedAgentsIfEmpty();
15969
+ const { installed } = await installSystemAgents();
15970
+ return jsonResponse({ ok: true, installed });
15971
+ } catch (err) {
15972
+ const msg = errorMessage(err);
15973
+ return jsonResponse({ error: msg }, 400);
15974
+ }
15975
+ }
15976
+ },
15887
15977
  "/api/settings": {
15888
15978
  GET: (req) => handleSettings(req),
15889
15979
  PUT: (req) => handleSettings(req)
@@ -16037,53 +16127,62 @@ async function startServer() {
16037
16127
  if (dashboardMissing) {
16038
16128
  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`);
16039
16129
  }
16040
- const server = Bun.serve({
16041
- port: PORT,
16042
- hostname: HOST,
16043
- idleTimeout: 120,
16044
- routes: wrapRouteHandlers(createRoutes(), dashboardSecret),
16045
- fetch(req, server2) {
16046
- const url = new URL(req.url);
16047
- if (req.method === "GET" && url.pathname === "/api/events") {
16048
- if (dashboardSecret) {
16049
- const token = url.searchParams.get("token")?.trim() ?? getTokenFromRequest(req);
16050
- if (token !== dashboardSecret) {
16051
- return authUnauthorizedResponse();
16130
+ let server;
16131
+ try {
16132
+ server = Bun.serve({
16133
+ port: PORT,
16134
+ hostname: HOST,
16135
+ idleTimeout: 120,
16136
+ routes: wrapRouteHandlers(createRoutes(), dashboardSecret),
16137
+ fetch(req, server2) {
16138
+ const url = new URL(req.url);
16139
+ if (req.method === "GET" && url.pathname === "/api/events") {
16140
+ if (dashboardSecret) {
16141
+ const token = url.searchParams.get("token")?.trim() ?? getTokenFromRequest(req);
16142
+ if (token !== dashboardSecret) {
16143
+ return authUnauthorizedResponse();
16144
+ }
16145
+ }
16146
+ if (server2.upgrade(req, { data: undefined }))
16147
+ return;
16148
+ return Response.json({ error: "Upgrade failed" }, { status: 500, headers: CORS_HEADERS });
16149
+ }
16150
+ if (req.method === "GET" && !url.pathname.startsWith("/api/")) {
16151
+ const dashboardResponse = serveDashboard(url.pathname);
16152
+ if (dashboardResponse)
16153
+ return dashboardResponse;
16154
+ if (url.pathname === "/" || url.pathname === "") {
16155
+ return Response.json({
16156
+ error: "Dashboard not built",
16157
+ path: DASHBOARD_DIST,
16158
+ hint: "From the sulala package root run: cd dashboard && npm run build",
16159
+ hint_global: "If you installed globally, reinstall to get the dashboard: bun install -g @sulala/agent-os@latest"
16160
+ }, { status: 404, headers: CORS_HEADERS });
16052
16161
  }
16053
16162
  }
16054
- if (server2.upgrade(req, { data: undefined }))
16055
- return;
16056
- return Response.json({ error: "Upgrade failed" }, { status: 500, headers: CORS_HEADERS });
16057
- }
16058
- if (req.method === "GET" && !url.pathname.startsWith("/api/")) {
16059
- const dashboardResponse = serveDashboard(url.pathname);
16060
- if (dashboardResponse)
16061
- return dashboardResponse;
16062
- if (url.pathname === "/" || url.pathname === "") {
16063
- return Response.json({
16064
- error: "Dashboard not built",
16065
- path: DASHBOARD_DIST,
16066
- hint: "From the sulala package root run: cd dashboard && npm run build",
16067
- hint_global: "If you installed globally, reinstall to get the dashboard: bun install -g @sulala/agent-os@latest"
16068
- }, { status: 404, headers: CORS_HEADERS });
16069
- }
16070
- }
16071
- return Response.json({ error: "Not found" }, { status: 404, headers: CORS_HEADERS });
16072
- },
16073
- error(error) {
16074
- console.error(error);
16075
- return Response.json({ error: "Internal Server Error" }, { status: 500, headers: CORS_HEADERS });
16076
- },
16077
- websocket: {
16078
- open(ws) {
16079
- wsClients.add(ws);
16163
+ return Response.json({ error: "Not found" }, { status: 404, headers: CORS_HEADERS });
16080
16164
  },
16081
- close(ws) {
16082
- wsClients.delete(ws);
16165
+ error(error) {
16166
+ console.error(error);
16167
+ return Response.json({ error: "Internal Server Error" }, { status: 500, headers: CORS_HEADERS });
16083
16168
  },
16084
- message() {}
16169
+ websocket: {
16170
+ open(ws) {
16171
+ wsClients.add(ws);
16172
+ },
16173
+ close(ws) {
16174
+ wsClients.delete(ws);
16175
+ },
16176
+ message() {}
16177
+ }
16178
+ });
16179
+ } catch (err) {
16180
+ const e = err;
16181
+ if (e?.code === "EADDRINUSE" || e?.errno === 48) {
16182
+ 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`);
16085
16183
  }
16086
- });
16184
+ throw err;
16185
+ }
16087
16186
  console.log(`Agent OS server running at ${server.url}`);
16088
16187
  startTelegramPolling(memoryStore);
16089
16188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sulala/agent-os",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -24,7 +24,9 @@
24
24
  "files": [
25
25
  "dist",
26
26
  "dashboard-dist",
27
- "data"
27
+ "data/agents",
28
+ "data/skills",
29
+ "data/templates"
28
30
  ],
29
31
  "engines": {
30
32
  "bun": ">=1.0.0"