@openparachute/agent 0.2.0 → 0.2.3-rc.10

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 (70) hide show
  1. package/.parachute/module.json +3 -3
  2. package/package.json +8 -1
  3. package/src/agent-defs.ts +9 -0
  4. package/src/auth.ts +182 -14
  5. package/src/backends/registry.ts +65 -27
  6. package/src/daemon.ts +311 -12
  7. package/src/def-vault-triggers.ts +317 -0
  8. package/src/preflight.ts +139 -0
  9. package/src/spawn-agent.ts +16 -0
  10. package/src/step-up.ts +316 -0
  11. package/src/terminal-ui.ts +73 -0
  12. package/src/transports/http-ui.ts +10 -8
  13. package/src/transports/vault.ts +40 -22
  14. package/src/ui-kit.ts +6 -3
  15. package/src/ui-ticket.ts +121 -0
  16. package/web/ui/dist/assets/index-Dhr5Kl_d.css +1 -0
  17. package/web/ui/dist/assets/index-Di5MmFZR.js +60 -0
  18. package/web/ui/dist/index.html +15 -0
  19. package/src/_parked/interactive-spawn.test.ts +0 -324
  20. package/src/_parked/interactive-spawn.ts +0 -701
  21. package/src/agent-defs.test.ts +0 -1504
  22. package/src/agent-mcp-config.test.ts +0 -115
  23. package/src/agents.test.ts +0 -360
  24. package/src/auth.test.ts +0 -46
  25. package/src/backends/attached-queue.test.ts +0 -376
  26. package/src/backends/programmatic.test.ts +0 -1715
  27. package/src/backends/registry.test.ts +0 -1494
  28. package/src/backends/stream-json.test.ts +0 -570
  29. package/src/channel-backend-wiring.test.ts +0 -237
  30. package/src/credentials.test.ts +0 -274
  31. package/src/cron.test.ts +0 -342
  32. package/src/daemon-agent-def-api.test.ts +0 -166
  33. package/src/daemon-agent-defs-api.test.ts +0 -953
  34. package/src/daemon-agent-env-api.test.ts +0 -338
  35. package/src/daemon-attached-queue-store.test.ts +0 -65
  36. package/src/daemon-config-api.test.ts +0 -962
  37. package/src/daemon-jobs-api.test.ts +0 -271
  38. package/src/daemon-vault-chat.test.ts +0 -250
  39. package/src/daemon.test.ts +0 -746
  40. package/src/def-vaults.test.ts +0 -136
  41. package/src/delivery-state.test.ts +0 -110
  42. package/src/effective-env.test.ts +0 -114
  43. package/src/grants.test.ts +0 -638
  44. package/src/hub-jwt.test.ts +0 -161
  45. package/src/jobs.test.ts +0 -245
  46. package/src/mcp-http.test.ts +0 -265
  47. package/src/mint-token.test.ts +0 -152
  48. package/src/module-manifest.test.ts +0 -158
  49. package/src/programmatic-wiring.test.ts +0 -838
  50. package/src/registry.test.ts +0 -227
  51. package/src/resolve-port.test.ts +0 -64
  52. package/src/routing.test.ts +0 -184
  53. package/src/runner.test.ts +0 -506
  54. package/src/sandbox/config.test.ts +0 -150
  55. package/src/sandbox/egress.test.ts +0 -113
  56. package/src/sandbox/live-seatbelt.test.ts +0 -277
  57. package/src/sandbox/mounts.test.ts +0 -154
  58. package/src/sandbox/sandbox.test.ts +0 -168
  59. package/src/services-manifest.test.ts +0 -106
  60. package/src/spa-serve.test.ts +0 -116
  61. package/src/spawn-agent-cli.test.ts +0 -172
  62. package/src/spawn-agent.test.ts +0 -1218
  63. package/src/spawn-deps.test.ts +0 -54
  64. package/src/terminal-assets.test.ts +0 -50
  65. package/src/terminal.test.ts +0 -530
  66. package/src/transports/http-ui.test.ts +0 -455
  67. package/src/transports/telegram.test.ts +0 -174
  68. package/src/transports/vault.test.ts +0 -2011
  69. package/src/ui-kit.test.ts +0 -178
  70. package/web/ui/tsconfig.json +0 -21
