@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/dashboard-dist/assets/index-3nnVHZ7F.js +72 -0
- 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 +145 -8
- package/dist/index.js +113 -7
- package/package.json +1 -1
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 =
|
|
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 });
|