create-openagent 0.2.0 → 0.4.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/CHANGELOG.md +14 -0
- package/README.md +3 -3
- package/dist/index.js +32 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog — create-openagent
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-10-12
|
|
4
|
+
|
|
5
|
+
- Pin SDK 0.6.0 and add combinable glasses/headphones controls that preserve the
|
|
6
|
+
real receiver status, selected character family and animated eye nodes.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## [0.3.0] - 2026-10-12
|
|
10
|
+
|
|
11
|
+
- Give every starter selector an inset arrow and room for its label, with a
|
|
12
|
+
native-control fallback for forced colors.
|
|
13
|
+
- Pin SDK 0.5.0 and populate family/kind choices from its catalog, including all
|
|
14
|
+
holiday families. Keep Autos default, face framing, real verified-request state
|
|
15
|
+
and stable animated eye nodes across appearance changes.
|
|
16
|
+
|
|
3
17
|
## [0.2.0] - 2026-10-11
|
|
4
18
|
|
|
5
19
|
- Generate a complete local identity and animated-avatar application using SDK
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ explicit receiver trust, authenticated HTTP requests and animated avatars run
|
|
|
5
5
|
locally without a Life account or a hosted identity service.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx create-openagent@0.
|
|
8
|
+
npx create-openagent@0.4.0 my-agent-app --yes
|
|
9
9
|
cd my-agent-app
|
|
10
10
|
npm install
|
|
11
11
|
npm run dev
|
|
@@ -13,8 +13,8 @@ npm run dev
|
|
|
13
13
|
|
|
14
14
|
In a second terminal, run `npm run agent`. It signs an actual server-issued
|
|
15
15
|
challenge; the receiver verifies the signature and current trust and consumes
|
|
16
|
-
the challenge exactly once. The viewer uses Autos by default,
|
|
17
|
-
and updates from actual verified requests. It uses the same SDK player you can
|
|
16
|
+
the challenge exactly once. The viewer uses Autos by default, offers all 17 families and 62 kinds with face
|
|
17
|
+
framing (including SpaceX and holiday characters), and updates from actual verified requests. It uses the same SDK player you can
|
|
18
18
|
mount in your own UI.
|
|
19
19
|
|
|
20
20
|
Node 20+ is required. Private keys and receiver state stay in the generated
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath } from "url";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "create-openagent",
|
|
11
|
-
version: "0.
|
|
11
|
+
version: "0.4.0",
|
|
12
12
|
description: "Create a working OpenAgentID app with local identity, real verification and live avatars.",
|
|
13
13
|
license: "Apache-2.0 OR MIT",
|
|
14
14
|
author: "L1fe Labs, Inc. <eng@l1fe.ai>",
|
|
@@ -155,7 +155,7 @@ function generateStarterTemplate(projectName) {
|
|
|
155
155
|
identity: "node src/identity.mjs",
|
|
156
156
|
test: "node --test test/*.test.mjs"
|
|
157
157
|
},
|
|
158
|
-
dependencies: { "@openagentid/sdk": "0.
|
|
158
|
+
dependencies: { "@openagentid/sdk": "0.6.0" }
|
|
159
159
|
}, null, 2) + "\n" },
|
|
160
160
|
{ path: ".gitignore", content: "node_modules/\n.openagent/\ncoverage/\n.env\n" },
|
|
161
161
|
{ path: "src/workspaces.mjs", content: workspaces },
|
|
@@ -430,17 +430,35 @@ var html = String.raw`<!doctype html>
|
|
|
430
430
|
<title>My OpenAgent</title><link rel="stylesheet" href="/style.css"><script type="module" src="/viewer.js"></script></head>
|
|
431
431
|
<body><main><p class="eyebrow">OpenAgent · local starter</p><h1>Your agent, alive.</h1>
|
|
432
432
|
<div id="avatar" aria-label="Agent avatar"></div><p id="status" role="status">Connecting to the local receiver…</p>
|
|
433
|
-
<p id="identity"></p><div class="controls"><label>
|
|
433
|
+
<p id="identity"></p><div class="controls"><label>Family <select id="skin"></select></label>
|
|
434
|
+
<label>Kind <select id="shape"></select></label>
|
|
434
435
|
<label>Expression <select id="expression"><option value="">Follow actual status</option></select></label></div>
|
|
436
|
+
<fieldset><legend>Accessories</legend><div id="accessories" class="controls"></div></fieldset>
|
|
435
437
|
<p>In another terminal, run:</p><pre>npm run agent -- "Hello from my agent"</pre>
|
|
436
438
|
<p>This viewer is read-only. A completed request appears after the receiver verifies a fresh signed proof.</p>
|
|
437
439
|
<p>Keys stay on this computer. No account or identity service is required.</p></main></body></html>
|
|
438
440
|
`;
|
|
439
|
-
var css = String.raw`:root{font-family:system-ui,sans-serif;color:#ecf0f5;background:#10151c;color-scheme:dark}body{margin:0}main{max-width:760px;margin:0 auto;padding:48px 24px}h1{font-size:clamp(2rem,6vw,3.8rem);letter-spacing:-.05em;margin:12px 0}p{line-height:1.6;color:#bdc8d6}.eyebrow{font-size:.85rem;letter-spacing:.1em;text-transform:uppercase}#avatar{height:300px;display:flex;align-items:center;justify-content:center;margin:12px 0}#avatar svg{max-width:100%;height:280px}#identity{font-family:monospace;font-size:.85rem;overflow-wrap:anywhere}.controls{display:flex;gap:24px;flex-wrap:wrap}label{display:flex;gap:10px;align-items:center}select{padding:8px;border-radius:8px;background:#222d3b;color:inherit;border:1px solid #516176}pre{padding:18px;white-space:pre-wrap;background:#1b2532;border-radius:12px;border:1px solid #344458}#status{font-weight:600;color:#fff}
|
|
441
|
+
var css = String.raw`:root{font-family:system-ui,sans-serif;color:#ecf0f5;background:#10151c;color-scheme:dark}body{margin:0}main{max-width:760px;margin:0 auto;padding:48px 24px}h1{font-size:clamp(2rem,6vw,3.8rem);letter-spacing:-.05em;margin:12px 0}p{line-height:1.6;color:#bdc8d6}.eyebrow{font-size:.85rem;letter-spacing:.1em;text-transform:uppercase}#avatar{height:300px;display:flex;align-items:center;justify-content:center;margin:12px 0}#avatar svg{max-width:100%;height:280px;border-radius:50%;overflow:hidden}#identity{font-family:monospace;font-size:.85rem;overflow-wrap:anywhere}.controls{display:flex;gap:24px;flex-wrap:wrap}label{display:flex;gap:10px;align-items:center}fieldset{border:0;margin:24px 0;padding:0;min-width:0}legend{padding:0;margin-bottom:12px}input[type=checkbox]{width:18px;height:18px;accent-color:#1687ff}select{appearance:none;padding:8px 38px 8px 12px;border-radius:8px;background-color:#222d3b;background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,transparent 50%);background-position:right 17px center,right 12px center;background-size:5px 5px;background-repeat:no-repeat;color:inherit;border:1px solid #516176}@media(forced-colors:active){select{appearance:auto;background-image:none}}pre{padding:18px;white-space:pre-wrap;background:#1b2532;border-radius:12px;border:1px solid #344458}#status{font-weight:600;color:#fff}
|
|
440
442
|
`;
|
|
441
|
-
var viewer = String.raw`import { mountCharacter, getCharacterExpressions } from '/character-player.js';
|
|
443
|
+
var viewer = String.raw`import { mountCharacter, getCharacterExpressions, getCharacterSkins, getCharacterAccessories } from '/character-player.js';
|
|
442
444
|
|
|
443
445
|
const skin = document.querySelector('#skin');
|
|
446
|
+
const shape = document.querySelector('#shape');
|
|
447
|
+
const catalog = getCharacterSkins();
|
|
448
|
+
const accessories = document.querySelector('#accessories');
|
|
449
|
+
const selectedAccessories = () => Array.from(accessories.querySelectorAll('input:checked'), input => input.value);
|
|
450
|
+
for (const accessory of getCharacterAccessories()) {
|
|
451
|
+
const label = document.createElement('label');
|
|
452
|
+
const input = document.createElement('input'); input.type = 'checkbox'; input.value = accessory.id;
|
|
453
|
+
label.append(input, document.createTextNode(accessory.label)); accessories.append(label);
|
|
454
|
+
}
|
|
455
|
+
const display = value => value.replaceAll('-', ' ').replace(/^./, letter => letter.toUpperCase());
|
|
456
|
+
skin.replaceChildren(...catalog.map(entry => new Option(entry.label, entry.id)));
|
|
457
|
+
skin.value = 'autos';
|
|
458
|
+
function kinds() {
|
|
459
|
+
shape.replaceChildren(new Option('From identity', ''), ...catalog.find(entry => entry.id === skin.value).shapes.map(value => new Option(display(value), value)));
|
|
460
|
+
}
|
|
461
|
+
kinds();
|
|
444
462
|
const expression = document.querySelector('#expression');
|
|
445
463
|
const label = document.querySelector('#status');
|
|
446
464
|
const identity = document.querySelector('#identity');
|
|
@@ -449,7 +467,9 @@ for (const value of getCharacterExpressions()) {
|
|
|
449
467
|
expression.append(option);
|
|
450
468
|
}
|
|
451
469
|
let player, did, revision = -1, latestStatus = 'idle', stopped = false, timer;
|
|
452
|
-
skin.addEventListener('change', () => player?.setAppearance({ skin: skin.value }));
|
|
470
|
+
skin.addEventListener('change', () => { kinds(); player?.setAppearance({ skin: skin.value }); });
|
|
471
|
+
shape.addEventListener('change', () => player?.setAppearance({ shape: shape.value || null }));
|
|
472
|
+
accessories.addEventListener('change', () => player?.setAppearance({ accessories: selectedAccessories() }));
|
|
453
473
|
expression.addEventListener('change', () => player?.update({ status: latestStatus, expression: expression.value || null }));
|
|
454
474
|
async function refresh() {
|
|
455
475
|
try {
|
|
@@ -461,7 +481,7 @@ async function refresh() {
|
|
|
461
481
|
!['idle', 'working', 'success'].includes(state.status) || !Number.isSafeInteger(state.completedRequests) || state.completedRequests < 0) throw new Error('Invalid receiver state.');
|
|
462
482
|
if (!player || state.did !== did) {
|
|
463
483
|
player?.dispose(); document.querySelector('#avatar').replaceChildren();
|
|
464
|
-
did = state.did; player = mountCharacter(document.querySelector('#avatar'), did, { skin: skin.value, size: 280, reducedMotion: 'system' });
|
|
484
|
+
did = state.did; player = mountCharacter(document.querySelector('#avatar'), did, { skin: skin.value, accessories: selectedAccessories(), ...(shape.value ? { shape: shape.value } : {}), size: 280, reducedMotion: 'system' });
|
|
465
485
|
identity.textContent = did; revision = -1;
|
|
466
486
|
}
|
|
467
487
|
if (state.revision !== revision) {
|
|
@@ -482,7 +502,7 @@ Requires Node 20+. Run npm install, then npm run dev. Open the numeric loopback
|
|
|
482
502
|
|
|
483
503
|
npm run agent -- "Hello from my agent"
|
|
484
504
|
|
|
485
|
-
The command obtains a receiver-issued challenge, signs it with a persisted Ed25519 key, and sends a proof. After verification, the receiver hashes and echoes the message. The browser shows the real completed-request count, animated Autos by default, optional SpaceX and selectable expressions. It does not simulate work or broadcast runtime state elsewhere. Reduced motion follows your browser preference.
|
|
505
|
+
The command obtains a receiver-issued challenge, signs it with a persisted Ed25519 key, and sends a proof. After verification, the receiver hashes and echoes the message. The browser shows the real completed-request count, animated Autos by default, 16 optional families (including SpaceX and seasonal characters), distinct kinds, face framing, combinable glasses/headphones and selectable expressions. It does not simulate work or broadcast runtime state elsewhere. Reduced motion follows your browser preference.
|
|
486
506
|
|
|
487
507
|
On first setup, src/workspaces.mjs creates two separate local authorities, persists a unique receiver audience and explicitly imports the signer's public DID/key document into the receiver with trustIdentity. The receiver has no signing key for that agent. The public export is .openagent/identity.json. Private keys, challenge and replay state remain under .openagent/ and are excluded from git. Inspect public identity with npm run identity -- export. Never publish .openagent/ or serve it as static assets.
|
|
488
508
|
|
|
@@ -494,7 +514,7 @@ Revocation is the receiver's durable local trust decision. Restarting does not r
|
|
|
494
514
|
|
|
495
515
|
The HTTP service listens only on 127.0.0.1:3210. Set PORT consistently for the server and agent command to choose another port, or set OPENAGENT_RECEIVER_URL on the command to the exact printed numeric origin. Host checks prevent DNS rebinding, cross-origin browser controls are refused, and only fixed public assets are served. Trust/revoke are local operator actions, never HTTP controls. GET /state contains only public DID and transient request presentation metadata; it contains no message, output, token or key.
|
|
496
516
|
|
|
497
|
-
This starter deliberately has no cloud-deployment, MCP, enterprise bridge or optional identity-stack dependency. Historical 0.1 templates need migration and are not generated by create-openagent 0.
|
|
517
|
+
This starter deliberately has no cloud-deployment, MCP, enterprise bridge or optional identity-stack dependency. Historical 0.1 templates need migration and are not generated by create-openagent 0.3.
|
|
498
518
|
`;
|
|
499
519
|
var tests = String.raw`import test from 'node:test';
|
|
500
520
|
import assert from 'node:assert/strict';
|
|
@@ -623,7 +643,9 @@ test('host, browser-origin, closed-schema and private/static paths are refused',
|
|
|
623
643
|
const viewer = await fetch(f.app.url + '/');
|
|
624
644
|
assert.equal(viewer.status, 200); assert.equal(viewer.headers.get('cache-control'), 'no-store');
|
|
625
645
|
assert.match(viewer.headers.get('content-security-policy'), /frame-ancestors 'none'/);
|
|
626
|
-
|
|
646
|
+
const markup = await viewer.text();
|
|
647
|
+
assert.match(markup, /<select id="skin"><\/select>/);
|
|
648
|
+
assert.match(markup, /<select id="shape"><\/select>/);
|
|
627
649
|
const script = await fetch(f.app.url + '/character-player.js');
|
|
628
650
|
assert.equal(script.status, 200); assert.match(await script.text(), /mountCharacter/);
|
|
629
651
|
const huge = await post(f.app, '/request', { value: 'x'.repeat(25000) }); assert.equal(huge.status, 413);
|
package/package.json
CHANGED