shadow-claw 1.34.5 → 1.35.0
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/.well-known/mcp/server-card.json +1 -1
- package/.well-known/mcp.json +1 -1
- package/README.md +5 -1
- package/dist/cli/chunk-BCLxzLO92.js +5 -0
- package/dist/cli/chunk-BHNCi3ui2.js +1 -0
- package/dist/cli/chunk-BMPhLSn8.js +1 -0
- package/dist/cli/chunk-BMW6QB5-2.js +27 -0
- package/dist/cli/chunk-BQCWtrzh.js +1 -0
- package/dist/cli/chunk-BQCWtrzh2.js +1 -0
- package/dist/cli/chunk-BdJUjMuz.js +1 -0
- package/dist/cli/chunk-C47WdxKx2.js +1 -0
- package/dist/cli/{chunk-D9dku_3t.js → chunk-CAqvQuGr.js} +1 -1
- package/dist/cli/{chunk-DoVxObGa2.js → chunk-CHzhUS1N2.js} +1 -1
- package/dist/cli/chunk-CUMlq7Jw2.js +263 -0
- package/dist/cli/chunk-CXi9LU9e2.js +110 -0
- package/dist/cli/chunk-ChefnX-r.js +110 -0
- package/dist/cli/{chunk-zvi71-77.js → chunk-D3JxbzjI2.js} +15 -15
- package/dist/cli/chunk-D7VYMl092.js +1 -0
- package/dist/cli/chunk-DNIduiJ82.js +91 -0
- package/dist/cli/{chunk-C4UfzPK4.js → chunk-DTbrldk_.js} +1 -1
- package/dist/cli/chunk-DZ8YBXax.js +969 -0
- package/dist/cli/chunk-DduRvw8B2.js +1 -0
- package/dist/cli/chunk-DnYxGjdu2.js +1 -0
- package/dist/cli/chunk-Ea7nXmgb2.js +1 -0
- package/dist/cli/{chunk-BRiGkTIi.js → chunk-EohNY8vW.js} +1 -1
- package/dist/cli/chunk-LIIpZlwP2.js +1 -0
- package/dist/cli/chunk-LIJZHJml2.js +1 -0
- package/dist/cli/chunk-MHBh2BgF2.js +91 -0
- package/dist/cli/{chunk-BQ7lHA7c.js → chunk-SKgLGyTY.js} +1 -1
- package/dist/cli/chunk-kcJSXcbq2.js +1 -0
- package/dist/cli/cli.js +12 -11
- package/dist/headless-agent.js +191 -187
- package/dist/lib/components/index.js +21 -21
- package/dist/lib/components/settings/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/settings/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/settings/shadow-claw-git.js +1 -1
- package/dist/lib/components/settings/shadow-claw-llm.js +1 -1
- package/dist/lib/components/settings/shadow-claw-networking.js +1 -1
- package/dist/lib/components/settings/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/settings/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/settings/shadow-claw-storage.js +1 -1
- package/dist/lib/components/settings/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/settings/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw-channel-config.js +1 -1
- package/dist/lib/components/shadow-claw-channels.js +1 -1
- package/dist/lib/components/shadow-claw-chat.js +1 -1
- package/dist/lib/components/shadow-claw-control-plane.js +1 -1
- package/dist/lib/components/shadow-claw-conversations.js +1 -1
- package/dist/lib/components/shadow-claw-file-viewer.js +1 -1
- package/dist/lib/components/shadow-claw-files.js +1 -1
- package/dist/lib/components/shadow-claw-git.js +1 -1
- package/dist/lib/components/shadow-claw-llm.js +1 -1
- package/dist/lib/components/shadow-claw-networking.js +1 -1
- package/dist/lib/components/shadow-claw-notifications.js +1 -1
- package/dist/lib/components/shadow-claw-pages.js +1 -1
- package/dist/lib/components/shadow-claw-peerjs.js +1 -1
- package/dist/lib/components/shadow-claw-settings.js +1 -1
- package/dist/lib/components/shadow-claw-storage.js +1 -1
- package/dist/lib/components/shadow-claw-task-server.js +1 -1
- package/dist/lib/components/shadow-claw-tasks.js +1 -1
- package/dist/lib/components/shadow-claw-terminal.js +1 -1
- package/dist/lib/components/shadow-claw-tools.js +1 -1
- package/dist/lib/components/shadow-claw-webvm.js +1 -1
- package/dist/lib/components/shadow-claw.js +1 -1
- package/dist/lib/{configurePeerJs-DSRcNp3v.js → configurePeerJs-CdPzmpyI.js} +2 -2
- package/dist/lib/{configurePeerJs-DSRcNp3v.js.map → configurePeerJs-CdPzmpyI.js.map} +1 -1
- package/dist/lib/index.js +21 -21
- package/dist/lib/{initControlPlane-CWfE_Oan.js → initControlPlane-BLymRW34.js} +7 -7
- package/dist/lib/{initControlPlane-CWfE_Oan.js.map → initControlPlane-BLymRW34.js.map} +1 -1
- package/dist/lib/{orchestrator-BEUug1TW.js → orchestrator-DZkhJj7f.js} +13 -2
- package/dist/lib/{orchestrator-BEUug1TW.js.map → orchestrator-DZkhJj7f.js.map} +1 -1
- package/dist/lib/{orchestrator-CGUzfGbE.js → orchestrator-ZsQTRknI.js} +5 -5
- package/dist/lib/{orchestrator-CGUzfGbE.js.map → orchestrator-ZsQTRknI.js.map} +1 -1
- package/dist/lib/{peerjs-BSD88rT3.js → peerjs-CJYfnR5V.js} +3 -3
- package/dist/lib/{peerjs-BSD88rT3.js.map → peerjs-CJYfnR5V.js.map} +1 -1
- package/dist/lib/{push-client-CC3qifgc.js → push-client-C9hIPqLZ.js} +2 -2
- package/dist/lib/{push-client-CC3qifgc.js.map → push-client-C9hIPqLZ.js.map} +1 -1
- package/dist/lib/{shadow-claw-BWS-UVnE.js → shadow-claw-8qL2-QoK.js} +4 -4
- package/dist/lib/{shadow-claw-BWS-UVnE.js.map → shadow-claw-8qL2-QoK.js.map} +1 -1
- package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js → shadow-claw-channel-config-CvnbqGBy.js} +3 -3
- package/dist/lib/{shadow-claw-channel-config-DwMn3nFJ.js.map → shadow-claw-channel-config-CvnbqGBy.js.map} +1 -1
- package/dist/lib/{shadow-claw-channels-BdE4cUlx.js → shadow-claw-channels-CkCvjWn1.js} +2 -2
- package/dist/lib/{shadow-claw-channels-BdE4cUlx.js.map → shadow-claw-channels-CkCvjWn1.js.map} +1 -1
- package/dist/lib/{shadow-claw-chat-DqaMoE9C.js → shadow-claw-chat-B1YEHKN3.js} +3 -3
- package/dist/lib/{shadow-claw-chat-DqaMoE9C.js.map → shadow-claw-chat-B1YEHKN3.js.map} +1 -1
- package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js → shadow-claw-control-plane-jjo789IE.js} +6 -6
- package/dist/lib/{shadow-claw-control-plane-D3DUDO29.js.map → shadow-claw-control-plane-jjo789IE.js.map} +1 -1
- package/dist/lib/{shadow-claw-conversations-XT-OXogj.js → shadow-claw-conversations-MMT7wYH3.js} +2 -2
- package/dist/lib/{shadow-claw-conversations-XT-OXogj.js.map → shadow-claw-conversations-MMT7wYH3.js.map} +1 -1
- package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js → shadow-claw-file-viewer-qSkD1RQK.js} +2 -2
- package/dist/lib/{shadow-claw-file-viewer-DJqxyd97.js.map → shadow-claw-file-viewer-qSkD1RQK.js.map} +1 -1
- package/dist/lib/{shadow-claw-files-CUEJlA1o.js → shadow-claw-files-Cg8ycQ_N.js} +2 -2
- package/dist/lib/{shadow-claw-files-CUEJlA1o.js.map → shadow-claw-files-Cg8ycQ_N.js.map} +1 -1
- package/dist/lib/{shadow-claw-git-D_iVcb86.js → shadow-claw-git-gaClz-ZC.js} +3 -3
- package/dist/lib/{shadow-claw-git-D_iVcb86.js.map → shadow-claw-git-gaClz-ZC.js.map} +1 -1
- package/dist/lib/{shadow-claw-llm-C2msAsyU.js → shadow-claw-llm-DyOz0qzT.js} +2 -2
- package/dist/lib/{shadow-claw-llm-C2msAsyU.js.map → shadow-claw-llm-DyOz0qzT.js.map} +1 -1
- package/dist/lib/{shadow-claw-networking-D2NvodC-.js → shadow-claw-networking-B2xx0UUR.js} +2 -2
- package/dist/lib/{shadow-claw-networking-D2NvodC-.js.map → shadow-claw-networking-B2xx0UUR.js.map} +1 -1
- package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js → shadow-claw-notifications-BjKfZy0k.js} +2 -2
- package/dist/lib/{shadow-claw-notifications-BcvVn_vu.js.map → shadow-claw-notifications-BjKfZy0k.js.map} +1 -1
- package/dist/lib/{shadow-claw-pages-CTMXRhEX.js → shadow-claw-pages-CJ2tsMtL.js} +2 -2
- package/dist/lib/{shadow-claw-pages-CTMXRhEX.js.map → shadow-claw-pages-CJ2tsMtL.js.map} +1 -1
- package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js → shadow-claw-peerjs-CazCkXQ1.js} +3 -3
- package/dist/lib/{shadow-claw-peerjs-C2YvypqM.js.map → shadow-claw-peerjs-CazCkXQ1.js.map} +1 -1
- package/dist/lib/{shadow-claw-settings-B0hiDwEt.js → shadow-claw-settings-qro6pO9I.js} +2 -2
- package/dist/lib/{shadow-claw-settings-B0hiDwEt.js.map → shadow-claw-settings-qro6pO9I.js.map} +1 -1
- package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js → shadow-claw-storage-ewDdvCGa.js} +2 -2
- package/dist/lib/{shadow-claw-storage-Dt4yg4Q4.js.map → shadow-claw-storage-ewDdvCGa.js.map} +1 -1
- package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js → shadow-claw-task-server-DpqQcNIs.js} +2 -2
- package/dist/lib/{shadow-claw-task-server-B2gbZaz4.js.map → shadow-claw-task-server-DpqQcNIs.js.map} +1 -1
- package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js → shadow-claw-tasks-B2fMF4qE.js} +2 -2
- package/dist/lib/{shadow-claw-tasks-Bxq-niuM.js.map → shadow-claw-tasks-B2fMF4qE.js.map} +1 -1
- package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js → shadow-claw-terminal-B25kQJC3.js} +2 -2
- package/dist/lib/{shadow-claw-terminal-BfbW3xtg.js.map → shadow-claw-terminal-B25kQJC3.js.map} +1 -1
- package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js → shadow-claw-tools-B0y8JzoA.js} +10 -3
- package/dist/lib/{shadow-claw-tools-BNSAaK0Y.js.map → shadow-claw-tools-B0y8JzoA.js.map} +1 -1
- package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js → shadow-claw-webvm-CoCedIza.js} +2 -2
- package/dist/lib/{shadow-claw-webvm-Dc2KmEXq.js.map → shadow-claw-webvm-CoCedIza.js.map} +1 -1
- package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js → syncWebMcpRegistration-JKLALq4e.js} +2 -2
- package/dist/lib/{syncWebMcpRegistration-BIL83P0Q.js.map → syncWebMcpRegistration-JKLALq4e.js.map} +1 -1
- package/dist/lib/worker/utils/sandboxedEval.d.ts +8 -0
- package/dist/public/.well-known/mcp/server-card.json +1 -1
- package/dist/public/.well-known/mcp.json +1 -1
- package/dist/public/AGENTS.md +1 -1
- package/dist/public/README.md +5 -1
- package/dist/public/agent.worker.js +1 -1
- package/dist/public/components/shadow-claw-tools/shadow-claw-tools.html +1 -1
- package/dist/public/{configurePeerJs-B925t9C7.js → configurePeerJs-BrHeJGw1.js} +1 -1
- package/dist/public/docs/decisions/headless-cli-agent-participant.md +1 -1
- package/dist/public/docs/example/article/index.html +1 -1
- package/dist/public/docs/publishing/index.html +1 -1
- package/dist/public/docs/skill-creator/index.html +1 -1
- package/dist/public/docs/subsystems/cli.md +33 -23
- package/dist/public/docs/subsystems/skills.md +17 -0
- package/dist/public/docs/subsystems/tools.md +1 -1
- package/dist/public/e2e/README.md +2 -2
- package/dist/public/{e2e-bridge-CE1xleGn.js → e2e-bridge-D_lyH__p.js} +1 -1
- package/dist/public/{executeTool-DtTrsZHV.js → executeTool-b6DnRhcv.js} +2 -1
- package/dist/public/{file-viewer-LEEStSq-.js → file-viewer-CKrQoxZi.js} +1 -1
- package/dist/public/index.html +1 -1
- package/dist/public/index.js +1 -1
- package/dist/public/{initControlPlane-e2JgxXEb.js → initControlPlane-Dn-XFuxg.js} +1 -1
- package/dist/public/llms.txt +2 -0
- package/dist/public/main/index.html +1 -1
- package/dist/public/main/memory/index.html +1 -1
- package/dist/public/{orchestrator-Baif6kmu.js → orchestrator-0BnBtxHO.js} +3 -2
- package/dist/public/{orchestrator-TndREZPL.js → orchestrator-C3GKZriH.js} +1 -1
- package/dist/public/{peerjs-DE6WF3h2.js → peerjs-CgfLEp2N.js} +1 -1
- package/dist/public/{push-client-B_IkTpNm.js → push-client-D6DLiY1s.js} +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-channels-BCGz36Zl.js → shadow-claw-channels-DSijinZ9.js} +1 -1
- package/dist/public/{shadow-claw-chat-H9U9jbqD.js → shadow-claw-chat-BeHlAjnA.js} +1 -1
- package/dist/public/{shadow-claw-control-plane-CeKh64s2.js → shadow-claw-control-plane-BfF_AZSg.js} +2 -2
- package/dist/public/{shadow-claw-conversations-C26t1HXN.js → shadow-claw-conversations-Bb_2bVLJ.js} +1 -1
- package/dist/public/{shadow-claw-DBeXILmk.js → shadow-claw-f71DlRdV.js} +2 -2
- package/dist/public/{shadow-claw-file-viewer-DWUoTjkr.js → shadow-claw-file-viewer-B67000Mm.js} +1 -1
- package/dist/public/{shadow-claw-files-BQ8DSX-z.js → shadow-claw-files-DeZZYt01.js} +1 -1
- package/dist/public/{shadow-claw-git-CIXyISw-.js → shadow-claw-git-Ccy25LuW.js} +2 -2
- package/dist/public/{shadow-claw-llm-D_GpOSZE.js → shadow-claw-llm-XjYcTlXT.js} +1 -1
- package/dist/public/{shadow-claw-networking-BYp5K3B7.js → shadow-claw-networking-efXQtydQ.js} +1 -1
- package/dist/public/{shadow-claw-notifications-ZiXmPweY.js → shadow-claw-notifications-Cq2ZSeJa.js} +1 -1
- package/dist/public/{shadow-claw-pages-BHXwf3BD.js → shadow-claw-pages-BVFNSSb1.js} +1 -1
- package/dist/public/{shadow-claw-settings-BfvDvt9c.js → shadow-claw-settings-B3sHxPh9.js} +2 -2
- package/dist/public/{shadow-claw-storage-Bvn85DII.js → shadow-claw-storage-DNdlzCgC.js} +1 -1
- package/dist/public/{shadow-claw-task-server-DZL_d8FC.js → shadow-claw-task-server-BJq0rpju.js} +1 -1
- package/dist/public/{shadow-claw-tasks-Cy8jzd8q.js → shadow-claw-tasks-CkF1uMeZ.js} +1 -1
- package/dist/public/{shadow-claw-terminal-BgEXr82T.js → shadow-claw-terminal-CnHhcpA-.js} +1 -1
- package/dist/public/{shadow-claw-tools-BjnssRSi.js → shadow-claw-tools-iw0fljud.js} +8 -1
- package/dist/public/{shadow-claw-webvm-tZ1VDoVA.js → shadow-claw-webvm-Bliu4V__.js} +1 -1
- package/dist/public/{syncWebMcpRegistration-CLMvJ4kV.js → syncWebMcpRegistration-D91usEMu.js} +1 -1
- package/package.json +1 -1
- package/src/cli/cli.ts +26 -2
- package/src/cli/commands/agent.test.ts +421 -0
- package/src/cli/commands/agent.ts +270 -20
- package/src/components/shadow-claw-tools/shadow-claw-tools.html +7 -0
- package/src/worker/headless-agent.ts +14 -0
- package/src/worker/headless-types.ts +39 -0
- package/src/worker/utils/native-eval-executor.test.ts +36 -0
- package/src/worker/utils/native-eval-executor.ts +58 -0
- package/src/worker/utils/sandboxedEval.ts +36 -0
- package/dist/cli/chunk-1BmpU7jO2.js +0 -1
- package/dist/cli/chunk-54Euq8Ji2.js +0 -1
- package/dist/cli/chunk-BTitwCgT2.js +0 -88
- package/dist/cli/chunk-Bjh76Dai.js +0 -1081
- package/dist/cli/chunk-C2HeZQ6K.js +0 -1
- package/dist/cli/chunk-CxdpafWg.js +0 -1
- package/dist/cli/chunk-DEy72ZNV2.js +0 -88
- package/dist/cli/chunk-Da1BZyqf.js +0 -1
- package/dist/cli/chunk-FHsJD-Sf2.js +0 -27
- package/dist/cli/chunk-aWgoVB7x2.js +0 -1
- package/dist/cli/chunk-hOceZVB92.js +0 -371
- package/dist/cli/chunk-pwNc6VtC2.js +0 -1
|
@@ -1855,3 +1855,424 @@ describe("runAgentRun — SIGINT cancellation and process cleanup", () => {
|
|
|
1855
1855
|
expect(cleanupCalled).toBe(true);
|
|
1856
1856
|
});
|
|
1857
1857
|
});
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* TDD tests for the `agent import` subcommand.
|
|
1861
|
+
*/
|
|
1862
|
+
describe("agent import", () => {
|
|
1863
|
+
let tmpDir: string;
|
|
1864
|
+
|
|
1865
|
+
beforeEach(async () => {
|
|
1866
|
+
tmpDir = await mkdtemp(path.join(tmpdir(), "sc-agent-import-test-"));
|
|
1867
|
+
});
|
|
1868
|
+
|
|
1869
|
+
afterEach(async () => {
|
|
1870
|
+
const { closeSqliteDatabase } =
|
|
1871
|
+
await import("../../db/sqlite/openSqliteDatabase.js").catch(() => ({
|
|
1872
|
+
closeSqliteDatabase: () => {},
|
|
1873
|
+
}));
|
|
1874
|
+
closeSqliteDatabase?.();
|
|
1875
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
it("requires a URL argument and returns error if missing", async () => {
|
|
1879
|
+
const { runAgentCommand, runAgentImport } = await import("./agent.js");
|
|
1880
|
+
const resultCommand = await runAgentCommand("import", [], {
|
|
1881
|
+
workspace: tmpDir,
|
|
1882
|
+
quiet: true,
|
|
1883
|
+
});
|
|
1884
|
+
expect(resultCommand.success).toBe(false);
|
|
1885
|
+
expect(resultCommand.error).toMatch(/required/i);
|
|
1886
|
+
|
|
1887
|
+
const resultDirect = await runAgentImport("", {
|
|
1888
|
+
workspace: tmpDir,
|
|
1889
|
+
quiet: true,
|
|
1890
|
+
});
|
|
1891
|
+
expect(resultDirect.success).toBe(false);
|
|
1892
|
+
expect(resultDirect.error).toMatch(/required/i);
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
it("fetches manifest and imports all tools, skills, and scripts by default", async () => {
|
|
1896
|
+
const { runAgentImport, runAgentTools, runAgentSkills } =
|
|
1897
|
+
await import("./agent.js");
|
|
1898
|
+
|
|
1899
|
+
const manifest = {
|
|
1900
|
+
$schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
|
|
1901
|
+
name: "Weather Station Hub",
|
|
1902
|
+
description: "Weather station declarative tools and skills",
|
|
1903
|
+
skills: [
|
|
1904
|
+
{
|
|
1905
|
+
name: "weather-briefing",
|
|
1906
|
+
type: "skill-md",
|
|
1907
|
+
description: "Daily weather briefing",
|
|
1908
|
+
url: "../../.agents/skills/main/weather-briefing/SKILL.md",
|
|
1909
|
+
},
|
|
1910
|
+
],
|
|
1911
|
+
tools: [
|
|
1912
|
+
{
|
|
1913
|
+
name: "get_current_weather",
|
|
1914
|
+
description: "Fetch real-time weather",
|
|
1915
|
+
url: "../../.agents/tools/main/get_current_weather.json",
|
|
1916
|
+
},
|
|
1917
|
+
],
|
|
1918
|
+
scripts: [
|
|
1919
|
+
{
|
|
1920
|
+
name: "weather_helper",
|
|
1921
|
+
url: "../../.agents/scripts/main/weather_helper.js",
|
|
1922
|
+
},
|
|
1923
|
+
],
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
const skillContent = `---
|
|
1927
|
+
name: weather-briefing
|
|
1928
|
+
description: Daily weather briefing
|
|
1929
|
+
user-invocable: true
|
|
1930
|
+
---
|
|
1931
|
+
# Weather Briefing
|
|
1932
|
+
Fetch today's weather.
|
|
1933
|
+
`;
|
|
1934
|
+
|
|
1935
|
+
const toolContent = JSON.stringify({
|
|
1936
|
+
name: "get_current_weather",
|
|
1937
|
+
description: "Fetch real-time weather",
|
|
1938
|
+
input_schema: {
|
|
1939
|
+
type: "object",
|
|
1940
|
+
properties: {
|
|
1941
|
+
location: { type: "string" },
|
|
1942
|
+
},
|
|
1943
|
+
},
|
|
1944
|
+
execution: {
|
|
1945
|
+
type: "javascript",
|
|
1946
|
+
code: "return JSON.stringify({ temp: 21, condition: 'Sunny' });",
|
|
1947
|
+
},
|
|
1948
|
+
});
|
|
1949
|
+
|
|
1950
|
+
const scriptContent = 'export function helper() { return "weather"; }';
|
|
1951
|
+
|
|
1952
|
+
const mockFetch = jest.fn<any>().mockImplementation(async (url: any) => {
|
|
1953
|
+
const urlStr = String(url);
|
|
1954
|
+
if (urlStr.endsWith("index.json") || urlStr.includes(".well-known")) {
|
|
1955
|
+
return {
|
|
1956
|
+
ok: true,
|
|
1957
|
+
status: 200,
|
|
1958
|
+
statusText: "OK",
|
|
1959
|
+
json: async () => manifest,
|
|
1960
|
+
text: async () => JSON.stringify(manifest),
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
if (urlStr.endsWith("get_current_weather.json")) {
|
|
1964
|
+
return {
|
|
1965
|
+
ok: true,
|
|
1966
|
+
status: 200,
|
|
1967
|
+
statusText: "OK",
|
|
1968
|
+
text: async () => toolContent,
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
if (urlStr.endsWith("SKILL.md")) {
|
|
1972
|
+
return {
|
|
1973
|
+
ok: true,
|
|
1974
|
+
status: 200,
|
|
1975
|
+
statusText: "OK",
|
|
1976
|
+
text: async () => skillContent,
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
if (urlStr.endsWith("weather_helper.js")) {
|
|
1980
|
+
return {
|
|
1981
|
+
ok: true,
|
|
1982
|
+
status: 200,
|
|
1983
|
+
statusText: "OK",
|
|
1984
|
+
text: async () => scriptContent,
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
return {
|
|
1988
|
+
ok: false,
|
|
1989
|
+
status: 404,
|
|
1990
|
+
statusText: "Not Found",
|
|
1991
|
+
text: async () => "Not found",
|
|
1992
|
+
};
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1995
|
+
const result = await runAgentImport("https://example.com/weather", {
|
|
1996
|
+
workspace: tmpDir,
|
|
1997
|
+
fetchFn: mockFetch as any,
|
|
1998
|
+
quiet: true,
|
|
1999
|
+
});
|
|
2000
|
+
|
|
2001
|
+
expect(result.success).toBe(true);
|
|
2002
|
+
expect(result.importedCount).toBe(3);
|
|
2003
|
+
expect(result.failedCount).toBe(0);
|
|
2004
|
+
|
|
2005
|
+
// Verify files were written to disk in workspace
|
|
2006
|
+
const toolFile = path.join(
|
|
2007
|
+
tmpDir,
|
|
2008
|
+
".agents/tools/main/get_current_weather.json",
|
|
2009
|
+
);
|
|
2010
|
+
const skillFile = path.join(
|
|
2011
|
+
tmpDir,
|
|
2012
|
+
".agents/skills/main/weather-briefing/SKILL.md",
|
|
2013
|
+
);
|
|
2014
|
+
const scriptFile = path.join(
|
|
2015
|
+
tmpDir,
|
|
2016
|
+
".agents/scripts/main/weather_helper.js",
|
|
2017
|
+
);
|
|
2018
|
+
|
|
2019
|
+
expect(JSON.parse(await readFile(toolFile, "utf8"))).toEqual(
|
|
2020
|
+
JSON.parse(toolContent),
|
|
2021
|
+
);
|
|
2022
|
+
expect(await readFile(skillFile, "utf8")).toBe(skillContent);
|
|
2023
|
+
expect(await readFile(scriptFile, "utf8")).toBe(scriptContent);
|
|
2024
|
+
|
|
2025
|
+
// Verify declarative tools are listed in agent tools
|
|
2026
|
+
const toolsResult = await runAgentTools({ workspace: tmpDir, quiet: true });
|
|
2027
|
+
const importedTool = toolsResult.tools.find(
|
|
2028
|
+
(t: any) => t.name === "get_current_weather",
|
|
2029
|
+
);
|
|
2030
|
+
expect(importedTool).toBeDefined();
|
|
2031
|
+
expect(importedTool.description).toBe("Fetch real-time weather");
|
|
2032
|
+
expect(importedTool.headlessSafe).toBe(true);
|
|
2033
|
+
|
|
2034
|
+
// Verify skills are listed in agent skills
|
|
2035
|
+
const skillsResult = await runAgentSkills({
|
|
2036
|
+
workspace: tmpDir,
|
|
2037
|
+
quiet: true,
|
|
2038
|
+
});
|
|
2039
|
+
const importedSkill = skillsResult.skills.find(
|
|
2040
|
+
(s: any) => s.name === "weather-briefing",
|
|
2041
|
+
);
|
|
2042
|
+
expect(importedSkill).toBeDefined();
|
|
2043
|
+
});
|
|
2044
|
+
|
|
2045
|
+
it("supports selective filtering using options.tools", async () => {
|
|
2046
|
+
const { runAgentImport } = await import("./agent.js");
|
|
2047
|
+
|
|
2048
|
+
const manifest = {
|
|
2049
|
+
$schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
|
|
2050
|
+
name: "Selective Station",
|
|
2051
|
+
tools: [
|
|
2052
|
+
{
|
|
2053
|
+
name: "tool_alpha",
|
|
2054
|
+
description: "Tool Alpha",
|
|
2055
|
+
url: "../../.agents/tools/main/tool_alpha.json",
|
|
2056
|
+
},
|
|
2057
|
+
{
|
|
2058
|
+
name: "tool_beta",
|
|
2059
|
+
description: "Tool Beta",
|
|
2060
|
+
url: "../../.agents/tools/main/tool_beta.json",
|
|
2061
|
+
},
|
|
2062
|
+
],
|
|
2063
|
+
};
|
|
2064
|
+
|
|
2065
|
+
const makeTool = (name: string) =>
|
|
2066
|
+
JSON.stringify({
|
|
2067
|
+
name,
|
|
2068
|
+
description: `Description of ${name}`,
|
|
2069
|
+
input_schema: { type: "object", properties: {} },
|
|
2070
|
+
execution: { type: "javascript", code: "return 'ok';" },
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
const mockFetch = jest.fn<any>().mockImplementation(async (url: any) => {
|
|
2074
|
+
const urlStr = String(url);
|
|
2075
|
+
if (urlStr.includes(".well-known")) {
|
|
2076
|
+
return {
|
|
2077
|
+
ok: true,
|
|
2078
|
+
status: 200,
|
|
2079
|
+
statusText: "OK",
|
|
2080
|
+
json: async () => manifest,
|
|
2081
|
+
text: async () => JSON.stringify(manifest),
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
if (urlStr.endsWith("tool_alpha.json")) {
|
|
2085
|
+
return {
|
|
2086
|
+
ok: true,
|
|
2087
|
+
status: 200,
|
|
2088
|
+
statusText: "OK",
|
|
2089
|
+
text: async () => makeTool("tool_alpha"),
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
if (urlStr.endsWith("tool_beta.json")) {
|
|
2093
|
+
return {
|
|
2094
|
+
ok: true,
|
|
2095
|
+
status: 200,
|
|
2096
|
+
statusText: "OK",
|
|
2097
|
+
text: async () => makeTool("tool_beta"),
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
return { ok: false, status: 404, statusText: "Not Found" };
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
const result = await runAgentImport("https://example.com/hub", {
|
|
2104
|
+
workspace: tmpDir,
|
|
2105
|
+
tools: "tool_alpha",
|
|
2106
|
+
fetchFn: mockFetch as any,
|
|
2107
|
+
quiet: true,
|
|
2108
|
+
});
|
|
2109
|
+
|
|
2110
|
+
expect(result.success).toBe(true);
|
|
2111
|
+
expect(result.importedCount).toBe(1);
|
|
2112
|
+
expect(result.result?.tools[0].name).toBe("tool_alpha");
|
|
2113
|
+
expect(result.result?.tools[0].status).toBe("imported");
|
|
2114
|
+
|
|
2115
|
+
// tool_alpha exists, tool_beta does not
|
|
2116
|
+
await expect(
|
|
2117
|
+
access(
|
|
2118
|
+
path.join(tmpDir, ".agents/tools/main/tool_alpha.json"),
|
|
2119
|
+
constants.F_OK,
|
|
2120
|
+
),
|
|
2121
|
+
).resolves.toBeUndefined();
|
|
2122
|
+
await expect(
|
|
2123
|
+
access(
|
|
2124
|
+
path.join(tmpDir, ".agents/tools/main/tool_beta.json"),
|
|
2125
|
+
constants.F_OK,
|
|
2126
|
+
),
|
|
2127
|
+
).rejects.toThrow();
|
|
2128
|
+
});
|
|
2129
|
+
|
|
2130
|
+
it("skips existing files unless overwrite is true", async () => {
|
|
2131
|
+
const { runAgentImport } = await import("./agent.js");
|
|
2132
|
+
|
|
2133
|
+
const manifest = {
|
|
2134
|
+
name: "Overwrite Station",
|
|
2135
|
+
tools: [
|
|
2136
|
+
{
|
|
2137
|
+
name: "tool_target",
|
|
2138
|
+
description: "Target tool",
|
|
2139
|
+
url: "../../.agents/tools/main/tool_target.json",
|
|
2140
|
+
},
|
|
2141
|
+
],
|
|
2142
|
+
};
|
|
2143
|
+
|
|
2144
|
+
const toolV1 = JSON.stringify({
|
|
2145
|
+
name: "tool_target",
|
|
2146
|
+
description: "Version 1",
|
|
2147
|
+
input_schema: { type: "object", properties: {} },
|
|
2148
|
+
execution: { type: "javascript", code: "return 1;" },
|
|
2149
|
+
});
|
|
2150
|
+
|
|
2151
|
+
const toolV2 = JSON.stringify({
|
|
2152
|
+
name: "tool_target",
|
|
2153
|
+
description: "Version 2",
|
|
2154
|
+
input_schema: { type: "object", properties: {} },
|
|
2155
|
+
execution: { type: "javascript", code: "return 2;" },
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
let currentTool = toolV1;
|
|
2159
|
+
const mockFetch = jest.fn<any>().mockImplementation(async (url: any) => {
|
|
2160
|
+
const urlStr = String(url);
|
|
2161
|
+
if (urlStr.includes(".well-known")) {
|
|
2162
|
+
return {
|
|
2163
|
+
ok: true,
|
|
2164
|
+
status: 200,
|
|
2165
|
+
json: async () => manifest,
|
|
2166
|
+
text: async () => JSON.stringify(manifest),
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
return { ok: true, status: 200, text: async () => currentTool };
|
|
2170
|
+
});
|
|
2171
|
+
|
|
2172
|
+
// 1st import: imported
|
|
2173
|
+
const res1 = await runAgentImport("https://example.com", {
|
|
2174
|
+
workspace: tmpDir,
|
|
2175
|
+
fetchFn: mockFetch as any,
|
|
2176
|
+
quiet: true,
|
|
2177
|
+
});
|
|
2178
|
+
expect(res1.importedCount).toBe(1);
|
|
2179
|
+
|
|
2180
|
+
// 2nd import without overwrite: skipped
|
|
2181
|
+
currentTool = toolV2;
|
|
2182
|
+
const res2 = await runAgentImport("https://example.com", {
|
|
2183
|
+
workspace: tmpDir,
|
|
2184
|
+
fetchFn: mockFetch as any,
|
|
2185
|
+
quiet: true,
|
|
2186
|
+
});
|
|
2187
|
+
expect(res2.skippedCount).toBe(1);
|
|
2188
|
+
expect(res2.importedCount).toBe(0);
|
|
2189
|
+
const contentUnchanged = JSON.parse(
|
|
2190
|
+
await readFile(
|
|
2191
|
+
path.join(tmpDir, ".agents/tools/main/tool_target.json"),
|
|
2192
|
+
"utf8",
|
|
2193
|
+
),
|
|
2194
|
+
);
|
|
2195
|
+
expect(contentUnchanged.description).toBe("Version 1");
|
|
2196
|
+
|
|
2197
|
+
// 3rd import with overwrite: true: imported
|
|
2198
|
+
const res3 = await runAgentImport("https://example.com", {
|
|
2199
|
+
workspace: tmpDir,
|
|
2200
|
+
overwrite: true,
|
|
2201
|
+
fetchFn: mockFetch as any,
|
|
2202
|
+
quiet: true,
|
|
2203
|
+
});
|
|
2204
|
+
expect(res3.importedCount).toBe(1);
|
|
2205
|
+
const contentUpdated = JSON.parse(
|
|
2206
|
+
await readFile(
|
|
2207
|
+
path.join(tmpDir, ".agents/tools/main/tool_target.json"),
|
|
2208
|
+
"utf8",
|
|
2209
|
+
),
|
|
2210
|
+
);
|
|
2211
|
+
expect(contentUpdated.description).toBe("Version 2");
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
it("handles network failure gracefully", async () => {
|
|
2215
|
+
const { runAgentImport } = await import("./agent.js");
|
|
2216
|
+
|
|
2217
|
+
const mockFetch = jest
|
|
2218
|
+
.fn<any>()
|
|
2219
|
+
.mockRejectedValue(new Error("Network unreachable"));
|
|
2220
|
+
|
|
2221
|
+
const result = await runAgentImport("https://broken.example.com", {
|
|
2222
|
+
workspace: tmpDir,
|
|
2223
|
+
fetchFn: mockFetch as any,
|
|
2224
|
+
quiet: true,
|
|
2225
|
+
});
|
|
2226
|
+
|
|
2227
|
+
expect(result.success).toBe(false);
|
|
2228
|
+
expect(result.error).toMatch(/Network unreachable/i);
|
|
2229
|
+
});
|
|
2230
|
+
|
|
2231
|
+
it("dispatches via runAgentCommand('import', ...)", async () => {
|
|
2232
|
+
const { runAgentCommand } = await import("./agent.js");
|
|
2233
|
+
|
|
2234
|
+
const manifest = {
|
|
2235
|
+
name: "Dispatch Station",
|
|
2236
|
+
tools: [
|
|
2237
|
+
{
|
|
2238
|
+
name: "dispatch_tool",
|
|
2239
|
+
description: "Dispatch tool",
|
|
2240
|
+
url: "../../.agents/tools/main/dispatch_tool.json",
|
|
2241
|
+
},
|
|
2242
|
+
],
|
|
2243
|
+
};
|
|
2244
|
+
|
|
2245
|
+
const toolJson = JSON.stringify({
|
|
2246
|
+
name: "dispatch_tool",
|
|
2247
|
+
description: "Dispatch test",
|
|
2248
|
+
input_schema: { type: "object", properties: {} },
|
|
2249
|
+
execution: { type: "javascript", code: "return 'dispatched';" },
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2252
|
+
const mockFetch = jest.fn<any>().mockImplementation(async (url: any) => {
|
|
2253
|
+
const urlStr = String(url);
|
|
2254
|
+
if (urlStr.includes(".well-known")) {
|
|
2255
|
+
return {
|
|
2256
|
+
ok: true,
|
|
2257
|
+
status: 200,
|
|
2258
|
+
json: async () => manifest,
|
|
2259
|
+
text: async () => JSON.stringify(manifest),
|
|
2260
|
+
};
|
|
2261
|
+
}
|
|
2262
|
+
return { ok: true, status: 200, text: async () => toolJson };
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2265
|
+
const result = await runAgentCommand(
|
|
2266
|
+
"import",
|
|
2267
|
+
["https://example.com/dispatch"],
|
|
2268
|
+
{
|
|
2269
|
+
workspace: tmpDir,
|
|
2270
|
+
fetchFn: mockFetch as any,
|
|
2271
|
+
quiet: true,
|
|
2272
|
+
},
|
|
2273
|
+
);
|
|
2274
|
+
|
|
2275
|
+
expect(result.success).toBe(true);
|
|
2276
|
+
expect(result.importedCount).toBe(1);
|
|
2277
|
+
});
|
|
2278
|
+
});
|