@sulala/agent-os 0.1.14 → 0.1.17

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
@@ -39,17 +39,22 @@ __export(exports_config, {
39
39
  readConfig: () => readConfig,
40
40
  getWorkspaceDir: () => getWorkspaceDir,
41
41
  getTemplatesDir: () => getTemplatesDir,
42
+ getSkillsRegistryUrl: () => getSkillsRegistryUrl,
42
43
  getSkillsDir: () => getSkillsDir,
43
44
  getSkillConfigPath: () => getSkillConfigPath,
44
45
  getSeedSkillsDir: () => getSeedSkillsDir,
46
+ getDashboardSecretFromConfig: () => getDashboardSecretFromConfig,
47
+ getDashboardSecret: () => getDashboardSecret,
45
48
  getConfigsDir: () => getConfigsDir,
46
49
  getConfigPath: () => getConfigPath,
47
50
  getAgentOsHome: () => getAgentOsHome,
51
+ generateDashboardSecret: () => generateDashboardSecret,
48
52
  ensureWorkspace: () => ensureWorkspace
49
53
  });
50
54
  import { readFile, writeFile, mkdir } from "fs/promises";
51
55
  import { existsSync } from "fs";
52
56
  import { join, resolve, relative } from "path";
57
+ import { randomBytes } from "crypto";
53
58
  function getAgentOsHome() {
54
59
  return process.env.AGENT_OS_HOME || DEFAULT_HOME;
55
60
  }
@@ -82,6 +87,8 @@ async function readConfig() {
82
87
  const viber_auth_token = o.viber_auth_token;
83
88
  const viber_default_agent_id = o.viber_default_agent_id;
84
89
  const onboarding_completed = o.onboarding_completed;
90
+ const skills_registry_url = o.skills_registry_url;
91
+ const dashboard_secret = o.dashboard_secret;
85
92
  return {
86
93
  provider: provider === "openrouter" || provider === "openai" ? provider : undefined,
87
94
  api_key: typeof api_key === "string" ? api_key : undefined,
@@ -101,7 +108,9 @@ async function readConfig() {
101
108
  signal_default_agent_id: typeof signal_default_agent_id === "string" ? signal_default_agent_id : undefined,
102
109
  viber_auth_token: typeof viber_auth_token === "string" ? viber_auth_token : undefined,
103
110
  viber_default_agent_id: typeof viber_default_agent_id === "string" ? viber_default_agent_id : undefined,
104
- onboarding_completed: onboarding_completed === true ? true : undefined
111
+ onboarding_completed: onboarding_completed === true ? true : undefined,
112
+ skills_registry_url: typeof skills_registry_url === "string" ? skills_registry_url.trim() || undefined : undefined,
113
+ dashboard_secret: typeof dashboard_secret === "string" ? dashboard_secret.trim() || undefined : undefined
105
114
  };
106
115
  }
107
116
  } catch (err) {
@@ -132,7 +141,9 @@ async function writeConfig(updates) {
132
141
  signal_default_agent_id: updates.signal_default_agent_id !== undefined ? updates.signal_default_agent_id : current.signal_default_agent_id,
133
142
  viber_auth_token: updates.viber_auth_token !== undefined ? updates.viber_auth_token : current.viber_auth_token,
134
143
  viber_default_agent_id: updates.viber_default_agent_id !== undefined ? updates.viber_default_agent_id : current.viber_default_agent_id,
135
- onboarding_completed: updates.onboarding_completed !== undefined ? updates.onboarding_completed : current.onboarding_completed
144
+ onboarding_completed: updates.onboarding_completed !== undefined ? updates.onboarding_completed : current.onboarding_completed,
145
+ skills_registry_url: updates.skills_registry_url !== undefined ? updates.skills_registry_url : current.skills_registry_url,
146
+ dashboard_secret: updates.dashboard_secret !== undefined ? updates.dashboard_secret : current.dashboard_secret
136
147
  };
137
148
  const home = getAgentOsHome();
138
149
  const path = getConfigPath();
@@ -156,9 +167,40 @@ async function writeConfig(updates) {
156
167
  signal_default_agent_id: merged.signal_default_agent_id ?? null,
157
168
  viber_auth_token: merged.viber_auth_token ?? null,
158
169
  viber_default_agent_id: merged.viber_default_agent_id ?? null,
159
- onboarding_completed: merged.onboarding_completed ?? null
170
+ onboarding_completed: merged.onboarding_completed ?? null,
171
+ skills_registry_url: merged.skills_registry_url ?? null,
172
+ dashboard_secret: merged.dashboard_secret ?? null
160
173
  }, null, 2), "utf-8");
