@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/dashboard-dist/assets/index-B4RwGkGB.js +72 -0
- package/dashboard-dist/assets/index-Cdtyuhuu.css +1 -0
- package/dashboard-dist/favicon.ico +0 -0
- package/dashboard-dist/index.html +4 -4
- package/dashboard-dist/logo_dark.png +0 -0
- package/dashboard-dist/logo_white.png +0 -0
- package/dist/cli.js +132 -6
- package/dist/index.js +96 -5
- package/package.json +1 -1
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 });
|