@@ -1,178 +0,0 @@
1
- /**
2
- * Unit tests for the shared UI kit (src/ui-kit.ts) — the foundation every
3
- * channel page adopts. Guards the shell contract (active-tab marking, controls
4
- * slot, nav set) + the token/CSS invariants the pages depend on.
5
- */
6
- import { describe, test, expect } from "bun:test";
7
- import { THEME_CSS, SHELL_JS, appShell, NAV_VIEWS, BRAND } from "./ui-kit.ts";
8
-
9
- describe("appShell", () => {
10
- test("renders the brand + all nav tabs, marking only the active one", () => {
11
- const h = appShell({ active: "agents" });
12
- expect(h).toContain("app-header");
13
- expect(h).toContain('class="brand-mark"');
14
- for (const v of NAV_VIEWS) expect(h).toContain(`data-view="${v.view}"`);
15
- // active tab gets class="active"; others don't.
16
- expect(h).toContain('data-view="agents" href="#" class="active"');
17
- expect(h).toContain('data-view="chat" href="#"');
18
- expect(h).not.toContain('data-view="chat" href="#" class="active"');
19
- });
20
-
21
- test("marks Home active when it is the current view", () => {
22
- const h = appShell({ active: "home" });
23
- expect(h).toContain('data-view="home" href="#" class="active"');
24
- expect(h).toContain('data-view="chat" href="#"');
25
- expect(h).not.toContain('data-view="chat" href="#" class="active"');
26
- });
27
-
28
- test("status defaults, and a custom status + tag suffix render", () => {
29
- expect(appShell({ active: "chat" })).toContain('id="status"');
30
- const h = appShell({ active: "chat", status: "● ready", tag: "chat" });
31
- expect(h).toContain("● ready");
32
- expect(h).toContain("· chat");
33
- });
34
-
35
- test("controls slot is injected before the status when provided, omitted otherwise", () => {
36
- const withCtl = appShell({ active: "terminal", controls: "<button id='reconnect'>Reconnect</button>" });
37
- expect(withCtl).toContain("app-controls");
38
- expect(withCtl).toContain("id='reconnect'");
39
- expect(appShell({ active: "terminal" })).not.toContain("app-controls");
40
- });
41
-
42
- test("nav covers exactly home/chat/agents/schedules/terminal/config, Home first", () => {
43
- expect(NAV_VIEWS.map((v) => v.view)).toEqual([
44
- "home",
45
- "chat",
46
- "agents",
47
- "schedules",
48
- "terminal",
49
- "config",
50
- ]);
51
- });
52
- });
53
-
54
- describe("THEME_CSS", () => {
55
- test("declares the brand tokens incl. the warm-light bg, accent, warn, and dark term pane", () => {
56
- expect(THEME_CSS).toContain(":root");
57
- expect(THEME_CSS).toContain(`--bg: ${BRAND.bg}`); // #faf8f4, the warm light brand
58
- expect(THEME_CSS).toContain(`--accent: ${BRAND.accent}`);
59
- expect(THEME_CSS).toContain(`--warn: ${BRAND.warn}`);
60
- expect(THEME_CSS).toContain(`--term-bg: ${BRAND.termBg}`); // #000 — the only intended black
61
- });
62
- test("ships the shared component layer (shell + buttons + banners + pills)", () => {
63
- for (const sel of [".app-nav", ".app-nav a.active", ".btn-primary", ".banner-warn", ".pill.warn"]) {
64
- expect(THEME_CSS).toContain(sel);
65
- }
66
- });
67
- test("carries NO leftover dark-console tokens (the pages unified on the brand)", () => {
68
- expect(THEME_CSS).not.toContain("#0f1115");
69
- expect(THEME_CSS).not.toContain("--panel");
70
- });
71
- });
72
-
73
- describe("SHELL_JS", () => {
74
- test("provides MOUNT derivation, nav wiring, token fetch, and helpers", () => {
75
- for (const sym of ["var MOUNT", "function wireShell", "function escapeHtml", "function setStatus", "function fetchToken", "function authedFetch", "function setTerminalNavVisible"]) {
76
- expect(SHELL_JS).toContain(sym);
77
- }
78
- // It hits the hub agent-token endpoint with the operator cookie.
79
- expect(SHELL_JS).toContain("/admin/agent-token");
80
- expect(SHELL_JS).toContain('credentials: "include"');
81
- });
82
-
83
- // Terminal-nav cleanup (Parachute Agent Phase 1): wireShell hides the standalone
84
- // Terminal nav entry by default (programmatic backend has no terminal); pages
85
- // reveal it via setTerminalNavVisible when an interactive agent exists. The
86
- // Terminal page itself (active === "terminal") shows it.
87
- test("wireShell gates the Terminal nav link via setTerminalNavVisible", () => {
88
- expect(SHELL_JS).toContain('a[data-view="terminal"]');
89
- // wireShell defaults the terminal entry to its own-page-only visibility.
90
- expect(SHELL_JS).toContain('setTerminalNavVisible(active === "terminal")');
91
- });
92
- test("is safe to interpolate — no naked backtick that could break a host literal", () => {
93
- expect(SHELL_JS.includes("`")).toBe(false);
94
- });
95
- test("exports a renderMarkdown helper (reused by the chat transcript)", () => {
96
- expect(SHELL_JS).toContain("function renderMarkdown");
97
- });
98
- });
99
-
100
- // renderMarkdown lives inside SHELL_JS (vanilla JS, no DOM). Evaluate SHELL_JS in
101
- // a fresh function scope and hand back its renderMarkdown so we can exercise it
102
- // directly — the same code the chat page runs in the browser.
103
- function loadRenderMarkdown(): (text: string) => string {
104
- // SHELL_JS defines `var MOUNT = window.location...` at the top; stub a minimal
105
- // window so that line doesn't throw when evaluated outside a browser.
106
- const factory = new Function(
107
- "window",
108
- SHELL_JS + "\nreturn renderMarkdown;",
109
- ) as (w: unknown) => (text: string) => string;
110
- return factory({ location: { pathname: "/ui" } });
111
- }
112
-
113
- describe("renderMarkdown (SHELL_JS, XSS-safe Markdown subset)", () => {
114
- const renderMarkdown = loadRenderMarkdown();
115
-
116
- test("escapes raw HTML first — a <script> tag never survives as markup", () => {
117
- const out = renderMarkdown("<script>alert(1)</script>");
118
- expect(out).not.toContain("<script>");
119
- expect(out).toContain("&lt;script&gt;");
120
- });
121
-
122
- test("renders bold and italic", () => {
123
- expect(renderMarkdown("**bold**")).toContain("<strong>bold</strong>");
124
- expect(renderMarkdown("an *italic* word")).toContain("<em>italic</em>");
125
- });
126
-
127
- test("renders inline code and fenced code blocks", () => {
128
- const bt = String.fromCharCode(96);
129
- expect(renderMarkdown(bt + "inline" + bt)).toContain("<code>inline</code>");
130
- const fenced = renderMarkdown(bt + bt + bt + "\nconst x = 1;\n" + bt + bt + bt);
131
- expect(fenced).toContain("<pre><code>");
132
- expect(fenced).toContain("const x = 1;");
133
- });
134
-
135
- test("does not apply inline rules inside code spans", () => {
136
- const bt = String.fromCharCode(96);
137
- const out = renderMarkdown(bt + "**not bold**" + bt);
138
- expect(out).toContain("<code>**not bold**</code>");
139
- expect(out).not.toContain("<strong>");
140
- });
141
-
142
- test("renders http/https links as anchors with the url preserved", () => {
143
- const out = renderMarkdown("[site](https://example.com/x)");
144
- expect(out).toContain('href="https://example.com/x"');
145
- expect(out).toContain(">site</a>");
146
- expect(out).toContain('rel="noopener noreferrer"');
147
- });
148
-
149
- test("rejects javascript: URLs — renders inert escaped text, no anchor", () => {
150
- const out = renderMarkdown("[click](javascript:alert(1))");
151
- // No anchor and no href is produced — the would-be URL never reaches markup.
152
- expect(out).not.toContain("<a ");
153
- expect(out).not.toContain("href=");
154
- // The markdown is left as inert escaped text (safe — not an executable link).
155
- expect(out).toContain("[click]");
156
- });
157
-
158
- test("rejects data: URLs too — only http/https survive as anchors", () => {
159
- const out = renderMarkdown("[x](data:text/html,<script>alert(1)</script>)");
160
- expect(out).not.toContain("<a ");
161
- expect(out).not.toContain("href=");
162
- // any escaped markup inside is inert text, never executable.
163
- expect(out).not.toContain("<script>");
164
- });
165
-
166
- test("escapes other canonical XSS vectors (img onerror, svg onload)", () => {
167
- const out1 = renderMarkdown('<img src=x onerror=alert(1)>');
168
- expect(out1).not.toContain("<img");
169
- expect(out1).toContain("&lt;img");
170
- const out2 = renderMarkdown('<svg onload=alert(1)>');
171
- expect(out2).not.toContain("<svg");
172
- expect(out2).toContain("&lt;svg");
173
- });
174
-
175
- test("converts newlines to <br>", () => {
176
- expect(renderMarkdown("line1\nline2")).toContain("line1<br>line2");
177
- });
178
- });
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "useDefineForClassFields": true,
5
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
- "module": "ESNext",
7
- "moduleResolution": "Bundler",
8
- "skipLibCheck": true,
9
- "allowImportingTsExtensions": true,
10
- "resolveJsonModule": true,
11
- "isolatedModules": true,
12
- "noEmit": true,
13
- "jsx": "react-jsx",
14
- "strict": true,
15
- "noUnusedLocals": true,
16
- "noUnusedParameters": true,
17
- "noFallthroughCasesInSwitch": true,
18
- "types": ["vite/client"]
19
- },
20
- "include": ["src/**/*.ts", "src/**/*.tsx"]
21
- }