@clawlabz/clawskin 1.0.1 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawlabz/clawskin",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "Pixel Agent Skin Engine \u2014 Procedural 32\u00d732 character generator with scenes, animations & customization",
6
6
  "keywords": [
package/public/app.html CHANGED
@@ -319,11 +319,16 @@
319
319
  let app, editingSlot = null;
320
320
 
321
321
  // ── Fullscreen canvas sizing ──
322
+ // Fixed logical resolution — CSS scales it up with pixelated rendering.
323
+ // This keeps pixel characters proportional on any screen size.
324
+ const LOGICAL_W = 960;
325
+ const LOGICAL_H = 600;
326
+
322
327
  function resizeCanvas() {
323
328
  const c = document.getElementById('app-canvas');
324
- c.width = window.innerWidth;
325
- c.height = window.innerHeight;
326
- if (app) { app.width = c.width; app.height = c.height; }
329
+ c.width = LOGICAL_W;
330
+ c.height = LOGICAL_H;
331
+ if (app) { app.width = LOGICAL_W; app.height = LOGICAL_H; }
327
332
  }
328
333
  window.addEventListener('resize', resizeCanvas);
329
334
  resizeCanvas();
@@ -533,8 +538,8 @@
533
538
  document.addEventListener('DOMContentLoaded', () => {
534
539
  try {
535
540
  app = new ClawSkinApp('app-canvas', {
536
- width: window.innerWidth,
537
- height: window.innerHeight,
541
+ width: LOGICAL_W,
542
+ height: LOGICAL_H,
538
543
  });
539
544
  app.init();
540
545
  window._app = app;
@@ -168,17 +168,19 @@ class ClawSkinApp {
168
168
  const res = await fetch('/api/config', { signal: AbortSignal.timeout(2000) });
169
169
  if (res.ok) {
170
170
  const config = await res.json();
171
+ console.log('[ClawSkin] /api/config →', JSON.stringify(config));
171
172
  if (config?.gatewayUrl) {
172
173
  const saved = this.settings.load();
174
+ const token = config.token || saved.token || '';
175
+ this._configAgents = config.agents || [];
173
176
  this.settings.update({
174
177
  gatewayUrl: config.gatewayUrl,
178
+ token: token,
175
179
  autoConnect: true,
176
180
  });
177
181
  if (window._connPanel) {
178
182
  window._connPanel.render(this.settings.load());
179
183
  }
180
- // Use token from local config, fall back to previously saved token
181
- const token = config.token || saved.token || '';
182
184
  this.gateway.connect(config.gatewayUrl, token);
183
185
  return;
184
186
  }
@@ -266,55 +268,62 @@ class ClawSkinApp {
266
268
  // ──── Multi-Agent Management ────
267
269
 
268
270
  async _discoverAgents() {
271
+ // Start with agents from config file (all agents, regardless of activity)
272
+ const configAgents = this._configAgents || [];
273
+ const knownAgentIds = new Set();
274
+
275
+ // Phase 1: Create slots for all agents from config file
276
+ for (const agent of configAgents) {
277
+ if (!agent.id) continue;
278
+ knownAgentIds.add(agent.id);
279
+ this._addAgent({
280
+ agentId: agent.id,
281
+ name: agent.name || agent.id,
282
+ label: agent.name || agent.id,
283
+ sessionKeys: [],
284
+ });
285
+ }
286
+
287
+ // Phase 2: Discover active sessions from Gateway and attach to slots
269
288
  try {
270
289
  const result = await this.gateway.getSessionsList({ activeMinutes: 1440 });
271
290
  const sessions = result?.sessions || result || [];
272
291
 
273
- if (!Array.isArray(sessions) || sessions.length === 0) {
274
- this._addAgent({ agentId: 'main', label: 'Main Agent', sessionKeys: ['main'] });
275
- return;
276
- }
277
-
278
- // Group sessions by agentId
279
- // Session keys look like: "agent:main:main", "agent:ifig:discord:channel:123", "agent:xhs:main"
280
- // The agentId is either session.agentId or extracted from the key pattern "agent:<agentId>:..."
281
- const agentMap = new Map();
282
-
283
- for (const session of sessions) {
284
- const key = session.key || session.sessionKey;
285
- if (!key) continue;
292
+ if (Array.isArray(sessions)) {
293
+ for (const session of sessions) {
294
+ const key = session.key || session.sessionKey;
295
+ if (!key) continue;
286
296
 
287
- // Determine agentId: prefer session.agentId, else parse from key
288
- let agentId = session.agentId || null;
289
- if (!agentId) {
290
- const match = key.match(/^agent:([^:]+):/);
291
- agentId = match ? match[1] : 'main';
292
- }
297
+ let agentId = session.agentId || null;
298
+ if (!agentId) {
299
+ const match = key.match(/^agent:([^:]+):/);
300
+ agentId = match ? match[1] : 'main';
301
+ }
293
302
 
294
- if (!agentMap.has(agentId)) {
295
- agentMap.set(agentId, {
296
- agentId,
297
- label: session.label || agentId,
298
- sessionKeys: [],
299
- });
303
+ // If this agent wasn't in config, create a slot for it
304
+ if (!knownAgentIds.has(agentId)) {
305
+ knownAgentIds.add(agentId);
306
+ this._addAgent({
307
+ agentId,
308
+ label: session.label || agentId,
309
+ sessionKeys: [key],
310
+ });
311
+ } else {
312
+ // Attach session key to existing slot
313
+ const slot = this.agents.find(a => a.agentId === agentId);
314
+ if (slot && !slot.sessionKeys.includes(key)) {
315
+ slot.sessionKeys.push(key);
316
+ slot.stateMapper.addSessionKey(key);
317
+ }
318
+ }
300
319
  }
301
- agentMap.get(agentId).sessionKeys.push(key);
302
- }
303
-
304
- // Create one AgentSlot per unique agentId
305
- for (const [agentId, info] of agentMap) {
306
- this._addAgent({
307
- agentId,
308
- label: info.label,
309
- sessionKeys: info.sessionKeys,
310
- });
311
- }
312
-
313
- if (this.agents.length === 0) {
314
- this._addAgent({ agentId: 'main', label: 'Main Agent', sessionKeys: ['main'] });
315
320
  }
316
321
  } catch (e) {
317
322
  console.warn('[ClawSkinApp] session discovery failed:', e);
323
+ }
324
+
325
+ // Phase 3: Fallback — ensure at least one agent exists
326
+ if (this.agents.length === 0) {
318
327
  this._addAgent({ agentId: 'main', label: 'Main Agent', sessionKeys: ['main'] });
319
328
  }
320
329
 
@@ -46,8 +46,15 @@ class GatewayClient {
46
46
  }
47
47
 
48
48
  _setState(state, detail) {
49
- if (state === 'error') this.lastError = detail || 'Unknown error';
50
- else if (state === 'connected') this.lastError = null;
49
+ if (state === 'error') {
50
+ this.lastError = detail || 'Unknown error';
51
+ console.warn('[GatewayClient]', state, detail || '');
52
+ } else if (state === 'connected') {
53
+ this.lastError = null;
54
+ console.log('[GatewayClient] connected');
55
+ } else {
56
+ console.log('[GatewayClient]', state, detail || '');
57
+ }
51
58
  if (this.onStateChange) this.onStateChange(state, detail);
52
59
  }
53
60
 
@@ -200,10 +207,9 @@ class GatewayClient {
200
207
  params.auth.token = this.token;
201
208
  }
202
209
 
203
- // Device identity — Gateway requires Ed25519 signed device identity.
204
- // The signed message MUST use the same role/scopes/clientId/clientMode
205
- // as the connect params, because Gateway reconstructs the message to verify.
206
- if (typeof DeviceIdentity !== 'undefined') {
210
+ // Device identity — only needed for clients that require Ed25519 pairing
211
+ // (e.g. webchat-ui, control-ui). The 'webchat' client type works without it.
212
+ if (typeof DeviceIdentity !== 'undefined' && this.clientId !== 'webchat') {
207
213
  try {
208
214
  const identity = await DeviceIdentity.getOrCreate();
209
215
  const device = await DeviceIdentity.sign(identity, {
package/serve.cjs CHANGED
@@ -44,11 +44,16 @@ function getLocalGatewayConfig() {
44
44
  const config = JSON.parse(raw);
45
45
  const gw = config.gateway || {};
46
46
  const port = gw.port || 18789;
47
+ const agentsList = (config.agents?.list || []).map(a => ({
48
+ id: a.id,
49
+ name: a.identity?.name || a.name || a.id,
50
+ }));
47
51
  return {
48
52
  gatewayUrl: `ws://localhost:${port}`,
49
53
  // Safe to expose token on localhost — serve.cjs binds to 127.0.0.1 by default.
50
54
  // The token never leaves the local machine.
51
55
  token: (gw.auth && gw.auth.token) || '',
56
+ agents: agentsList,
52
57
  };
53
58
  } catch { continue; }
54
59
  }