create-janux 0.1.0 → 0.2.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.
package/bin.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  import { cpSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
3
- import { join } from 'node:path';
3
+ import { basename, join } from 'node:path';
4
+
5
+ // Install artifacts from running the template in place (local dev of the template itself).
6
+ const SKIP = new Set(['node_modules', 'bun.lock']);
4
7
 
5
8
  const name = process.argv[2];
6
9
 
@@ -14,7 +17,10 @@ if (existsSync(target)) {
14
17
  console.error(`create-janux: "${name}" already exists`);
15
18
  process.exit(1);
16
19
  }
17
- cpSync(join(import.meta.dirname, 'template'), target, { recursive: true });
20
+ cpSync(join(import.meta.dirname, 'template'), target, {
21
+ recursive: true,
22
+ filter: (source) => !SKIP.has(basename(source)),
23
+ });
18
24
 
19
25
  const pkgPath = join(target, 'package.json');
20
26
 
@@ -25,4 +31,4 @@ console.log(`✔ ${name} created
25
31
  bun install
26
32
  bun run dev
27
33
 
28
- Configure the copilot with JANUX_MODEL or a provider API key (optional).`);
34
+ The right panel is the agent surface same thing as: curl localhost:3000/_janux/manifest`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-janux",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Scaffold a new Janux app: bunx create-janux my-app",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,5 +36,6 @@
36
36
  ],
37
37
  "engines": {
38
38
  "bun": ">=1.3.0"
39
- }
39
+ },
40
+ "dependencies": {}
40
41
  }
@@ -0,0 +1,29 @@
1
+ # __APP_NAME__
2
+
3
+ A [Janux](https://github.com/aralroca/Janux) app. One component, two faces: the counter is a UI for you **and** a set of typed tools for agents — the right panel shows that second face live, exactly like the [playground](https://github.com/aralroca/Janux/tree/main/apps/docs).
4
+
5
+ ## Run it
6
+
7
+ ```bash
8
+ bun install
9
+ bun run dev # http://localhost:3000
10
+ bun test # the example unit test — no browser needed
11
+ ```
12
+
13
+ ## What's inside
14
+
15
+ | File | Shows |
16
+ |---|---|
17
+ | `src/components/Counter.tsx` | State, typed intents, and a `confirm` guard |
18
+ | `src/components/AgentPanel.tsx` | The agent bridge: manifest, resource reads, calls, proposals with human approve |
19
+ | `src/components/Counter.test.ts` | Testing intents & guards without a browser |
20
+
21
+ Press **Call as agent** on `counter.reset` — the `confirm` guard turns the call into a proposal: nothing runs until you approve it on screen.
22
+
23
+ ## See the second face over HTTP
24
+
25
+ ```bash
26
+ curl -s localhost:3000/_janux/manifest | jq # what agents see
27
+ ```
28
+
29
+ Docs: start with the [tutorial](https://github.com/aralroca/Janux/tree/main/apps/docs/content/tutorial). Want a chat copilot in your app? See the [shop example](https://github.com/aralroca/Janux/tree/main/examples/shop).
@@ -0,0 +1,208 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "__APP_NAME__",
7
+ "dependencies": {
8
+ "@janux/cli": "^0.2.0",
9
+ "@janux/server": "^0.2.0",
10
+ "janux": "^0.2.0",
11
+ },
12
+ "devDependencies": {
13
+ "@types/bun": "^1",
14
+ },
15
+ },
16
+ },
17
+ "overrides": {
18
+ "@janux/agent": "^0.2.0",
19
+ "@janux/server": "^0.2.0",
20
+ "@janux/vite": "^0.2.0",
21
+ "janux": "^0.2.0",
22
+ },
23
+ "packages": {
24
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="],
25
+
26
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="],
27
+
28
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="],
29
+
30
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="],
31
+
32
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="],
33
+
34
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="],
35
+
36
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="],
37
+
38
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="],
39
+
40
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="],
41
+
42
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="],
43
+
44
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="],
45
+
46
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="],
47
+
48
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="],
49
+
50
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="],
51
+
52
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="],
53
+
54
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="],
55
+
56
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="],
57
+
58
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="],
59
+
60
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="],
61
+
62
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="],
63
+
64
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="],
65
+
66
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="],
67
+
68
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="],
69
+
70
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="],
71
+
72
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="],
73
+
74
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="],
75
+
76
+ "@janux/agent": ["@janux/agent@0.2.0", "", { "dependencies": { "@janux/server": "0.1.0", "janux": "0.1.0" } }, "sha512-AyrXThGv/9rgyBBMd4YzEZD1qSCtdPrSeuidKtnN2B3aTysV0jBa6C+1lGYjDcnM6u7h9xGJy9iO9HMOyiHfgA=="],
77
+
78
+ "@janux/cli": ["@janux/cli@0.2.0", "", { "dependencies": { "@janux/agent": "0.1.0", "@janux/server": "0.1.0", "@janux/vite": "0.1.0", "janux": "0.1.0", "vite": "^7.3.0" }, "bin": { "janux": "bin.ts" } }, "sha512-HzRyFaiGAalzA4epwn2NgpVWUh8ZMX+Prp4wWRnNTtPhIGqL5pnX52E7+bbrsdiCWXWU5ytgFgpGGpir3EIMMA=="],
79
+
80
+ "@janux/server": ["@janux/server@0.2.0", "", { "dependencies": { "janux": "0.1.0", "web-bot-auth": "^0.1.3" } }, "sha512-bx4iY9TVRLB2CVOTqQl3qPz4SQ++HNQICbK3bPToa6JU/Hz88vnTEsBi4o2y31FJ1Z/hIOchLn6jYV2JnTwCJw=="],
81
+
82
+ "@janux/vite": ["@janux/vite@0.2.0", "", { "dependencies": { "@janux/agent": "0.1.0", "@janux/server": "0.1.0", "@swc/core": "^1.9.0", "janux": "0.1.0" }, "peerDependencies": { "vite": ">=5" } }, "sha512-Lnnj2McXcEUVAPlhabMAPVD0H2KJatLv8TkOxv5wedpnR684jXBG7+N4LT+SKArlHouI/n+PLTGtXo3eINTukQ=="],
83
+
84
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="],
85
+
86
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw=="],
87
+
88
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.62.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A=="],
89
+
90
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.62.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA=="],
91
+
92
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.62.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw=="],
93
+
94
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.62.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg=="],
95
+
96
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg=="],
97
+
98
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.62.2", "", { "os": "linux", "cpu": "arm" }, "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA=="],
99
+
100
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA=="],
101
+
102
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.62.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ=="],
103
+
104
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg=="],
105
+
106
+ "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ=="],
107
+
108
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A=="],
109
+
110
+ "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.62.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w=="],
111
+
112
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg=="],
113
+
114
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.62.2", "", { "os": "linux", "cpu": "none" }, "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q=="],
115
+
116
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.62.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg=="],
117
+
118
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A=="],
119
+
120
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.62.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg=="],
121
+
122
+ "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.62.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg=="],
123
+
124
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.62.2", "", { "os": "none", "cpu": "arm64" }, "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA=="],
125
+
126
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.62.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg=="],
127
+
128
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.62.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q=="],
129
+
130
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg=="],
131
+
132
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.62.2", "", { "os": "win32", "cpu": "x64" }, "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA=="],
133
+
134
+ "@swc/core": ["@swc/core@1.15.46", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.27" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.15.46", "@swc/core-darwin-x64": "1.15.46", "@swc/core-linux-arm-gnueabihf": "1.15.46", "@swc/core-linux-arm64-gnu": "1.15.46", "@swc/core-linux-arm64-musl": "1.15.46", "@swc/core-linux-ppc64-gnu": "1.15.46", "@swc/core-linux-s390x-gnu": "1.15.46", "@swc/core-linux-x64-gnu": "1.15.46", "@swc/core-linux-x64-musl": "1.15.46", "@swc/core-win32-arm64-msvc": "1.15.46", "@swc/core-win32-ia32-msvc": "1.15.46", "@swc/core-win32-x64-msvc": "1.15.46" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-Ri3em2mBpq3h2zSPliCYl63otDGqek8PPEfv2nWgRQEbZ/VBCNyypVTVQ6cEbTCXBhy+WE2T3fQb08moIyuYaw=="],
135
+
136
+ "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.15.46", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IsISIT22EfktVJrlvIpnAxG2u/A9aob9l99HMlx80x72WlFmFPk1V3UhkEzx86eJP8hw049KTFv/RISho2cq2Q=="],
137
+
138
+ "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.15.46", "", { "os": "darwin", "cpu": "x64" }, "sha512-4Tj4ppVIPCmUMpmGFiGtyEriwLyJ+yi/US4WfBrP/ok8COGddDZXLEzQETnKyK46mjvr1v0jevrS23zjoff7vA=="],
139
+
140
+ "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.15.46", "", { "os": "linux", "cpu": "arm" }, "sha512-i8tUGnNjyOgMmfmgFSg4aeJLQoFyfpIHK5FjpQAwpRyQIqEUB2w1e8zIDQzY1WhOxx8NoS1S5iUL813Un4Sf5A=="],
141
+
142
+ "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.15.46", "", { "os": "linux", "cpu": "arm64" }, "sha512-c0OnhqzdhfOvv6qhNCcByepB+sNYOGZyhtr2Qa6ZCHvAWTYhSRw4j/u92Stue9PbZ/6q74b9nHzi76+kVzqQHQ=="],
143
+
144
+ "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.15.46", "", { "os": "linux", "cpu": "arm64" }, "sha512-imyRpNEcUzFQFV2LE4jL68ErvmKEuZCbvZru77iQREunJ+bR4i658cupTgtG1mLYM3F1Tzy3Sb9xYb02KghWTg=="],
145
+
146
+ "@swc/core-linux-ppc64-gnu": ["@swc/core-linux-ppc64-gnu@1.15.46", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ctEfcl/HcUeomK33cbySiHZm98GEDIxTm1EkpBsYCiHxElYBzvTXVeuQT2YwbUXn9XCrjiw4ipyUNk33k26qRg=="],
147
+
148
+ "@swc/core-linux-s390x-gnu": ["@swc/core-linux-s390x-gnu@1.15.46", "", { "os": "linux", "cpu": "s390x" }, "sha512-DxlMdnt84TtRVTv7WL/thWyz9+QU8QZNNoAP9rrk0P68LziuhfePp8MjQ44zIprpTHTsEwyziIuGUUN5iSC1bQ=="],
149
+
150
+ "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.15.46", "", { "os": "linux", "cpu": "x64" }, "sha512-SKxI7J6t90XPl8hRUqtJi9NfGdunN/E/vZMc7Bc0figeRdOPDBT+Tm8g7cx9xM0T0mewh2l+8dewa3Am27/P+A=="],
151
+
152
+ "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.15.46", "", { "os": "linux", "cpu": "x64" }, "sha512-qj9T6B7bosI0VEsrWOVXZN1OXxS8Tp63ywyrLxNdOycnUtLdkgYcoBsN5y8ImnDDsnwrEWZOy1e+J4xSe7mA3Q=="],
153
+
154
+ "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.15.46", "", { "os": "win32", "cpu": "arm64" }, "sha512-8p7l4c3LU+eA5g9Et1JPhNeMC1oQwXTGU+uah8DPIBX7YXzqswvaBtyKVmXefVGi/DJU1x3YJsc3mbAp9aWzSQ=="],
155
+
156
+ "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.15.46", "", { "os": "win32", "cpu": "ia32" }, "sha512-tUEnfr3Bn9u6FOjUb3PN9p+09qZC2j+wNDLKHzXXZn22rqGcUqR/ohCRSS+nG9B9+X+U+3FewNEHJkTmdIvMjQ=="],
157
+
158
+ "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.15.46", "", { "os": "win32", "cpu": "x64" }, "sha512-Vux7UDzBJYQggSuPfcl2w9iu+IJpgpRCxHzgCaVkELnAXAE4XZMOTX9HNcaNiwfeIDqdu2rkr69RuDm6wY8neA=="],
159
+
160
+ "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
161
+
162
+ "@swc/types": ["@swc/types@0.1.27", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg=="],
163
+
164
+ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
165
+
166
+ "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
167
+
168
+ "@types/node": ["@types/node@26.1.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw=="],
169
+
170
+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
171
+
172
+ "diff-dom-streaming": ["diff-dom-streaming@0.6.6", "", {}, "sha512-K217pR/h/UEQv3J0PXu0sshkW49PbyM/Il9pqXaoLLqHU/KaApdujPr0LfzobqqjEdfmSA+EYmHWHsnE9c7+NQ=="],
173
+
174
+ "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="],
175
+
176
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
177
+
178
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
179
+
180
+ "http-message-sig": ["http-message-sig@0.2.0", "", { "dependencies": { "structured-headers": "2.0.2" } }, "sha512-9fXSmxsdWDuMgk6Rd5Jc8Fa/wEblmRXZyO89KPkRrGsdeAwYlUkvS5oSOw6aOKKK/qKHNTq3jCHHTRCEid6RaQ=="],
181
+
182
+ "janux": ["janux@0.2.0", "", { "dependencies": { "diff-dom-streaming": "^0.6.6" } }, "sha512-749FV+pJuVz6aPAR8+rW6sePGOMiUAhirMC2f6JPEZtp+bbxXXCIsrYspHSrg9jrbHLxyW4SLMTMARpMo9Pe7A=="],
183
+
184
+ "jsonwebkey-thumbprint": ["jsonwebkey-thumbprint@0.1.0", "", {}, "sha512-4m/gJEUQH8N2NfvZFjCL8/E+vlt0FlrmHoR4T93CEXQRkC64qwRi6oQri4/eqLVXqEqXYCss0+Q7pLx6vJBqzw=="],
185
+
186
+ "nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="],
187
+
188
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
189
+
190
+ "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
191
+
192
+ "postcss": ["postcss@8.5.22", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ=="],
193
+
194
+ "rollup": ["rollup@4.62.2", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.2", "@rollup/rollup-android-arm64": "4.62.2", "@rollup/rollup-darwin-arm64": "4.62.2", "@rollup/rollup-darwin-x64": "4.62.2", "@rollup/rollup-freebsd-arm64": "4.62.2", "@rollup/rollup-freebsd-x64": "4.62.2", "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", "@rollup/rollup-linux-arm-musleabihf": "4.62.2", "@rollup/rollup-linux-arm64-gnu": "4.62.2", "@rollup/rollup-linux-arm64-musl": "4.62.2", "@rollup/rollup-linux-loong64-gnu": "4.62.2", "@rollup/rollup-linux-loong64-musl": "4.62.2", "@rollup/rollup-linux-ppc64-gnu": "4.62.2", "@rollup/rollup-linux-ppc64-musl": "4.62.2", "@rollup/rollup-linux-riscv64-gnu": "4.62.2", "@rollup/rollup-linux-riscv64-musl": "4.62.2", "@rollup/rollup-linux-s390x-gnu": "4.62.2", "@rollup/rollup-linux-x64-gnu": "4.62.2", "@rollup/rollup-linux-x64-musl": "4.62.2", "@rollup/rollup-openbsd-x64": "4.62.2", "@rollup/rollup-openharmony-arm64": "4.62.2", "@rollup/rollup-win32-arm64-msvc": "4.62.2", "@rollup/rollup-win32-ia32-msvc": "4.62.2", "@rollup/rollup-win32-x64-gnu": "4.62.2", "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA=="],
195
+
196
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
197
+
198
+ "structured-headers": ["structured-headers@2.0.2", "", {}, "sha512-IUul56vVHuMg2UxWhwDj9zVJE6ztYEQQkynr1FQ/NydPhivtk5+Qb2N1RS36owEFk2fNUriTguJ2R7htRObcdA=="],
199
+
200
+ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
201
+
202
+ "undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="],
203
+
204
+ "vite": ["vite@7.3.6", "", { "dependencies": { "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg=="],
205
+
206
+ "web-bot-auth": ["web-bot-auth@0.1.3", "", { "dependencies": { "http-message-sig": "^0.2.0", "jsonwebkey-thumbprint": "^0.1.0" } }, "sha512-gDT9+5aMQbo4A0nNTPvIiN70a4F9iI+NLsmcE14CcTbiUsGgQOAnDZUDHCmDpvIqyJgZWkOcbv+jDvSmhKaIXQ=="],
207
+ }
208
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "__APP_NAME__",
3
3
  "private": true,
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "janux dev",
@@ -9,9 +9,17 @@
9
9
  "start": "janux start"
10
10
  },
11
11
  "dependencies": {
12
- "janux": "^0.1.0",
13
- "@janux/server": "^0.1.0",
14
- "@janux/agent": "^0.1.0",
15
- "@janux/cli": "^0.1.0"
12
+ "janux": "^0.2.1",
13
+ "@janux/server": "^0.2.1",
14
+ "@janux/cli": "^0.2.1"
15
+ },
16
+ "overrides": {
17
+ "janux": "^0.2.1",
18
+ "@janux/server": "^0.2.1",
19
+ "@janux/agent": "^0.2.1",
20
+ "@janux/vite": "^0.2.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/bun": "^1"
16
24
  }
17
25
  }
@@ -0,0 +1,19 @@
1
+ <svg width="240" height="240" viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <style>
3
+ svg { color: #000000; }
4
+ @media (prefers-color-scheme: dark) { svg { color: #ffffff; } }
5
+ </style>
6
+
7
+ <!-- the shared head: one column -->
8
+ <rect x="112" y="46" width="16" height="148" rx="8" fill="currentColor"/>
9
+
10
+ <!-- human face, looking left: curved profile + round eye -->
11
+ <path d="M112 60 C 66 67, 46 96, 46 120 C 46 144, 66 173, 112 180"
12
+ stroke="currentColor" stroke-width="16" stroke-linecap="round" fill="none"/>
13
+ <circle cx="80" cy="110" r="11" fill="currentColor"/>
14
+
15
+ <!-- agent face, looking right: angular profile + square eye -->
16
+ <path d="M128 60 L 176 77 L 193 120 L 176 163 L 128 180"
17
+ stroke="currentColor" stroke-width="16" stroke-linejoin="round" stroke-linecap="round" fill="none"/>
18
+ <rect x="152" y="99" width="21" height="21" rx="3" fill="currentColor"/>
19
+ </svg>
@@ -1,4 +1,5 @@
1
1
  import { boot } from 'janux/client';
2
2
  import { Counter } from './components/Counter';
3
+ import { AgentPanel } from './components/AgentPanel';
3
4
 
4
- boot({ defs: [Counter] });
5
+ boot({ defs: [Counter, AgentPanel], glow: true });
@@ -0,0 +1,150 @@
1
+ import { component, intent, schema, str, list } from 'janux';
2
+
3
+ let stopListening: (() => void) | undefined;
4
+
5
+ function bridge(): any {
6
+ return (window as any).janux;
7
+ }
8
+
9
+ /** Builds a valid example payload from a tool's JSON Schema input. */
10
+ function exampleInput(input: any): Record<string, unknown> {
11
+ const entries = Object.entries(input?.properties ?? {}).map(([key, prop]: [string, any]) => {
12
+ if (prop.default !== undefined) return [key, prop.default];
13
+ if (prop.type === 'integer' || prop.type === 'number') return [key, 1];
14
+ if (prop.enum) return [key, prop.enum[0]];
15
+
16
+ return [key, 'example'];
17
+ });
18
+
19
+ return Object.fromEntries(entries);
20
+ }
21
+
22
+ /** The same manifest an agent gets over HTTP — islands not yet resumed included. */
23
+ async function fetchManifest(): Promise<any> {
24
+ const href = document.querySelector('link[rel="janux-manifest"]')?.getAttribute('href');
25
+
26
+ return href ? fetch(href).then((response) => response.json()) : bridge().manifest();
27
+ }
28
+
29
+ /** Re-reads the live manifest + resource. Assigns only on change to avoid useless re-renders. */
30
+ async function refresh(state: any): Promise<void> {
31
+ const manifest = await fetchManifest();
32
+ const tools = manifest.tools.map((tool: any) => ({
33
+ name: tool.name,
34
+ guard: tool.guard,
35
+ description: tool.description ?? '',
36
+ example: tool.input ? JSON.stringify(exampleInput(tool.input)) : '',
37
+ }));
38
+
39
+ if (JSON.stringify(tools) !== JSON.stringify(state.tools)) state.tools = tools;
40
+ const target = manifest.resources.find((entry: any) => !entry.uri.includes('agent-panel'));
41
+
42
+ if (!target) return;
43
+ const resource: any = await bridge().read(target.uri);
44
+ const snapshot = JSON.stringify({ state: resource.state, derived: resource.derived }, null, 2);
45
+
46
+ state.uri = target.uri;
47
+ if (snapshot !== state.resource) state.resource = snapshot;
48
+ }
49
+
50
+ export const AgentPanel = component({
51
+ name: 'agent-panel',
52
+ description: 'Live view of this page’s agent surface: tools, resource, approvals.',
53
+
54
+ state: schema({
55
+ tools: list({ name: str(), guard: str(), description: str(), example: str() }),
56
+ uri: str(),
57
+ resource: str(),
58
+ proposal: schema({ id: str(), tool: str() }).nullable(),
59
+ }),
60
+
61
+ intents: {
62
+ sync: intent({
63
+ description: 'Refresh the panel from the live manifest',
64
+ guard: 'forbidden',
65
+ run: ({ state }: any) => refresh(state),
66
+ }),
67
+ callTool: intent({
68
+ description: 'Invoke a tool exactly like an agent would',
69
+ guard: 'forbidden',
70
+ input: schema({ tool: str(), example: str() }),
71
+ run: async ({ state, input }: any) => {
72
+ const result: any = await bridge()
73
+ .call(input.tool, input.example ? JSON.parse(input.example) : undefined)
74
+ .catch((error: unknown) => ({ error: String(error) }));
75
+
76
+ if (result?.status === 'proposal') state.proposal = { id: result.id, tool: input.tool };
77
+ await refresh(state);
78
+ },
79
+ }),
80
+ approve: intent({
81
+ guard: 'forbidden',
82
+ run: async ({ state }: any) => {
83
+ await bridge().approve(state.proposal.id);
84
+ state.proposal = null;
85
+ await refresh(state);
86
+ },
87
+ }),
88
+ reject: intent({
89
+ guard: 'forbidden',
90
+ run: ({ state }: any) => {
91
+ bridge().reject(state.proposal?.id);
92
+ state.proposal = null;
93
+ },
94
+ }),
95
+ },
96
+
97
+ lifecycle: {
98
+ // The counter is also operated directly by humans — re-sync after any click settles.
99
+ attach: ({ intents }: any) => {
100
+ const resync = () => setTimeout(() => bridge() && intents.sync(), 60);
101
+
102
+ resync();
103
+ document.addEventListener('click', resync);
104
+ stopListening = () => document.removeEventListener('click', resync);
105
+ },
106
+ detach: () => stopListening?.(),
107
+ },
108
+
109
+ view: ({ state, intents }: any) => (
110
+ <aside class="agent-pane">
111
+ <h2>🤖 What the agent sees</h2>
112
+ {state.tools.map((tool: any) => (
113
+ <div key={tool.name} class="tool-row">
114
+ <code>{tool.name}</code>
115
+ <span class={`guard ${tool.guard}`}>{tool.guard}</span>
116
+ <small>{tool.description}</small>
117
+ {tool.example ? <code class="example">{tool.example}</code> : null}
118
+ <button on={intents.callTool} data-input={JSON.stringify({ tool: tool.name, example: tool.example })}>
119
+ Call as agent
120
+ </button>
121
+ </div>
122
+ ))}
123
+ {state.resource ? (
124
+ <div class="resource-block">
125
+ <h2>
126
+ Resource <code>{state.uri}</code>
127
+ </h2>
128
+ <pre class="resource">{state.resource}</pre>
129
+ </div>
130
+ ) : null}
131
+ {state.proposal ? (
132
+ <div class="proposal-card" role="alert">
133
+ <p class="proposal-title">⏸ Approval required</p>
134
+ <p>
135
+ The agent wants to run <code>{state.proposal.tool}</code>
136
+ </p>
137
+ <p class="proposal-why">guard: confirm — nothing happens until you decide.</p>
138
+ <div class="proposal-actions">
139
+ <button class="approve" on={intents.approve}>
140
+ Approve
141
+ </button>
142
+ <button class="reject" on={intents.reject}>
143
+ Reject
144
+ </button>
145
+ </div>
146
+ </div>
147
+ ) : null}
148
+ </aside>
149
+ ),
150
+ });
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { createInstance } from 'janux';
3
+ import { Counter } from './Counter';
4
+
5
+ describe('Counter', () => {
6
+ it('increments and decrements', async () => {
7
+ const counter = createInstance(Counter);
8
+
9
+ await counter.intents.inc!({});
10
+ await counter.intents.inc!({ by: 4 });
11
+ await counter.intents.dec!({});
12
+ expect(counter.snapshot().count).toBe(4);
13
+ });
14
+
15
+ it('reset is a proposal for agents — nothing happens until approval', async () => {
16
+ let proposal: any;
17
+ const counter = createInstance(Counter, { onProposal: (p) => (proposal = p) });
18
+
19
+ await counter.intents.inc!({ by: 3 });
20
+ const result: any = await counter.intents.reset!({}, { origin: 'agent' });
21
+
22
+ expect(result.status).toBe('proposal');
23
+ expect(counter.snapshot().count).toBe(3);
24
+ await proposal.execute();
25
+ expect(counter.snapshot().count).toBe(0);
26
+ });
27
+ });
@@ -2,28 +2,39 @@ import { component, intent, schema, int } from 'janux';
2
2
 
3
3
  export const Counter = component({
4
4
  name: 'counter',
5
- description: 'A counter. Agents can read it (ui://counter) and bump it (counter.inc).',
5
+ description: 'A counter agents can read and operate.',
6
6
 
7
- state: schema({ n: int() }),
7
+ state: schema({ count: int() }),
8
8
 
9
9
  intents: {
10
10
  inc: intent({
11
11
  description: 'Increment the counter',
12
12
  input: schema({ by: int().default(1) }),
13
- run: ({ state, input }) => (state.n += input.by),
13
+ run: ({ state, input }: any) => (state.count += input.by),
14
+ }),
15
+ dec: intent({
16
+ description: 'Decrement the counter',
17
+ input: schema({ by: int().default(1) }),
18
+ run: ({ state, input }: any) => (state.count -= input.by),
14
19
  }),
15
20
  reset: intent({
16
- description: 'Reset the counter to zero',
21
+ description: 'Reset to zero. Needs human approval.',
17
22
  guard: 'confirm',
18
- run: ({ state }) => (state.n = 0),
23
+ run: ({ state }: any) => (state.count = 0),
19
24
  }),
20
25
  },
21
26
 
22
- view: ({ state, intents }) => (
23
- <section>
24
- <output>{state.n}</output>
25
- <button on={intents.inc}>+1</button>
26
- <button on={intents.reset}>Reset</button>
27
+ view: ({ state, intents }: any) => (
28
+ <section class="counter">
29
+ <h1>{state.count}</h1>
30
+ <div class="row">
31
+ <button class="dec" on={intents.dec}>
32
+ −1
33
+ </button>
34
+ <button class="inc" on={intents.inc}>
35
+ +1
36
+ </button>
37
+ </div>
27
38
  </section>
28
39
  ),
29
40
  });
@@ -1,14 +1,27 @@
1
1
  import { Counter } from '../components/Counter';
2
+ import { AgentPanel } from '../components/AgentPanel';
3
+
4
+ export const meta = {
5
+ title: 'Welcome to Janux',
6
+ description: 'One component, two faces: a UI for you, typed tools for agents.',
7
+ };
2
8
 
3
9
  export default function Home() {
4
10
  return (
5
- <main>
6
- <h1>Welcome to Janux</h1>
7
- <p>This heading is static HTML (0 KB JS). The counter below is a resumable island.</p>
8
- <Counter />
9
- <p>
10
- Try the agent surface: <code>curl localhost:3000/_janux/manifest</code>
11
- </p>
12
- </main>
11
+ <div class="app">
12
+ <header class="bar">
13
+ <span class="brand">⚡ Janux</span>
14
+ <span class="bar-hint">The counter is your UI · the right panel is what an agent sees</span>
15
+ </header>
16
+ <main class="split">
17
+ <section class="preview">
18
+ <Counter />
19
+ <p class="curl">
20
+ Same surface over HTTP: <code>curl localhost:3000/_janux/manifest</code>
21
+ </p>
22
+ </section>
23
+ <AgentPanel eager />
24
+ </main>
25
+ </div>
13
26
  );
14
27
  }
@@ -1,10 +1,9 @@
1
1
  :root {
2
- --ink: #1e1b4b;
2
+ --ink: #0f172a;
3
3
  --violet: #7c3aed;
4
- --cyan: #06b6d4;
5
- --text: #334155;
6
4
  --border: #e2e8f0;
7
- --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
5
+ --muted: #64748b;
6
+ --mono: ui-monospace, SFMono-Regular, Menlo, monospace;
8
7
  }
9
8
 
10
9
  * {
@@ -13,208 +12,266 @@
13
12
 
14
13
  body {
15
14
  margin: 0;
16
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, sans-serif;
17
- color: var(--text);
18
- background: #f8fafc;
19
- -webkit-font-smoothing: antialiased;
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, sans-serif;
16
+ background: #fff;
17
+ color: var(--ink);
18
+ }
19
+
20
+ .bar {
21
+ display: flex;
22
+ align-items: baseline;
23
+ gap: 12px;
24
+ padding: 12px 16px;
25
+ border-bottom: 1px solid var(--border);
26
+ }
27
+
28
+ .brand {
29
+ font-weight: 800;
30
+ font-size: 1rem;
20
31
  }
21
32
 
22
- main {
23
- max-width: 960px;
24
- margin: 0 auto;
25
- padding: 48px 24px 120px;
33
+ .bar-hint {
34
+ color: var(--muted);
35
+ font-size: 13px;
26
36
  }
27
37
 
28
- main.shop {
38
+ .split {
29
39
  display: grid;
30
- grid-template-columns: 1fr 400px;
40
+ grid-template-columns: minmax(0, 1fr) 340px;
41
+ height: calc(100vh - 46px);
42
+ }
43
+
44
+ .preview {
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: center;
48
+ justify-content: center;
31
49
  gap: 28px;
50
+ border-right: 1px solid var(--border);
32
51
  }
33
52
 
34
- main.shop > h1 {
35
- grid-column: 1 / -1;
53
+ /* ── Counter ────────────────────────────────────────────────────── */
54
+ .counter {
55
+ display: flex;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ gap: 24px;
36
59
  }
37
60
 
38
- h1 {
39
- margin: 0 0 20px;
40
- font-size: 2.1rem;
61
+ .counter h1 {
62
+ margin: 0;
63
+ font-size: 3.75rem;
41
64
  font-weight: 800;
42
- letter-spacing: -0.03em;
43
- color: var(--ink);
65
+ color: #1e1b4b;
44
66
  }
45
67
 
46
- h2,
47
- h3 {
48
- color: var(--ink);
49
- letter-spacing: -0.02em;
68
+ .counter .row {
69
+ display: flex;
70
+ gap: 12px;
71
+ }
72
+
73
+ .counter button {
74
+ padding: 10px 28px;
75
+ border: none;
76
+ border-radius: 12px;
77
+ font-size: 1.25rem;
78
+ font-weight: 700;
79
+ color: #fff;
80
+ cursor: pointer;
81
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.15);
82
+ }
83
+
84
+ .counter .dec {
85
+ background: #64748b;
86
+ }
87
+
88
+ .counter .dec:hover {
89
+ background: #475569;
50
90
  }
51
91
 
52
- a {
53
- color: var(--violet);
54
- font-weight: 600;
92
+ .counter .inc {
93
+ background: linear-gradient(90deg, #7c3aed, #06b6d4);
55
94
  }
56
95
 
57
- code {
58
- padding: 2px 6px;
96
+ .counter .inc:hover {
97
+ opacity: 0.9;
98
+ }
99
+
100
+ .curl {
101
+ color: var(--muted);
102
+ font-size: 13px;
103
+ }
104
+
105
+ .curl code {
106
+ padding: 2px 8px;
59
107
  border-radius: 6px;
60
108
  background: #ede9fe;
61
109
  color: #5b21b6;
110
+ font-size: 12px;
62
111
  font-family: var(--mono);
63
- font-size: 0.88em;
64
112
  }
65
113
 
66
- button {
67
- padding: 8px 16px;
68
- border: none;
69
- border-radius: 9px;
70
- background: var(--ink);
71
- color: #fff;
72
- font-size: 14px;
73
- font-weight: 600;
74
- cursor: pointer;
75
- transition: transform 0.12s, opacity 0.12s;
114
+ /* ── Agent pane ─────────────────────────────────────────────────── */
115
+ .agent-pane {
116
+ overflow-y: auto;
117
+ padding: 14px;
118
+ background: #f8fafc;
76
119
  }
77
120
 
78
- button:hover {
79
- opacity: 0.9;
80
- transform: translateY(-1px);
121
+ .agent-pane h2 {
122
+ margin: 0 0 10px;
123
+ font-size: 0.95rem;
81
124
  }
82
125
 
83
- /* ── Cart ───────────────────────────────────────────────────────── */
84
- .cart {
85
- padding: 24px;
126
+ .agent-pane .tool-row {
127
+ margin-bottom: 8px;
128
+ padding: 10px 12px;
86
129
  border: 1px solid var(--border);
87
- border-radius: 16px;
130
+ border-radius: 10px;
88
131
  background: #fff;
89
- box-shadow: 0 8px 30px -18px rgba(30, 27, 75, 0.35);
90
- }
91
-
92
- .cart ul {
93
- margin: 12px 0;
94
- padding: 0;
95
- list-style: none;
132
+ font-size: 13px;
96
133
  }
97
134
 
98
- .cart .catalog li,
99
- .cart .items li {
100
- display: flex;
101
- align-items: center;
102
- justify-content: space-between;
103
- gap: 12px;
104
- padding: 10px 4px;
105
- border-bottom: 1px solid var(--border);
106
- font-size: 15px;
135
+ .agent-pane .tool-row code {
136
+ font-size: 12px;
137
+ font-family: var(--mono);
107
138
  }
108
139
 
109
- .cart .catalog button {
110
- background: linear-gradient(90deg, var(--violet), var(--cyan));
140
+ .agent-pane .tool-row small {
141
+ display: block;
142
+ margin-top: 2px;
143
+ color: var(--muted);
111
144
  }
112
145
 
113
- .cart .items li {
114
- color: var(--ink);
115
- font-weight: 600;
146
+ .agent-pane .tool-row .example {
147
+ display: inline-block;
148
+ margin-top: 6px;
149
+ padding: 2px 8px;
150
+ border-radius: 6px;
151
+ background: #f1f5f9;
152
+ color: var(--muted);
116
153
  }
117
154
 
118
- .cart .total {
119
- margin: 16px 0 8px;
120
- font-size: 1.2rem;
121
- font-weight: 800;
122
- color: var(--ink);
155
+ .agent-pane .guard {
156
+ margin-left: 6px;
157
+ padding: 1px 8px;
158
+ border-radius: 999px;
159
+ font-size: 11px;
160
+ font-weight: 700;
123
161
  }
124
162
 
125
- .cart .order {
126
- padding: 10px 14px;
127
- border-radius: 10px;
163
+ .agent-pane .guard.auto {
128
164
  background: #ecfdf5;
129
165
  color: #047857;
130
- font-weight: 600;
131
166
  }
132
167
 
133
- /* ── Copilot ────────────────────────────────────────────────────── */
134
- .copilot {
135
- display: flex;
136
- flex-direction: column;
137
- height: fit-content;
138
- padding: 20px;
139
- border: 1px solid var(--border);
140
- border-radius: 16px;
141
- background: #fff;
142
- box-shadow: 0 8px 30px -18px rgba(30, 27, 75, 0.35);
168
+ .agent-pane .guard.confirm {
169
+ background: #fffbeb;
170
+ color: #92400e;
143
171
  }
144
172
 
145
- .copilot h3 {
146
- margin: 0 0 12px;
173
+ .agent-pane .tool-row button {
174
+ display: block;
175
+ margin-top: 8px;
176
+ padding: 5px 12px;
177
+ border: none;
178
+ border-radius: 8px;
179
+ background: var(--ink);
180
+ color: #fff;
181
+ font-size: 12px;
182
+ cursor: pointer;
147
183
  }
148
184
 
149
- .copilot .chat {
150
- margin: 0 0 12px;
151
- padding: 0;
152
- list-style: none;
153
- max-height: 340px;
154
- overflow-y: auto;
185
+ .agent-pane .resource-block {
186
+ margin-top: 14px;
155
187
  }
156
188
 
157
- .copilot .chat li {
158
- margin-bottom: 8px;
159
- padding: 9px 13px;
189
+ .agent-pane .resource {
190
+ margin: 0;
191
+ padding: 12px;
192
+ border-radius: 10px;
193
+ max-height: 220px;
194
+ overflow: auto;
195
+ background: #16143a;
196
+ color: #a5f3fc;
197
+ font-size: 12px;
198
+ font-family: var(--mono);
199
+ }
200
+
201
+ /* ── Proposal (guard: confirm) ──────────────────────────────────── */
202
+ .proposal-card {
203
+ margin-top: 12px;
204
+ padding: 14px;
205
+ border: 2px solid #dc2626;
160
206
  border-radius: 12px;
161
- font-size: 14px;
162
- line-height: 1.5;
163
- max-width: 88%;
207
+ background: #fef2f2;
208
+ color: #991b1b;
209
+ font-size: 13px;
210
+ animation: proposal-pulse 1.2s ease-in-out 2;
164
211
  }
165
212
 
166
- .copilot .chat li.user {
167
- margin-left: auto;
168
- background: linear-gradient(90deg, var(--violet), #6d28d9);
169
- color: #fff;
213
+ .proposal-card p {
214
+ margin: 0 0 6px;
215
+ line-height: 1.5;
170
216
  }
171
217
 
172
- .copilot .chat li.assistant {
173
- background: #f1f5f9;
174
- color: var(--ink);
218
+ .proposal-card .proposal-title {
219
+ font-weight: 800;
220
+ font-size: 13.5px;
221
+ color: #b91c1c;
175
222
  }
176
223
 
177
- .copilot form {
178
- display: flex;
179
- gap: 8px;
224
+ .proposal-card .proposal-why {
225
+ color: #b45309;
180
226
  }
181
227
 
182
- .copilot input {
183
- flex: 1;
184
- padding: 9px 13px;
185
- border: 1px solid var(--border);
186
- border-radius: 9px;
187
- font-size: 14px;
188
- outline: none;
228
+ .proposal-card code {
229
+ background: #fee2e2;
230
+ color: #991b1b;
231
+ font-family: var(--mono);
189
232
  }
190
233
 
191
- .copilot input:focus {
192
- border-color: var(--violet);
193
- box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.15);
234
+ .proposal-actions {
235
+ display: flex;
236
+ gap: 8px;
237
+ margin-top: 10px;
194
238
  }
195
239
 
196
- .proposal {
197
- margin: 0 0 12px;
198
- padding: 14px;
199
- border: 1px solid #fde68a;
200
- border-radius: 12px;
201
- background: #fffbeb;
240
+ .proposal-actions .approve {
241
+ padding: 8px 18px;
242
+ border: none;
243
+ border-radius: 8px;
244
+ background: #dc2626;
245
+ color: #fff;
246
+ font-weight: 700;
247
+ cursor: pointer;
202
248
  }
203
249
 
204
- .proposal p {
205
- margin: 0 0 10px;
206
- font-size: 14px;
207
- color: #92400e;
250
+ .proposal-actions .reject {
251
+ padding: 8px 18px;
252
+ border: 1px solid #dc2626;
253
+ border-radius: 8px;
254
+ background: #fff;
255
+ color: #b91c1c;
256
+ font-weight: 700;
257
+ cursor: pointer;
208
258
  }
209
259
 
210
- .proposal button + button {
211
- margin-left: 8px;
212
- background: #e2e8f0;
213
- color: var(--ink);
260
+ @keyframes proposal-pulse {
261
+ 50% {
262
+ box-shadow: 0 0 0 5px rgba(220, 38, 38, 0.25);
263
+ }
214
264
  }
215
265
 
216
- @media (max-width: 900px) {
217
- main.shop {
266
+ @media (max-width: 800px) {
267
+ .split {
218
268
  grid-template-columns: 1fr;
269
+ height: auto;
270
+ }
271
+
272
+ .preview {
273
+ min-height: 60vh;
274
+ border-right: none;
275
+ border-bottom: 1px solid var(--border);
219
276
  }
220
277
  }
@@ -5,6 +5,7 @@
5
5
  "moduleResolution": "bundler",
6
6
  "strict": true,
7
7
  "skipLibCheck": true,
8
+ "types": ["bun"],
8
9
  "noEmit": true,
9
10
  "jsx": "react-jsx",
10
11
  "jsxImportSource": "janux"
@@ -1,9 +0,0 @@
1
- # Copilot model — pick ONE option (RFC §8.1 resolution order)
2
-
3
- # Option 1: explicit model
4
- # JANUX_MODEL=anthropic/claude-fable-5
5
-
6
- # Option 2: provider key only — default model inferred
7
- # ANTHROPIC_API_KEY=sk-ant-...
8
- # OPENAI_API_KEY=sk-...
9
- # GOOGLE_GENERATIVE_AI_API_KEY=...
@@ -1,5 +0,0 @@
1
- import { defineAgent } from '@janux/agent';
2
-
3
- export default defineAgent({
4
- instructions: 'You are this app’s copilot. Use the counter tools and api.hello.greet.',
5
- });
@@ -1,8 +0,0 @@
1
- import { api } from '@janux/server';
2
- import { schema, str } from 'janux';
3
-
4
- export const greet = api({
5
- description: 'Greet someone by name',
6
- input: schema({ name: str() }),
7
- run: ({ input }) => ({ message: `Hello, ${input.name}! — from a Janux api() tool` }),
8
- });