@sulala/agent-os 0.1.15 → 0.1.18

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,14 +43,18 @@ __export(exports_config, {
43
43
  getSkillsDir: () => getSkillsDir,
44
44
  getSkillConfigPath: () => getSkillConfigPath,
45
45
  getSeedSkillsDir: () => getSeedSkillsDir,
46
+ getDashboardSecretFromConfig: () => getDashboardSecretFromConfig,
47
+ getDashboardSecret: () => getDashboardSecret,
46
48
  getConfigsDir: () => getConfigsDir,
47
49
  getConfigPath: () => getConfigPath,
48
50
  getAgentOsHome: () => getAgentOsHome,
51
+ generateDashboardSecret: () => generateDashboardSecret,
49
52
  ensureWorkspace: () => ensureWorkspace
50
53
  });
51
54
  import { readFile, writeFile, mkdir } from "fs/promises";
52
55
  import { existsSync } from "fs";
53
56
  import { join, resolve, relative } from "path";
57
+ import { randomBytes } from "crypto";
54
58
  function getAgentOsHome() {
55
59
  return process.env.AGENT_OS_HOME || DEFAULT_HOME;
56
60
  }
@@ -84,6 +88,7 @@ async function readConfig() {
84
88
  const viber_default_agent_id = o.viber_default_agent_id;
85
89
  const onboarding_completed = o.onboarding_completed;
86
90
  const skills_registry_url = o.skills_registry_url;
91
+ const dashboard_secret = o.dashboard_secret;
87
92
  return {
88
93
  provider: provider === "openrouter" || provider === "openai" ? provider : undefined,
89
94
  api_key: typeof api_key === "string" ? api_key : undefined,
@@ -104,7 +109,8 @@ async function readConfig() {
104
109
  viber_auth_token: typeof viber_auth_token === "string" ? viber_auth_token : undefined,
105
110
  viber_default_agent_id: typeof viber_default_agent_id === "string" ? viber_default_agent_id : undefined,
106
111
  onboarding_completed: onboarding_completed === true ? true : undefined,
107
- skills_registry_url: typeof skills_registry_url === "string" ? skills_registry_url.trim() || undefined : 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
108
114
  };
109
115
  }
110
116
  } catch (err) {
@@ -136,7 +142,8 @@ async function writeConfig(updates) {
136
142
  viber_auth_token: updates.viber_auth_token !== undefined ? updates.viber_auth_token : current.viber_auth_token,
137
143
  viber_default_agent_id: updates.viber_default_agent_id !== undefined ? updates.viber_default_agent_id : current.viber_default_agent_id,
138
144
  onboarding_completed: updates.onboarding_completed !== undefined ? updates.onboarding_completed : current.onboarding_completed,
139
- skills_registry_url: updates.skills_registry_url !== undefined ? updates.skills_registry_url : current.skills_registry_url
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
140
147
  };
141
148
  const home = getAgentOsHome();
142
149
  const path = getConfigPath();
@@ -161,7 +168,8 @@ async function writeConfig(updates) {
161
168
  viber_auth_token: merged.viber_auth_token ?? null,
162
169
  viber_default_agent_id: merged.viber_default_agent_id ?? null,
163
170
  onboarding_completed: merged.onboarding_completed ?? null,
164
- skills_registry_url: merged.skills_registry_url ?? null
171
+ skills_registry_url: merged.skills_registry_url ?? null,
172
+ dashboard_secret: merged.dashboard_secret ?? null
165
173
  }, null, 2), "utf-8");
166
174
  }
167
175
  async function getSkillsRegistryUrl() {
@@ -174,6 +182,25 @@ async function getSkillsRegistryUrl() {
174
182
  return fromConfig;
175
183
  return DEFAULT_SKILLS_REGISTRY_URL;
176
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
+ }
177
204
  function getConfigsDir() {
178
205
  return join(getAgentOsHome(), "configs");
179
206
  }
@@ -11069,7 +11096,7 @@ function errorMessage(err) {
11069
11096
  var CORS_HEADERS = {
11070
11097
  "Access-Control-Allow-Origin": "*",
11071
11098
  "Access-Control-Allow-Methods": "GET,POST,PUT,PATCH,DELETE,OPTIONS",
11072
- "Access-Control-Allow-Headers": "Content-Type"
11099
+ "Access-Control-Allow-Headers": "Content-Type, Authorization"
11073
11100
  };
11074
11101
  function jsonResponse(data, status = 200) {
11075
11102
  return Response.json(data, {
@@ -15505,6 +15532,63 @@ var DASHBOARD_DIST = (() => {
15505
15532
  return b;
15506
15533
  })();
15507
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
+ }
15508
15592
  var EVENT_TYPES = [
15509
15593
  "task.created",
15510
15594
  "task.started",
@@ -15938,6 +16022,7 @@ async function startServer() {
15938
16022
  startScheduler();
15939
16023
  await loadPlugins();
15940
16024
  await seedAgentsIfEmpty();
16025
+ const dashboardSecret = await getDashboardSecret();
15941
16026
  const dashboardMissing = !existsSync3(DASHBOARD_DIST) || !existsSync3(join13(DASHBOARD_DIST, "index.html"));
15942
16027
  if (dashboardMissing) {
15943
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`);
@@ -15946,10 +16031,16 @@ async function startServer() {
15946
16031
  port: PORT,
15947
16032
  hostname: HOST,
15948
16033
  idleTimeout: 120,
15949
- routes: createRoutes(),
16034
+ routes: wrapRouteHandlers(createRoutes(), dashboardSecret),
15950
16035
  fetch(req, server2) {
15951
16036
  const url = new URL(req.url);
15952
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
+ }
15953
16044
  if (server2.upgrade(req, { data: undefined }))
15954
16045
  return;
15955
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.15",
3
+ "version": "0.1.18",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },