@sulala/agent-os 0.1.24 → 0.1.26

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/cli.js CHANGED
@@ -8451,6 +8451,28 @@ var init_skill_tools = () => {};
8451
8451
  import { readFile as readFile5, readdir as readdir3, cp, mkdir as mkdir3, writeFile as writeFile3, rm } from "fs/promises";
8452
8452
  import { join as join5, resolve as resolve2, basename } from "path";
8453
8453
  import { tmpdir } from "os";
8454
+ async function readSkillMeta(skillDir, subEntries) {
8455
+ if (!subEntries.includes(SULALA_META_FILE))
8456
+ return null;
8457
+ try {
8458
+ const raw = await readFile5(join5(skillDir, SULALA_META_FILE), "utf-8");
8459
+ const data = JSON.parse(raw);
8460
+ return typeof data.version === "string" && data.version.trim() !== "" ? { version: data.version.trim() } : null;
8461
+ } catch {
8462
+ return null;
8463
+ }
8464
+ }
8465
+ async function writeSkillMeta(skillId, meta) {
8466
+ const skillDir = join5(getSkillsDir(), skillId);
8467
+ const payload = {};
8468
+ if (meta.version?.trim())
8469
+ payload.version = meta.version.trim();
8470
+ if (meta.source?.trim())
8471
+ payload.source = meta.source.trim();
8472
+ if (Object.keys(payload).length === 0)
8473
+ return;
8474
+ await writeFile3(join5(skillDir, SULALA_META_FILE), JSON.stringify(payload, null, 0), "utf-8");
8475
+ }
8454
8476
  async function loadSkill(name) {
8455
8477
  const dir = join5(getSkillsDir(), name);
8456
8478
  let entries;
@@ -8693,10 +8715,14 @@ async function listSkills() {
8693
8715
  continue;
8694
8716
  const skillName = doc.name ?? name;
8695
8717
  const requiredEnv = await getRequiredEnvForSkill(skillDir, subEntries, doc);
8718
+ const docVersion = doc.version?.trim() || undefined;
8719
+ const meta = await readSkillMeta(skillDir, subEntries);
8720
+ const version = docVersion ?? meta?.version;
8696
8721
  results.push({
8697
8722
  id: name,
8698
8723
  name: skillName,
8699
8724
  description: doc.description,
8725
+ version,
8700
8726
  tools: (doc.tools ?? []).map((t) => ({ id: t.id, description: t.description })),
8701
8727
  required_env: requiredEnv.length ? requiredEnv : undefined,
8702
8728
  system: SYSTEM_SKILL_IDS.has(name)
@@ -8766,7 +8792,7 @@ async function installSystemSkills() {
8766
8792
  }
8767
8793
  return { installed };
8768
8794
  }
8769
- async function installSkillFromUrl(url, explicitId) {
8795
+ async function installSkillFromUrl(url, explicitId, meta) {
8770
8796
  const urlLower = url.toLowerCase();
8771
8797
  const isStoreSkillContentUrl = urlLower.includes("/api/sulalahub/skills/") && !urlLower.includes("/download") && !urlLower.endsWith(".zip");
8772
8798
  const headers = isStoreSkillContentUrl ? { Accept: "application/zip" } : {};
@@ -8781,31 +8807,35 @@ async function installSkillFromUrl(url, explicitId) {
8781
8807
  const tmpDir = join5(tmpdir(), `agent-os-skill-${Date.now()}`);
8782
8808
  await mkdir3(tmpDir, { recursive: true });
8783
8809
  try {
8810
+ let destId;
8784
8811
  if (isZip) {
8785
8812
  const zipPath = join5(tmpDir, "archive.zip");
8786
8813
  await writeFile3(zipPath, new Uint8Array(buf));
8787
- const proc2 = Bun.spawn({ cmd: ["unzip", "-q", "-o", zipPath, "-d", tmpDir], stdout: "ignore", stderr: "pipe" });
8788
- const exit2 = await proc2.exited;
8789
- if (exit2 !== 0) {
8790
- const err = await new Response(proc2.stderr).text();
8814
+ const proc = Bun.spawn({ cmd: ["unzip", "-q", "-o", zipPath, "-d", tmpDir], stdout: "ignore", stderr: "pipe" });
8815
+ const exit = await proc.exited;
8816
+ if (exit !== 0) {
8817
+ const err = await new Response(proc.stderr).text();
8791
8818
  throw new Error(`unzip failed: ${err}`);
8792
8819
  }
8793
- const { id: id2, sourcePath: sourcePath2 } = await chooseSkillRootFromExtract(tmpDir, "archive.zip");
8794
- const destId2 = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id2;
8795
- await cp(sourcePath2, join5(skillsDir, destId2), { recursive: true });
8796
- return { id: destId2 };
8797
- }
8798
- const tarPath = join5(tmpDir, "archive.tar.gz");
8799
- await writeFile3(tarPath, new Uint8Array(buf));
8800
- const proc = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "ignore", stderr: "pipe" });
8801
- const exit = await proc.exited;
8802
- if (exit !== 0) {
8803
- const err = await new Response(proc.stderr).text();
8804
- throw new Error(`tar extract failed: ${err}`);
8805
- }
8806
- const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.tar.gz");
8807
- const destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
8808
- await cp(sourcePath, join5(skillsDir, destId), { recursive: true });
8820
+ const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.zip");
8821
+ destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
8822
+ await cp(sourcePath, join5(skillsDir, destId), { recursive: true });
8823
+ } else {
8824
+ const tarPath = join5(tmpDir, "archive.tar.gz");
8825
+ await writeFile3(tarPath, new Uint8Array(buf));
8826
+ const proc = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "ignore", stderr: "pipe" });
8827
+ const exit = await proc.exited;
8828
+ if (exit !== 0) {
8829
+ const err = await new Response(proc.stderr).text();
8830
+ throw new Error(`tar extract failed: ${err}`);
8831
+ }
8832
+ const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.tar.gz");
8833
+ destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
8834
+ await cp(sourcePath, join5(skillsDir, destId), { recursive: true });
8835
+ }
8836
+ if (meta?.version?.trim() || meta?.source?.trim()) {
8837
+ await writeSkillMeta(destId, { version: meta.version, source: meta.source ?? (explicitId ? "hub" : undefined) });
8838
+ }
8809
8839
  return { id: destId };
8810
8840
  } finally {
8811
8841
  await rm(tmpDir, { recursive: true, force: true });
@@ -8864,7 +8894,7 @@ async function installSkillFromSkillMd(buffer, filename, explicitId) {
8864
8894
  await writeFile3(join5(destDir, "SKILL.md"), text, "utf-8");
8865
8895
  return { id };
8866
8896
  }
8867
- var SYSTEM_SKILL_IDS, DEFAULT_SYSTEM_SKILL_IDS;
8897
+ var SYSTEM_SKILL_IDS, DEFAULT_SYSTEM_SKILL_IDS, SULALA_META_FILE = ".sulala-meta.json";
8868
8898
  var init_loader = __esm(() => {
8869
8899
  init_config();
8870
8900
  init_tool_registry();
@@ -26095,8 +26125,10 @@ async function handleSkillInstall(req) {
26095
26125
  return jsonResponse({ error: "Provide path or url" }, 400);
26096
26126
  }
26097
26127
  const slug = typeof body.slug === "string" && body.slug.trim() !== "" ? body.slug.trim() : undefined;
26128
+ const version2 = typeof body.version === "string" && body.version.trim() !== "" ? body.version.trim() : undefined;
26129
+ const meta = version2 || slug ? { version: version2, source: slug ? "hub" : undefined } : undefined;
26098
26130
  try {
26099
- const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug);
26131
+ const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug, meta);
26100
26132
  return jsonResponse({ skill: result }, 201);
26101
26133
  } catch (err) {
26102
26134
  const msg = errorMessage(err);
@@ -30389,6 +30421,7 @@ Commands:
30389
30421
  onboard First-time setup: create config, seed agents & skills, open dashboard
30390
30422
  update Update package from npm and system agents/skills
30391
30423
  run <agent_id> <task> Run an agent with a one-off task
30424
+ skill install <slug> Install a skill from the store by slug (e.g. crm-hubspot)
30392
30425
  dashboard-token [--regenerate] Show or regenerate dashboard login token (copy to log in)
30393
30426
 
30394
30427
  Examples:
@@ -30399,6 +30432,7 @@ Examples:
30399
30432
  sulala onboard
30400
30433
  sulala update
30401
30434
  sulala run echo_agent What is 2+2?
30435
+ sulala skill install crm-hubspot
30402
30436
  sulala dashboard-token
30403
30437
  sulala dashboard-token --regenerate
30404
30438
  `);
@@ -30520,14 +30554,21 @@ async function cmdOnboard() {
30520
30554
  const started = await startServerDaemonIfNeeded();
30521
30555
  if (started) {
30522
30556
  console.log("Server starting in background. Use 'sulala stop' to stop.");
30523
- await new Promise((r) => setTimeout(r, 1500));
30524
30557
  }
30525
- openDashboard();
30526
30558
  const token = await getDashboardSecret();
30527
30559
  console.log("");
30528
30560
  console.log("Dashboard login token (copy and paste in the dashboard):");
30529
30561
  console.log(token);
30530
30562
  console.log("");
30563
+ const readline = await import("readline");
30564
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30565
+ const answer = await new Promise((resolve5) => rl.question("Go to dashboard? (y/n): ", resolve5));
30566
+ rl.close();
30567
+ if (/^y(es)?$/i.test(answer.trim())) {
30568
+ if (started)
30569
+ await new Promise((r) => setTimeout(r, 1500));
30570
+ openDashboard();
30571
+ }
30531
30572
  }
30532
30573
  var NPM_PACKAGE = "@sulala/agent-os";
30533
30574
  async function cmdUpdate() {
@@ -30605,6 +30646,57 @@ async function cmdRun(args) {
30605
30646
  process.exit(1);
30606
30647
  }
30607
30648
  }
30649
+ async function cmdSkill(args) {
30650
+ const sub = args[0]?.toLowerCase();
30651
+ const rest = args.slice(1);
30652
+ if (sub === "install") {
30653
+ await cmdSkillInstall(rest);
30654
+ return;
30655
+ }
30656
+ if (!sub || sub === "help" || sub === "-h" || sub === "--help") {
30657
+ console.log("Usage: sulala skill install <slug>");
30658
+ console.log(" Install a skill from the store by slug (e.g. crm-hubspot).");
30659
+ console.log(" Installs the latest version; you can pin a specific version in your skill config if needed.");
30660
+ console.log("");
30661
+ console.log("Example: sulala skill install crm-hubspot");
30662
+ return;
30663
+ }
30664
+ console.error(`Unknown subcommand: skill ${sub}`);
30665
+ console.error("Usage: sulala skill install <slug>");
30666
+ process.exit(1);
30667
+ }
30668
+ async function cmdSkillInstall(args) {
30669
+ const slug = args[0]?.trim();
30670
+ if (!slug) {
30671
+ console.error("Usage: sulala skill install <slug>");
30672
+ console.error("Example: sulala skill install crm-hubspot");
30673
+ process.exit(1);
30674
+ }
30675
+ const { skills, storeBase } = await getStoreRegistry();
30676
+ if (!storeBase) {
30677
+ console.error("Could not reach the skills store. Check config (skills_registry_url) or run 'sulala onboard' first.");
30678
+ process.exit(1);
30679
+ }
30680
+ const entry = skills.find((s) => s.slug === slug);
30681
+ if (!entry) {
30682
+ console.error(`Skill '${slug}' not found in the store. Check the slug at hub.sulala.ai or install from the dashboard (Skills page).`);
30683
+ process.exit(1);
30684
+ }
30685
+ const downloadUrl = entry.downloadUrl ?? `${storeBase}/api/sulalahub/skills/${encodeURIComponent(slug)}/download`;
30686
+ const version2 = entry.version;
30687
+ try {
30688
+ const { id } = await installSkillFromUrl(downloadUrl, slug, {
30689
+ version: version2 ?? undefined,
30690
+ source: "hub"
30691
+ });
30692
+ const verStr = version2 ? ` (v${version2})` : "";
30693
+ console.log(`Installed ${id}${verStr}. Add it to your agent in the dashboard (Edit agent \u2192 Skills).`);
30694
+ } catch (err) {
30695
+ const msg = err instanceof Error ? err.message : String(err);
30696
+ console.error("Install failed:", msg);
30697
+ process.exit(1);
30698
+ }
30699
+ }
30608
30700
  async function main() {
30609
30701
  if (!process.versions.bun) {
30610
30702
  console.error("Sulala CLI requires Bun. Use: bun run src/cli.ts");
@@ -30634,6 +30726,9 @@ async function main() {
30634
30726
  case "run":
30635
30727
  await cmdRun(rest);
30636
30728
  break;
30729
+ case "skill":
30730
+ await cmdSkill(rest);
30731
+ break;
30637
30732
  case "dashboard-token":
30638
30733
  await cmdDashboardToken(rest);
30639
30734
  break;
package/dist/index.js CHANGED
@@ -19606,6 +19606,29 @@ function createTokenRequestTool(skillId, toolId, baseUrl, description) {
19606
19606
  init_config();
19607
19607
  var SYSTEM_SKILL_IDS = new Set(["memory"]);
19608
19608
  var DEFAULT_SYSTEM_SKILL_IDS = ["memory", "date", "fetch", "jq", "file-search"];
19609
+ var SULALA_META_FILE = ".sulala-meta.json";
19610
+ async function readSkillMeta(skillDir, subEntries) {
19611
+ if (!subEntries.includes(SULALA_META_FILE))
19612
+ return null;
19613
+ try {
19614
+ const raw = await readFile5(join6(skillDir, SULALA_META_FILE), "utf-8");
19615
+ const data = JSON.parse(raw);
19616
+ return typeof data.version === "string" && data.version.trim() !== "" ? { version: data.version.trim() } : null;
19617
+ } catch {
19618
+ return null;
19619
+ }
19620
+ }
19621
+ async function writeSkillMeta(skillId, meta) {
19622
+ const skillDir = join6(getSkillsDir(), skillId);
19623
+ const payload = {};
19624
+ if (meta.version?.trim())
19625
+ payload.version = meta.version.trim();
19626
+ if (meta.source?.trim())
19627
+ payload.source = meta.source.trim();
19628
+ if (Object.keys(payload).length === 0)
19629
+ return;
19630
+ await writeFile3(join6(skillDir, SULALA_META_FILE), JSON.stringify(payload, null, 0), "utf-8");
19631
+ }
19609
19632
  async function loadSkill(name) {
19610
19633
  const dir = join6(getSkillsDir(), name);
19611
19634
  let entries;
@@ -19848,10 +19871,14 @@ async function listSkills() {
19848
19871
  continue;
19849
19872
  const skillName = doc.name ?? name;
19850
19873
  const requiredEnv = await getRequiredEnvForSkill(skillDir, subEntries, doc);
19874
+ const docVersion = doc.version?.trim() || undefined;
19875
+ const meta = await readSkillMeta(skillDir, subEntries);
19876
+ const version = docVersion ?? meta?.version;
19851
19877
  results.push({
19852
19878
  id: name,
19853
19879
  name: skillName,
19854
19880
  description: doc.description,
19881
+ version,
19855
19882
  tools: (doc.tools ?? []).map((t) => ({ id: t.id, description: t.description })),
19856
19883
  required_env: requiredEnv.length ? requiredEnv : undefined,
19857
19884
  system: SYSTEM_SKILL_IDS.has(name)
@@ -19921,7 +19948,7 @@ async function installSystemSkills() {
19921
19948
  }
19922
19949
  return { installed };
19923
19950
  }
19924
- async function installSkillFromUrl(url, explicitId) {
19951
+ async function installSkillFromUrl(url, explicitId, meta) {
19925
19952
  const urlLower = url.toLowerCase();
19926
19953
  const isStoreSkillContentUrl = urlLower.includes("/api/sulalahub/skills/") && !urlLower.includes("/download") && !urlLower.endsWith(".zip");
19927
19954
  const headers = isStoreSkillContentUrl ? { Accept: "application/zip" } : {};
@@ -19936,31 +19963,35 @@ async function installSkillFromUrl(url, explicitId) {
19936
19963
  const tmpDir = join6(tmpdir(), `agent-os-skill-${Date.now()}`);
19937
19964
  await mkdir3(tmpDir, { recursive: true });
19938
19965
  try {
19966
+ let destId;
19939
19967
  if (isZip) {
19940
19968
  const zipPath = join6(tmpDir, "archive.zip");
19941
19969
  await writeFile3(zipPath, new Uint8Array(buf));
19942
- const proc2 = Bun.spawn({ cmd: ["unzip", "-q", "-o", zipPath, "-d", tmpDir], stdout: "ignore", stderr: "pipe" });
19943
- const exit2 = await proc2.exited;
19944
- if (exit2 !== 0) {
19945
- const err = await new Response(proc2.stderr).text();
19970
+ const proc = Bun.spawn({ cmd: ["unzip", "-q", "-o", zipPath, "-d", tmpDir], stdout: "ignore", stderr: "pipe" });
19971
+ const exit = await proc.exited;
19972
+ if (exit !== 0) {
19973
+ const err = await new Response(proc.stderr).text();
19946
19974
  throw new Error(`unzip failed: ${err}`);
19947
19975
  }
19948
- const { id: id2, sourcePath: sourcePath2 } = await chooseSkillRootFromExtract(tmpDir, "archive.zip");
19949
- const destId2 = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id2;
19950
- await cp(sourcePath2, join6(skillsDir, destId2), { recursive: true });
19951
- return { id: destId2 };
19952
- }
19953
- const tarPath = join6(tmpDir, "archive.tar.gz");
19954
- await writeFile3(tarPath, new Uint8Array(buf));
19955
- const proc = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "ignore", stderr: "pipe" });
19956
- const exit = await proc.exited;
19957
- if (exit !== 0) {
19958
- const err = await new Response(proc.stderr).text();
19959
- throw new Error(`tar extract failed: ${err}`);
19960
- }
19961
- const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.tar.gz");
19962
- const destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
19963
- await cp(sourcePath, join6(skillsDir, destId), { recursive: true });
19976
+ const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.zip");
19977
+ destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
19978
+ await cp(sourcePath, join6(skillsDir, destId), { recursive: true });
19979
+ } else {
19980
+ const tarPath = join6(tmpDir, "archive.tar.gz");
19981
+ await writeFile3(tarPath, new Uint8Array(buf));
19982
+ const proc = Bun.spawn({ cmd: ["tar", "-xzf", tarPath, "-C", tmpDir], stdout: "ignore", stderr: "pipe" });
19983
+ const exit = await proc.exited;
19984
+ if (exit !== 0) {
19985
+ const err = await new Response(proc.stderr).text();
19986
+ throw new Error(`tar extract failed: ${err}`);
19987
+ }
19988
+ const { id, sourcePath } = await chooseSkillRootFromExtract(tmpDir, "archive.tar.gz");
19989
+ destId = explicitId != null && explicitId.trim() !== "" ? slugToSkillId(explicitId) : id;
19990
+ await cp(sourcePath, join6(skillsDir, destId), { recursive: true });
19991
+ }
19992
+ if (meta?.version?.trim() || meta?.source?.trim()) {
19993
+ await writeSkillMeta(destId, { version: meta.version, source: meta.source ?? (explicitId ? "hub" : undefined) });
19994
+ }
19964
19995
  return { id: destId };
19965
19996
  } finally {
19966
19997
  await rm(tmpDir, { recursive: true, force: true });
@@ -27926,8 +27957,10 @@ async function handleSkillInstall(req) {
27926
27957
  return jsonResponse({ error: "Provide path or url" }, 400);
27927
27958
  }
27928
27959
  const slug = typeof body.slug === "string" && body.slug.trim() !== "" ? body.slug.trim() : undefined;
27960
+ const version2 = typeof body.version === "string" && body.version.trim() !== "" ? body.version.trim() : undefined;
27961
+ const meta = version2 || slug ? { version: version2, source: slug ? "hub" : undefined } : undefined;
27929
27962
  try {
27930
- const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug);
27963
+ const result = hasPath ? await installSkillFromPath(body.path.trim()) : await installSkillFromUrl(body.url.trim(), slug, meta);
27931
27964
  return jsonResponse({ skill: result }, 201);
27932
27965
  } catch (err) {
27933
27966
  const msg = errorMessage(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sulala/agent-os",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },