shadow-claw 1.52.0 → 1.52.1

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.
Files changed (112) hide show
  1. package/.well-known/mcp/server-card.json +1 -1
  2. package/.well-known/mcp.json +1 -1
  3. package/README.md +9 -9
  4. package/dist/cli/build/build.js +1 -1
  5. package/dist/cli/{chunk-DYq-orHK.js → chunk-BgTv9w0K.js} +1 -1
  6. package/dist/cli/{chunk-C6jii2Fl.js → chunk-d5hIhj8t.js} +1 -1
  7. package/dist/cli/cli.js +1 -1
  8. package/dist/lib/components/index.js +1 -1
  9. package/dist/lib/components/shadow-claw-pages.js +1 -1
  10. package/dist/lib/index.js +1 -1
  11. package/dist/lib/{shadow-claw-pages-kA2qa-4B.js → shadow-claw-pages-CjHS50HN.js} +2 -2
  12. package/dist/lib/{shadow-claw-pages-kA2qa-4B.js.map → shadow-claw-pages-CjHS50HN.js.map} +1 -1
  13. package/dist/public/.well-known/mcp/server-card.json +1 -1
  14. package/dist/public/.well-known/mcp.json +1 -1
  15. package/dist/public/AGENTS.md +1 -1
  16. package/dist/public/README.md +9 -9
  17. package/dist/public/docs/README.md +1 -1
  18. package/dist/public/docs/architecture/orchestrator.md +2 -2
  19. package/dist/public/docs/architecture/overview.md +3 -3
  20. package/dist/public/docs/architecture/worker-protocol.md +29 -29
  21. package/dist/public/docs/example/article/index.html +1 -1
  22. package/dist/public/docs/example/article.html +1 -1
  23. package/dist/public/docs/publishing/index.html +1 -1
  24. package/dist/public/docs/publishing.html +1 -1
  25. package/dist/public/docs/skill-creator/index.html +1 -1
  26. package/dist/public/docs/skill-creator.html +1 -1
  27. package/dist/public/docs/subsystems/local-models.md +1 -1
  28. package/dist/public/docs/subsystems/providers.md +11 -4
  29. package/dist/public/e2e/README.md +2 -2
  30. package/dist/public/index.html +1 -1
  31. package/dist/public/index.js +1 -1
  32. package/dist/public/llms.txt +1 -1
  33. package/dist/public/main/index.html +1 -1
  34. package/dist/public/main/memory/index.html +1 -1
  35. package/dist/public/main/memory.html +1 -1
  36. package/dist/public/main.html +1 -1
  37. package/dist/public/service-worker.js +1 -1
  38. package/dist/public/{shadow-claw-Ba2ZFE9m.js → shadow-claw-T9oPpqxT.js} +1 -1
  39. package/dist/public/{shadow-claw-pages-Cy6T0G9y.js → shadow-claw-pages-IW_LNEL_.js} +1 -1
  40. package/package.json +1 -1
  41. package/src/cli/build/build.test.ts +95 -0
  42. package/src/cli/build/build.ts +5 -1
  43. package/src/cli/commands/agent.test.ts +134 -0
  44. package/src/cli/commands/backup.test.ts +85 -0
  45. package/src/cli/commands/clients.test.ts +32 -0
  46. package/src/cli/commands/mcp.test.ts +260 -1
  47. package/src/cli/commands/send-file.test.ts +73 -0
  48. package/src/cli/commands/send.test.ts +58 -0
  49. package/src/cli/commands/tasks.test.ts +78 -0
  50. package/src/cli/commands/webrtc-listen.test.ts +74 -0
  51. package/src/cli/tools/assert-version-bump.test.ts +30 -1
  52. package/src/cli/tools/file-search-replace.test.ts +33 -1
  53. package/src/cli/tools/patch-service-worker-trusted-types.test.ts +24 -1
  54. package/src/cli/tools/touch-nojekyll.test.ts +12 -0
  55. package/src/cli/tools/version.test.ts +109 -5
  56. package/src/cli/utils/agent-core.test.ts +40 -0
  57. package/src/cli/utils/control-client.test.ts +239 -2
  58. package/src/cli/utils/load-workspace-config.test.ts +63 -0
  59. package/src/cli/utils/local-models.test.ts +98 -0
  60. package/src/cli/utils/progress-bar.test.ts +41 -2
  61. package/src/cli/utils/resolve-cache-dir.test.ts +61 -0
  62. package/src/cli/utils/resolve-project-root.test.ts +17 -0
  63. package/src/cli/utils/stdin.test.ts +38 -0
  64. package/src/cli/utils/stdio.test.ts +51 -1
  65. package/src/cli/utils/suppress-warnings.test.ts +9 -0
  66. package/src/cli/utils/webrtc-control-client.test.ts +569 -0
  67. package/src/common/utils/frontmatter.test.ts +117 -0
  68. package/src/components/common/help/providers.test.ts +67 -0
  69. package/src/components/common/shadow-claw-provider-model-picker/shadow-claw-provider-model-picker.test.ts +121 -0
  70. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +131 -0
  71. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +130 -0
  72. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +12 -0
  73. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +328 -0
  74. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +530 -8
  75. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +459 -0
  76. package/src/components/settings/shadow-claw-networking/shadow-claw-networking.test.ts +88 -0
  77. package/src/components/settings/shadow-claw-notifications/shadow-claw-notifications.test.ts +92 -0
  78. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +127 -0
  79. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +173 -0
  80. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +62 -0
  81. package/src/components/settings/shadow-claw-webvm/shadow-claw-webvm.test.ts +104 -0
  82. package/src/components/shadow-claw/shadow-claw.test.ts +112 -0
  83. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +109 -0
  84. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +361 -0
  85. package/src/components/shadow-claw-dialog/shadow-claw-dialog.test.ts +76 -0
  86. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +307 -0
  87. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +398 -1
  88. package/src/components/shadow-claw-pages/shadow-claw-pages.test.ts +85 -0
  89. package/src/components/shadow-claw-pages/shadow-claw-pages.ts +2 -1
  90. package/src/components/shadow-claw-pdf-viewer/shadow-claw-pdf-viewer.test.ts +229 -0
  91. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +233 -0
  92. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +223 -1
  93. package/src/components/shadow-claw-toast/shadow-claw-toast.test.ts +79 -0
  94. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +229 -1
  95. package/src/server/logger.test.ts +57 -0
  96. package/src/server/peer.test.ts +47 -0
  97. package/src/server/routes/gemini.test.ts +244 -0
  98. package/src/server/server-peer.test.ts +245 -11
  99. package/src/server/services/llamafile-manager.test.ts +668 -0
  100. package/src/share-target/pending-shares.test.ts +90 -0
  101. package/src/shell/fs.test.ts +125 -0
  102. package/src/shell/shell.test.ts +8 -0
  103. package/src/subsystems/git/credentials.test.ts +260 -0
  104. package/src/subsystems/git/git.test.ts +64 -0
  105. package/src/subsystems/providers/executeNativeAiTask.test.ts +78 -0
  106. package/src/subsystems/providers/utils/parseLocalModelToolCall.test.ts +57 -0
  107. package/src/worker/tools/builtin-ai/builtin-ai.test.ts +88 -0
  108. package/src/worker/tools/email/email.test.ts +358 -0
  109. package/src/worker/tools/email/utils/resolveIntegrationEmailAuth.test.ts +120 -0
  110. package/src/worker/tools/email/utils/resolveUniqueAttachmentPath.test.ts +80 -0
  111. package/src/worker/tools/fetch-file/fetch-file.test.ts +111 -0
  112. package/src/worker/tools/fetch-url/fetch-url.test.ts +295 -0