161
174
  }
175
+ async function getSkillsRegistryUrl() {
176
+ const env = process.env.SKILLS_REGISTRY_URL?.trim();
177
+ if (env)
178
+ return env;
179
+ const config = await readConfig();
180
+ const fromConfig = config.skills_registry_url?.trim();
181
+ if (fromConfig)
182
+ return fromConfig;
183
+ return DEFAULT_SKILLS_REGISTRY_URL;
184
+ }
185
+ function generateDashboardSecret() {
186
+ return randomBytes(32).toString("base64url");
187
+ }
188
+ async function getDashboardSecret() {
189
+ const env = process.env.DASHBOARD_SECRET?.trim();
190
+ if (env)
191
+ return env;
192
+ let config = await readConfig();
193
+ let secret = config.dashboard_secret?.trim();
194
+ if (secret)
195
+ return secret;
196
+ secret = generateDashboardSecret();
197
+ await writeConfig({ dashboard_secret: secret });
198
+ return secret;
199
+ }
200
+ async function getDashboardSecretFromConfig() {
201
+ const config = await readConfig();
202
+ return config.dashboard_secret?.trim() || undefined;
203
+ }
162
204
  function getConfigsDir() {
163
205
  return join(getAgentOsHome(), "configs");
164
206
  }
@@ -227,7 +269,7 @@ function resolveInWorkspace(workspaceDir, relativePath) {
227
269
  }
228
270
  return resolved;
229
271
  }
230
- var DEFAULT_HOME;
272
+ var DEFAULT_HOME, DEFAULT_SKILLS_REGISTRY_URL = "https://hub.sulala.ai/api/sulalahub/registry";
231
273
  var init_config = __esm(() => {
232
274
  DEFAULT_HOME = join(process.env.HOME || process.env.USERPROFILE || "~", ".agent-os");
233
275
  });
