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 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.2.0 my-agent-app --yes
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, supports SpaceX,
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.2.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.4.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>Skin <select id="skin"><option value="autos">Autos</option><option value="spacex">SpaceX</option></select></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.2.
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
- assert.match(await viewer.text(), /Autos/);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openagent",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Create a working OpenAgentID app with local identity, real verification and live avatars.",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "author": "L1fe Labs, Inc. <eng@l1fe.ai>",