@@ -2370,5 +2370,312 @@ describe("shadow-claw-file-viewer", () => {
2370
2370
 
2371
2371
  document.body.removeChild(component);
2372
2372
  });
2373
+
2374
+ it("handles handleAnchorNavigation with line numbers and elements", async () => {
2375
+ const component = new ShadowClawFileViewer();
2376
+ document.body.appendChild(component);
2377
+ await component.render();
2378
+
2379
+ const content = component.shadowRoot!.querySelector(
2380
+ ".file-content",
2381
+ ) as HTMLElement;
2382
+ content.innerHTML = `
2383
+ <div class="code-line" data-line="1">Line 1</div>
2384
+ <div class="code-line" data-line="2">Line 2</div>
2385
+ <div class="code-line" data-line="3">Line 3</div>
2386
+ <h2 id="heading-test">Heading</h2>
2387
+ `;
2388
+
2389
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
2390
+
2391
+ const matchedLine = component.handleAnchorNavigation("#L1-L2");
2392
+ expect(matchedLine).toBe(true);
2393
+ expect(window.HTMLElement.prototype.scrollIntoView).toHaveBeenCalled();
2394
+
2395
+ const matchedHeading = component.handleAnchorNavigation("#heading-test");
2396
+ expect(matchedHeading).toBe(true);
2397
+
2398
+ const nonExistent = component.handleAnchorNavigation("#not-found");
2399
+ expect(nonExistent).toBe(false);
2400
+
2401
+ document.body.removeChild(component);
2402
+ });
2403
+
2404
+ it("handles storage proxy requests from iframe", async () => {
2405
+ const component = new ShadowClawFileViewer();
2406
+ document.body.appendChild(component);
2407
+ await component.render();
2408
+
2409
+ (fileViewerStore as any).file = {
2410
+ name: "test.html",
2411
+ path: "test.html",
2412
+ };
2413
+
2414
+ const mockSource = {
2415
+ postMessage: jest.fn(),
2416
+ } as any;
2417
+
2418
+ await (component as any).handleStorageProxyRequest(
2419
+ mockSource,
2420
+ "req-1",
2421
+ "removeItem",
2422
+ { key: "item-1" },
2423
+ );
2424
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2425
+ expect.objectContaining({
2426
+ type: "shadow-claw-storage-proxy-result",
2427
+ requestId: "req-1",
2428
+ result: null,
2429
+ }),
2430
+ "*",
2431
+ );
2432
+
2433
+ await (component as any).handleStorageProxyRequest(
2434
+ mockSource,
2435
+ "req-2",
2436
+ "clear",
2437
+ {},
2438
+ );
2439
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2440
+ expect.objectContaining({
2441
+ type: "shadow-claw-storage-proxy-result",
2442
+ requestId: "req-2",
2443
+ }),
2444
+ "*",
2445
+ );
2446
+
2447
+ await (component as any).handleStorageProxyRequest(
2448
+ mockSource,
2449
+ "req-3",
2450
+ "getAllItems",
2451
+ {},
2452
+ );
2453
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2454
+ expect.objectContaining({
2455
+ type: "shadow-claw-storage-proxy-result",
2456
+ requestId: "req-3",
2457
+ }),
2458
+ "*",
2459
+ );
2460
+
2461
+ await (component as any).handleStorageProxyRequest(
2462
+ mockSource,
2463
+ "req-4",
2464
+ "idb-put",
2465
+ { dbName: "mydb", storeName: "mystore", key: "k1", value: "v1" },
2466
+ );
2467
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2468
+ expect.objectContaining({
2469
+ type: "shadow-claw-storage-proxy-result",
2470
+ requestId: "req-4",
2471
+ result: "k1",
2472
+ }),
2473
+ "*",
2474
+ );
2475
+
2476
+ await (component as any).handleStorageProxyRequest(
2477
+ mockSource,
2478
+ "req-5",
2479
+ "idb-get",
2480
+ { dbName: "mydb", storeName: "mystore", key: "k1" },
2481
+ );
2482
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2483
+ expect.objectContaining({
2484
+ type: "shadow-claw-storage-proxy-result",
2485
+ requestId: "req-5",
2486
+ }),
2487
+ "*",
2488
+ );
2489
+
2490
+ await (component as any).handleStorageProxyRequest(
2491
+ mockSource,
2492
+ "req-6",
2493
+ "idb-getAll",
2494
+ { dbName: "mydb", storeName: "mystore" },
2495
+ );
2496
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2497
+ expect.objectContaining({
2498
+ type: "shadow-claw-storage-proxy-result",
2499
+ requestId: "req-6",
2500
+ }),
2501
+ "*",
2502
+ );
2503
+
2504
+ await (component as any).handleStorageProxyRequest(
2505
+ mockSource,
2506
+ "req-7",
2507
+ "idb-delete",
2508
+ { dbName: "mydb", storeName: "mystore", key: "k1" },
2509
+ );
2510
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2511
+ expect.objectContaining({
2512
+ type: "shadow-claw-storage-proxy-result",
2513
+ requestId: "req-7",
2514
+ }),
2515
+ "*",
2516
+ );
2517
+
2518
+ await (component as any).handleStorageProxyRequest(
2519
+ mockSource,
2520
+ "req-8",
2521
+ "idb-clear",
2522
+ { dbName: "mydb", storeName: "mystore" },
2523
+ );
2524
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2525
+ expect.objectContaining({
2526
+ type: "shadow-claw-storage-proxy-result",
2527
+ requestId: "req-8",
2528
+ }),
2529
+ "*",
2530
+ );
2531
+
2532
+ await (component as any).handleStorageProxyRequest(
2533
+ mockSource,
2534
+ "req-9",
2535
+ "idb-deleteDatabase",
2536
+ { dbName: "mydb" },
2537
+ );
2538
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2539
+ expect.objectContaining({
2540
+ type: "shadow-claw-storage-proxy-result",
2541
+ requestId: "req-9",
2542
+ }),
2543
+ "*",
2544
+ );
2545
+
2546
+ await (component as any).handleStorageProxyRequest(
2547
+ mockSource,
2548
+ "req-10",
2549
+ "unknown-op",
2550
+ {},
2551
+ );
2552
+ expect(mockSource.postMessage).toHaveBeenCalledWith(
2553
+ expect.objectContaining({
2554
+ type: "shadow-claw-storage-proxy-result",
2555
+ requestId: "req-10",
2556
+ error: expect.stringContaining("Unknown storage proxy method"),
2557
+ }),
2558
+ "*",
2559
+ );
2560
+
2561
+ document.body.removeChild(component);
2562
+ });
2563
+
2564
+ it("handles link click navigation in content", async () => {
2565
+ const component = new ShadowClawFileViewer();
2566
+ document.body.appendChild(component);
2567
+ await component.render();
2568
+ component.db = {} as any;
2569
+
2570
+ const openSpy = jest.spyOn(window, "open").mockImplementation(() => null);
2571
+
2572
+ const anchorEl = document.createElement("a");
2573
+ anchorEl.setAttribute("href", "https://example.com/external");
2574
+ anchorEl.setAttribute("target", "_blank");
2575
+
2576
+ const fakeEvent = {
2577
+ defaultPrevented: false,
2578
+ button: 0,
2579
+ metaKey: false,
2580
+ ctrlKey: false,
2581
+ shiftKey: false,
2582
+ altKey: false,
2583
+ target: anchorEl,
2584
+ preventDefault: jest.fn(),
2585
+ } as any;
2586
+
2587
+ await component.handlePreviewLinkClick(fakeEvent);
2588
+ expect(fakeEvent.preventDefault).toHaveBeenCalled();
2589
+ expect(openSpy).toHaveBeenCalledWith(
2590
+ "https://example.com/external",
2591
+ "_blank",
2592
+ "noopener,noreferrer",
2593
+ );
2594
+
2595
+ openSpy.mockRestore();
2596
+ document.body.removeChild(component);
2597
+ });
2598
+
2599
+ it("handles isFileEditMode rendering in updateView", async () => {
2600
+ const component = new ShadowClawFileViewer();
2601
+ document.body.appendChild(component);
2602
+ await component.render();
2603
+
2604
+ (fileViewerStore as any).file = {
2605
+ name: "index.ts",
2606
+ path: "src/index.ts",
2607
+ content: "const a = 10;",
2608
+ mimeType: "text/typescript",
2609
+ kind: "text",
2610
+ };
2611
+
2612
+ component.isFileEditMode = true;
2613
+ component.isFilePreviewMode = false;
2614
+ component.isEditorDirty = false;
2615
+ await component.updateView();
2616
+
2617
+ const root = component.shadowRoot!;
2618
+ const editor = root.querySelector(".file-editor") as HTMLTextAreaElement;
2619
+ expect(editor).toBeDefined();
2620
+ expect(editor.value).toBe("const a = 10;");
2621
+ expect(editor.getAttribute("language")).toBe("typescript");
2622
+
2623
+ // Dirty mode
2624
+ component.isEditorDirty = true;
2625
+ component.editorDraftContent = "const a = 20;";
2626
+ await component.updateView();
2627
+ expect(editor.value).toBe("const a = 20;");
2628
+
2629
+ document.body.removeChild(component);
2630
+ });
2631
+
2632
+ it("handles reloadPreviewWithSearchParams", async () => {
2633
+ const component = new ShadowClawFileViewer();
2634
+ document.body.appendChild(component);
2635
+ await component.render();
2636
+
2637
+ (fileViewerStore as any).file = {
2638
+ name: "page.html",
2639
+ path: "page.html",
2640
+ content: "<h1>Test</h1>",
2641
+ mimeType: "text/html",
2642
+ kind: "html",
2643
+ };
2644
+
2645
+ const root = component.shadowRoot!;
2646
+ const iframe = document.createElement("iframe");
2647
+ iframe.className = "file-viewer__iframe";
2648
+ root.appendChild(iframe);
2649
+
2650
+ await (component as any).reloadPreviewWithSearchParams("?color=blue");
2651
+ expect(iframe.srcdoc).toBeDefined();
2652
+
2653
+ document.body.removeChild(component);
2654
+ });
2655
+
2656
+ it("handles reactive modal open and close via effect callback", async () => {
2657
+ const { effect } = await import("../../core/effect.js");
2658
+ const component = new ShadowClawFileViewer();
2659
+ document.body.appendChild(component);
2660
+ await component.render();
2661
+
2662
+ const calls = (effect as any).mock.calls;
2663
+ const effectCallback = calls[calls.length - 1][0];
2664
+ expect(typeof effectCallback).toBe("function");
2665
+
2666
+ (fileViewerStore as any).file = {
2667
+ name: "note.txt",
2668
+ path: "note.txt",
2669
+ content: "hello world",
2670
+ };
2671
+ effectCallback();
2672
+ expect(component.lastOpenedFileName).toBe("note.txt");
2673
+
2674
+ (fileViewerStore as any).file = null;
2675
+ effectCallback();
2676
+ expect(component.lastOpenedFileName).toBe("");
2677
+
2678
+ document.body.removeChild(component);
2679
+ });
2373
2680
  });
2374
2681
  });