@@ -11054,7 +11096,7 @@ function errorMessage(err) {
11054
11096
  var CORS_HEADERS = {
11055
11097
  "Access-Control-Allow-Origin": "*",
11056
11098
  "Access-Control-Allow-Methods": "GET,POST,PUT,PATCH,DELETE,OPTIONS",
11057
- "Access-Control-Allow-Headers": "Content-Type"
11099
+ "Access-Control-Allow-Headers": "Content-Type, Authorization"
11058
11100
  };
11059
11101
  function jsonResponse(data, status = 200) {
11060
11102
  return Response.json(data, {
@@ -12645,7 +12687,7 @@ async function getSkillSetupMarkdown(skillId) {
12645
12687
  }
12646
12688
  }
12647
12689
  async function getStoreRegistry() {
12648
- const registryUrl = process.env.SKILLS_REGISTRY_URL?.trim() ?? null;
12690
+ const registryUrl = await getSkillsRegistryUrl();
12649
12691
  if (!registryUrl)
12650
12692
  return { skills: [], storeBase: null, registryUrl: null };
12651
12693
  let storeBase = null;
@@ -15490,6 +15532,63 @@ var DASHBOARD_DIST = (() => {
15490
15532
  return b;
15491
15533
  })();
15492
15534
  var HOST = process.env.HOST ?? "127.0.0.1";
15535
+ function isAuthExempt(pathname, method) {
15536
+ if (method === "OPTIONS")
15537
+ return true;
15538
+ if (method === "GET" && pathname === "/health")
15539
+ return true;
15540
+ if (method === "POST" && pathname === "/api/channels/telegram/webhook")
15541
+ return true;
15542
+ if (method === "POST" && pathname === "/api/channels/slack/webhook")
15543
+ return true;
15544
+ if (method === "POST" && pathname === "/api/channels/discord/webhook")
15545
+ return true;
15546
+ if (method === "POST" && pathname === "/api/channels/signal/webhook")
15547
+ return true;
15548
+ if (method === "POST" && pathname === "/api/channels/viber/webhook")
15549
+ return true;
15550
+ return false;
15551
+ }
15552
+ function getTokenFromRequest(req) {
15553
+ const auth = req.headers.get("Authorization");
15554
+ if (auth?.startsWith("Bearer "))
15555
+ return auth.slice(7).trim();
15556
+ const url = new URL(req.url);
15557
+ return url.searchParams.get("token")?.trim() ?? null;
15558
+ }
15559
+ function authUnauthorizedResponse() {
15560
+ return Response.json({ error: "Unauthorized" }, { status: 401, headers: CORS_HEADERS });
15561
+ }
15562
+ function wrapWithAuth(fn, secret) {
15563
+ if (!secret)
15564
+ return fn;
15565
+ return (req) => {
15566
+ const url = new URL(req.url);
15567
+ if (isAuthExempt(url.pathname, req.method))
15568
+ return fn(req);
15569
+ const token = getTokenFromRequest(req);
15570
+ if (token !== secret)
15571
+ return authUnauthorizedResponse();
15572
+ return fn(req);
15573
+ };
15574
+ }
15575
+ function wrapRouteHandlers(routes, secret) {
15576
+ const out = {};
15577
+ for (const [path, value] of Object.entries(routes)) {
15578
+ if (typeof value === "function") {
15579
+ out[path] = wrapWithAuth(value, secret);
15580
+ } else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
15581
+ const methods = {};
15582
+ for (const [method, handler] of Object.entries(value)) {
15583
+ methods[method] = typeof handler === "function" ? wrapWithAuth(handler, secret) : handler;
15584
+ }
15585
+ out[path] = methods;
15586
+ } else {
15587
+ out[path] = value;
15588
+ }
15589
+ }
15590
+ return out;
15591
+ }
15493
15592
  var EVENT_TYPES = [
15494
15593
  "task.created",
15495
15594
  "task.started",
@@ -15923,6 +16022,7 @@ async function startServer() {
15923
16022
  startScheduler();
15924
16023
  await loadPlugins();
15925
16024
  await seedAgentsIfEmpty();
16025
+ const dashboardSecret = await getDashboardSecret();
15926
16026
  const dashboardMissing = !existsSync3(DASHBOARD_DIST) || !existsSync3(join13(DASHBOARD_DIST, "index.html"));
15927
16027
  if (dashboardMissing) {
15928
16028
  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`);
@@ -15931,10 +16031,16 @@ async function startServer() {
15931
16031
  port: PORT,
15932
16032
  hostname: HOST,
15933
16033
  idleTimeout: 120,
15934
- routes: createRoutes(),
16034
+ routes: wrapRouteHandlers(createRoutes(), dashboardSecret),
15935
16035
  fetch(req, server2) {
15936
16036
  const url = new URL(req.url);
15937
16037
  if (req.method === "GET" && url.pathname === "/api/events") {
16038
+ if (dashboardSecret) {
16039
+ const token = url.searchParams.get("token")?.trim() ?? getTokenFromRequest(req);
16040
+ if (token !== dashboardSecret) {
16041
+ return authUnauthorizedResponse();
16042
+ }
16043
+ }
15938
16044
  if (server2.upgrade(req, { data: undefined }))
15939
16045
  return;
15940
16046
  return Response.json({ error: "Upgrade failed" }, { status: 500, headers: CORS_HEADERS });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sulala/agent-os",
3
- "version": "0.1.14",
3
+ "version": "0.1.17",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },