@sulala/agent 0.1.13 → 0.1.15
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/README.md +3 -3
- package/dashboard/dist/assets/index-DegBJNv6.css +1 -0
- package/dashboard/dist/assets/index-pVHpAj3h.js +83 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +21 -6
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/skill-generate.d.ts +1 -1
- package/dist/agent/skill-generate.d.ts.map +1 -1
- package/dist/agent/skill-generate.js +10 -3
- package/dist/agent/skill-generate.js.map +1 -1
- package/dist/agent/skill-install.d.ts +12 -1
- package/dist/agent/skill-install.d.ts.map +1 -1
- package/dist/agent/skill-install.js +130 -15
- package/dist/agent/skill-install.js.map +1 -1
- package/dist/agent/skills.d.ts +4 -3
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +53 -25
- package/dist/agent/skills.js.map +1 -1
- package/dist/agent/tool/spec-loader.d.ts +7 -0
- package/dist/agent/tool/spec-loader.d.ts.map +1 -0
- package/dist/agent/tool/spec-loader.js +540 -0
- package/dist/agent/tool/spec-loader.js.map +1 -0
- package/dist/agent/tools.d.ts.map +1 -1
- package/dist/agent/tools.integrations.test.js +4 -5
- package/dist/agent/tools.integrations.test.js.map +1 -1
- package/dist/agent/tools.js +144 -367
- package/dist/agent/tools.js.map +1 -1
- package/dist/ai/orchestrator.d.ts.map +1 -1
- package/dist/ai/orchestrator.js +82 -17
- package/dist/ai/orchestrator.js.map +1 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +25 -9
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -5
- package/dist/config.js.map +1 -1
- package/dist/db/index.d.ts +14 -7
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +108 -30
- package/dist/db/index.js.map +1 -1
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +141 -15
- package/dist/gateway/server.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/onboard-env.d.ts +1 -1
- package/dist/onboard-env.d.ts.map +1 -1
- package/dist/onboard-env.js +2 -0
- package/dist/onboard-env.js.map +1 -1
- package/dist/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/watcher/index.d.ts.map +1 -1
- package/dist/watcher/index.js +1 -2
- package/dist/watcher/index.js.map +1 -1
- package/package.json +4 -5
- package/src/index.ts +1 -1
- package/context/00-rules.md +0 -1
- package/context/airtable.md +0 -35
- package/context/apple-notes.md +0 -99
- package/context/asana.md +0 -37
- package/context/bluesky.md +0 -46
- package/context/calendar.md +0 -63
- package/context/country-info.md +0 -13
- package/context/create-skill.md +0 -128
- package/context/discord.md +0 -30
- package/context/docs.md +0 -29
- package/context/drive.md +0 -49
- package/context/dropbox.md +0 -39
- package/context/facebook.md +0 -47
- package/context/fetch-form-api.md +0 -16
- package/context/figma.md +0 -30
- package/context/files.md +0 -30
- package/context/git.md +0 -37
- package/context/github.md +0 -58
- package/context/gmail.md +0 -52
- package/context/google.md +0 -28
- package/context/hellohub.md +0 -29
- package/context/jira.md +0 -46
- package/context/linear.md +0 -40
- package/context/news.md +0 -64
- package/context/notion.md +0 -45
- package/context/portal-integrations.md +0 -42
- package/context/post-to-x.md +0 -50
- package/context/sheets.md +0 -47
- package/context/slack.md +0 -48
- package/context/slides.md +0 -35
- package/context/stripe.md +0 -38
- package/context/tes.md +0 -7
- package/context/test.md +0 -7
- package/context/weather.md +0 -32
- package/context/zoom.md +0 -28
- package/dashboard/dist/assets/index-BTx-9jCj.css +0 -1
- package/dashboard/dist/assets/index-B_QGQ8c_.js +0 -83
- package/registry/apple-notes.md +0 -99
- package/registry/bluesky.md +0 -34
- package/registry/files.md +0 -30
- package/registry/git.md +0 -37
- package/registry/news.md +0 -64
- package/registry/skills-registry.json +0 -46
- package/registry/weather.md +0 -32
package/dist/gateway/server.js
CHANGED
|
@@ -11,13 +11,13 @@ import { initDb, getDb, log, insertTask, getFileStates, updateTaskStatus, setTas
|
|
|
11
11
|
import { scheduleCronById, unscheduleJob } from '../scheduler/cron.js';
|
|
12
12
|
import { getTelegramChannelState, setTelegramChannelConfig } from '../channels/telegram.js';
|
|
13
13
|
import { getDiscordChannelState, setDiscordChannelConfig } from '../channels/discord.js';
|
|
14
|
-
import { getStripeChannelState, setStripeChannelConfig } from '../channels/stripe.js';
|
|
14
|
+
import { getEffectiveStripeSecretKey, getStripeChannelState, setStripeChannelConfig } from '../channels/stripe.js';
|
|
15
15
|
import { reloadProviders } from '../ai/orchestrator.js';
|
|
16
16
|
import { runAgentTurn, runAgentTurnStream } from '../agent/loop.js';
|
|
17
17
|
import { runAgentTurnWithPi, isPiAvailable } from '../agent/pi-runner.js';
|
|
18
18
|
import { listTools, executeTool } from '../agent/tools.js';
|
|
19
19
|
import { listSkills, getAllRequiredBins } from '../agent/skills.js';
|
|
20
|
-
import { getRegistrySkills, getAvailableUpdates, installSkill, uninstallSkill, updateSkillsAll } from '../agent/skill-install.js';
|
|
20
|
+
import { getRegistrySkills, getAvailableUpdates, installSkill, uninstallSkill, updateSkillsAll, installAllSystemSkills } from '../agent/skill-install.js';
|
|
21
21
|
import { getTemplates } from '../agent/skill-templates.js';
|
|
22
22
|
import { generateSkillSpec, writeGeneratedSkill, WIZARD_APPS, WIZARD_TRIGGERS } from '../agent/skill-generate.js';
|
|
23
23
|
import { loadSkillsConfig, saveSkillsConfig, getConfigPath, setSkillEnabled, removeSkillEntry, migrateConfigNameKeysToSlug, getOnboardingComplete, setOnboardingComplete } from '../agent/skills-config.js';
|
|
@@ -67,6 +67,18 @@ function rateLimitMiddleware(req, res, next) {
|
|
|
67
67
|
}
|
|
68
68
|
next();
|
|
69
69
|
}
|
|
70
|
+
/** Store base URL for publishing skills (POST /api/submissions). Derived from SKILLS_REGISTRY_URL origin. */
|
|
71
|
+
function getStorePublishBaseUrl() {
|
|
72
|
+
const registryUrl = (process.env.SKILLS_REGISTRY_URL || '').trim();
|
|
73
|
+
if (!registryUrl)
|
|
74
|
+
return null;
|
|
75
|
+
try {
|
|
76
|
+
return new URL(registryUrl).origin;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
70
82
|
export function createGateway(appMount = null) {
|
|
71
83
|
const app = appMount || express();
|
|
72
84
|
app.use(cors());
|
|
@@ -91,10 +103,20 @@ export function createGateway(appMount = null) {
|
|
|
91
103
|
res.status(401).json({ error: 'Invalid or missing API key' });
|
|
92
104
|
});
|
|
93
105
|
}
|
|
94
|
-
/** SulalaHub: public skills registry.
|
|
106
|
+
/** SulalaHub: public skills registry. When serving, base URL for skill links is derived from SKILLS_REGISTRY_URL (origin) or host:port. */
|
|
95
107
|
app.get('/api/sulalahub/registry', (_req, res) => {
|
|
96
108
|
try {
|
|
97
|
-
const
|
|
109
|
+
const registryUrl = process.env.SKILLS_REGISTRY_URL?.trim();
|
|
110
|
+
const baseUrl = registryUrl
|
|
111
|
+
? (() => {
|
|
112
|
+
try {
|
|
113
|
+
return new URL(registryUrl).origin;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return `http://${config.host}:${config.port}`;
|
|
117
|
+
}
|
|
118
|
+
})()
|
|
119
|
+
: `http://${config.host}:${config.port}`;
|
|
98
120
|
const registryPath = join(registryDir, 'skills-registry.json');
|
|
99
121
|
if (!existsSync(registryPath)) {
|
|
100
122
|
res.json({ skills: [] });
|
|
@@ -162,12 +184,20 @@ export function createGateway(appMount = null) {
|
|
|
162
184
|
res.status(500).json({ complete: false, error: e.message });
|
|
163
185
|
}
|
|
164
186
|
});
|
|
165
|
-
/** Onboard: mark onboarding as complete. */
|
|
187
|
+
/** Onboard: mark onboarding as complete. Installs all system skills to workspace/skills in the background when SKILLS_REGISTRY_URL is set. */
|
|
166
188
|
app.put('/api/onboard/complete', async (_req, res) => {
|
|
167
189
|
try {
|
|
168
190
|
setOnboardingComplete(true);
|
|
169
191
|
await reloadProviders();
|
|
170
192
|
res.json({ ok: true, complete: true });
|
|
193
|
+
installAllSystemSkills()
|
|
194
|
+
.then(({ installed, failed }) => {
|
|
195
|
+
if (installed.length)
|
|
196
|
+
log('gateway', 'info', `Onboard: installed ${installed.length} system skill(s): ${installed.join(', ')}`);
|
|
197
|
+
if (failed.length)
|
|
198
|
+
log('gateway', 'warn', `Onboard: failed to install ${failed.length} system skill(s): ${failed.map((f) => f.slug).join(', ')}`);
|
|
199
|
+
})
|
|
200
|
+
.catch((e) => log('gateway', 'error', `Onboard: system skills install failed: ${e.message}`));
|
|
171
201
|
}
|
|
172
202
|
catch (e) {
|
|
173
203
|
log('gateway', 'error', e.message);
|
|
@@ -408,6 +438,9 @@ export function createGateway(appMount = null) {
|
|
|
408
438
|
res.status(400).json({ error: result.error || 'Failed to save' });
|
|
409
439
|
return;
|
|
410
440
|
}
|
|
441
|
+
// Persist to ~/.sulala/.env so the key is visible there and survives across DB resets
|
|
442
|
+
const effective = getEffectiveStripeSecretKey();
|
|
443
|
+
writeOnboardEnvKeys({ STRIPE_SECRET_KEY: effective ?? '' });
|
|
411
444
|
const state = getStripeChannelState();
|
|
412
445
|
res.json(state);
|
|
413
446
|
}
|
|
@@ -859,9 +892,10 @@ export function createGateway(appMount = null) {
|
|
|
859
892
|
res.status(400).json({ error: 'slug required' });
|
|
860
893
|
return;
|
|
861
894
|
}
|
|
862
|
-
const t = target === 'managed' ? 'managed' : 'workspace';
|
|
895
|
+
const t = target === 'user' ? 'user' : target === 'managed' ? 'managed' : 'workspace';
|
|
863
896
|
const result = uninstallSkill(slug, t);
|
|
864
897
|
if (result.success) {
|
|
898
|
+
removeSkillEntry(slug);
|
|
865
899
|
res.json({ uninstalled: slug, path: result.path, target: t });
|
|
866
900
|
}
|
|
867
901
|
else {
|
|
@@ -873,6 +907,97 @@ export function createGateway(appMount = null) {
|
|
|
873
907
|
res.status(500).json({ error: e.message });
|
|
874
908
|
}
|
|
875
909
|
});
|
|
910
|
+
/** Publish a user-created skill to the store (POST to store /api/submissions). No API key required by default; set STORE_PUBLISH_API_KEY if the store requires it. */
|
|
911
|
+
app.post('/api/agent/skills/publish', async (req, res) => {
|
|
912
|
+
try {
|
|
913
|
+
const { slug, priceIntent, intendedPriceCents } = req.body || {};
|
|
914
|
+
if (!slug || typeof slug !== 'string') {
|
|
915
|
+
res.status(400).json({ error: 'slug required' });
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const skills = listSkills(config, { includeDisabled: true });
|
|
919
|
+
const skill = skills.find((s) => (s.slug ?? s.filePath.split(/[/\\]/).pop()?.replace(/\.md$/, '')) === slug && s.source === 'user');
|
|
920
|
+
if (!skill) {
|
|
921
|
+
res.status(400).json({ error: 'Skill not found or not a user-created skill. Only skills in My skills can be published.' });
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const skillDir = join(config.skillsWorkspaceMyDir, slug);
|
|
925
|
+
const readmePath = join(skillDir, 'README.md');
|
|
926
|
+
const skillPath = join(skillDir, 'SKILL.md');
|
|
927
|
+
const mdPath = existsSync(readmePath) ? readmePath : existsSync(skillPath) ? skillPath : null;
|
|
928
|
+
if (!mdPath) {
|
|
929
|
+
res.status(400).json({ error: 'Skill README.md or SKILL.md not found' });
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const markdown = readFileSync(mdPath, 'utf8');
|
|
933
|
+
const toolsPath = join(skillDir, 'tools.yaml');
|
|
934
|
+
const toolsYaml = existsSync(toolsPath) ? readFileSync(toolsPath, 'utf8').trim() : undefined;
|
|
935
|
+
const storeBase = getStorePublishBaseUrl();
|
|
936
|
+
if (!storeBase) {
|
|
937
|
+
res.status(400).json({
|
|
938
|
+
error: 'Store URL not configured. Set SKILLS_REGISTRY_URL to your hub (e.g. https://hub.sulala.ai or http://localhost:3002).',
|
|
939
|
+
});
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
const submitUrl = `${storeBase.replace(/\/$/, '')}/api/submissions`;
|
|
943
|
+
const body = {
|
|
944
|
+
slug,
|
|
945
|
+
name: skill.name,
|
|
946
|
+
description: skill.description,
|
|
947
|
+
version: skill.version || '1.0.0',
|
|
948
|
+
markdown,
|
|
949
|
+
priceIntent: priceIntent === 'paid' || priceIntent === 'free' ? priceIntent : 'free',
|
|
950
|
+
};
|
|
951
|
+
if (toolsYaml)
|
|
952
|
+
body.toolsYaml = toolsYaml;
|
|
953
|
+
if (priceIntent === 'paid' && typeof intendedPriceCents === 'number' && intendedPriceCents >= 0) {
|
|
954
|
+
body.intendedPriceCents = intendedPriceCents;
|
|
955
|
+
}
|
|
956
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
957
|
+
const apiKey = (process.env.STORE_PUBLISH_API_KEY || '').trim();
|
|
958
|
+
if (apiKey)
|
|
959
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
960
|
+
const resp = await fetch(submitUrl, { method: 'POST', headers, body: JSON.stringify(body) });
|
|
961
|
+
const data = (await resp.json().catch(() => ({})));
|
|
962
|
+
if (!resp.ok) {
|
|
963
|
+
const status = resp.status === 503 ? 503 : 400;
|
|
964
|
+
res.status(status).json({ error: data.error || `Store returned ${resp.status}`, githubUrl: data.githubUrl });
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
if (data.ok && data.id) {
|
|
968
|
+
res.json({ published: true, slug, id: data.id, message: 'Submitted to the store. An admin will review it.' });
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
res.json({ published: true, slug, message: 'Submitted to the store.' });
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
catch (e) {
|
|
975
|
+
log('gateway', 'error', e.message);
|
|
976
|
+
res.status(500).json({ error: e.message });
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
/** Publish status: list of user's submissions (pending/approved) from the store. Uses STORE_PUBLISH_API_KEY. */
|
|
980
|
+
app.get('/api/agent/skills/publish-status', async (_req, res) => {
|
|
981
|
+
try {
|
|
982
|
+
const storeBase = getStorePublishBaseUrl();
|
|
983
|
+
const apiKey = (process.env.STORE_PUBLISH_API_KEY || '').trim();
|
|
984
|
+
if (!storeBase || !apiKey) {
|
|
985
|
+
res.json({ submissions: [] });
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
const url = `${storeBase.replace(/\/$/, '')}/api/me/submissions`;
|
|
989
|
+
const resp = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
|
|
990
|
+
const data = (await resp.json().catch(() => ({})));
|
|
991
|
+
if (!resp.ok) {
|
|
992
|
+
res.json({ submissions: [] });
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
res.json({ submissions: data.submissions ?? [] });
|
|
996
|
+
}
|
|
997
|
+
catch {
|
|
998
|
+
res.json({ submissions: [] });
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
876
1001
|
app.post('/api/agent/skills/install', async (req, res) => {
|
|
877
1002
|
try {
|
|
878
1003
|
const { slug, target, registryUrl } = req.body || {};
|
|
@@ -1241,13 +1366,9 @@ export function createGateway(appMount = null) {
|
|
|
1241
1366
|
}
|
|
1242
1367
|
}
|
|
1243
1368
|
const timeoutMs = typeof timeout_ms === 'number' ? timeout_ms : config.agentTimeoutMs || 0;
|
|
1244
|
-
|
|
1245
|
-
if (usePi && !isPiAvailable())
|
|
1246
|
-
|
|
1247
|
-
error: 'Pi runner requested but not available. Install optional deps: npm install @mariozechner/pi-agent-core @mariozechner/pi-ai @mariozechner/pi-coding-agent',
|
|
1248
|
-
});
|
|
1249
|
-
return;
|
|
1250
|
-
}
|
|
1369
|
+
let usePi = config.agentUsePi || use_pi === true || use_pi === '1';
|
|
1370
|
+
if (usePi && !isPiAvailable())
|
|
1371
|
+
usePi = false;
|
|
1251
1372
|
try {
|
|
1252
1373
|
const result = await withSessionLock(id, () => usePi
|
|
1253
1374
|
? runAgentTurnWithPi({
|
|
@@ -1793,7 +1914,6 @@ export function attachWebSocket(server, onConnection = () => { }) {
|
|
|
1793
1914
|
return { wss, broadcast };
|
|
1794
1915
|
}
|
|
1795
1916
|
export function startGateway() {
|
|
1796
|
-
initDb(config.dbPath);
|
|
1797
1917
|
const app = createGateway();
|
|
1798
1918
|
const server = createServer(app);
|
|
1799
1919
|
const { broadcast } = attachWebSocket(server, (ws) => {
|
|
@@ -1815,6 +1935,12 @@ export function startGateway() {
|
|
|
1815
1935
|
return { app, server };
|
|
1816
1936
|
}
|
|
1817
1937
|
if (process.argv[1]?.includes('server')) {
|
|
1818
|
-
|
|
1938
|
+
(async () => {
|
|
1939
|
+
await initDb(config.dbPath);
|
|
1940
|
+
startGateway();
|
|
1941
|
+
})().catch((err) => {
|
|
1942
|
+
console.error(err);
|
|
1943
|
+
process.exit(1);
|
|
1944
|
+
});
|
|
1819
1945
|
}
|
|
1820
1946
|
//# sourceMappingURL=server.js.map
|