@xfxstudio/claworld 0.2.12 → 0.2.14

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 (62) hide show
  1. package/README.md +45 -19
  2. package/index.js +0 -1
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +1 -5
  5. package/skills/claworld-help/SKILL.md +84 -91
  6. package/skills/claworld-join-and-chat/SKILL.md +9 -9
  7. package/src/openclaw/index.js +0 -3
  8. package/src/openclaw/plugin/account-identity.js +0 -1
  9. package/src/openclaw/plugin/claworld-channel-plugin.js +8 -253
  10. package/src/openclaw/plugin/managed-config.js +1 -7
  11. package/src/openclaw/plugin/onboarding.js +128 -103
  12. package/src/openclaw/plugin/register.js +183 -232
  13. package/src/openclaw/plugin/relay-client.js +8 -5
  14. package/src/openclaw/runtime/product-shell-helper.js +11 -364
  15. package/src/openclaw/runtime/tool-contracts.js +0 -182
  16. package/src/openclaw/runtime/tool-inventory.js +4 -27
  17. package/bin/claworld.mjs +0 -9
  18. package/src/lib/agent-profile.js +0 -74
  19. package/src/lib/http-auth.js +0 -151
  20. package/src/lib/policy.js +0 -114
  21. package/src/openclaw/installer/cli.js +0 -406
  22. package/src/openclaw/installer/constants.js +0 -14
  23. package/src/openclaw/installer/core.js +0 -2122
  24. package/src/openclaw/installer/doctor.js +0 -876
  25. package/src/openclaw/installer/workspace-contract.js +0 -427
  26. package/src/product-shell/agent-cards/card-routes.js +0 -64
  27. package/src/product-shell/agent-cards/card-service.js +0 -287
  28. package/src/product-shell/agent-cards/spec-builder.js +0 -167
  29. package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
  30. package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
  31. package/src/product-shell/agent-cards/svg-renderer.js +0 -325
  32. package/src/product-shell/agent-cards/template-registry.js +0 -131
  33. package/src/product-shell/catalog/default-world-catalog.js +0 -38
  34. package/src/product-shell/contracts/candidate-feed.js +0 -393
  35. package/src/product-shell/contracts/world-manifest.js +0 -369
  36. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
  37. package/src/product-shell/feedback/feedback-contract.js +0 -13
  38. package/src/product-shell/feedback/feedback-routes.js +0 -98
  39. package/src/product-shell/feedback/feedback-service.js +0 -252
  40. package/src/product-shell/index.js +0 -212
  41. package/src/product-shell/matching/matchmaking-service.js +0 -395
  42. package/src/product-shell/membership/membership-service.js +0 -284
  43. package/src/product-shell/onboarding/onboarding-routes.js +0 -37
  44. package/src/product-shell/onboarding/onboarding-service.js +0 -220
  45. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
  46. package/src/product-shell/profile/profile-service.js +0 -142
  47. package/src/product-shell/profile/public-identity-routes.js +0 -160
  48. package/src/product-shell/profile/public-identity-service.js +0 -192
  49. package/src/product-shell/search/search-service.js +0 -393
  50. package/src/product-shell/social/chat-request-approval-policy.js +0 -332
  51. package/src/product-shell/social/chat-request-routes.js +0 -130
  52. package/src/product-shell/social/chat-request-service.js +0 -723
  53. package/src/product-shell/social/friend-routes.js +0 -82
  54. package/src/product-shell/social/friend-service.js +0 -557
  55. package/src/product-shell/social/social-routes.js +0 -21
  56. package/src/product-shell/social/social-service.js +0 -136
  57. package/src/product-shell/worlds/world-admin-service.js +0 -486
  58. package/src/product-shell/worlds/world-authorization.js +0 -136
  59. package/src/product-shell/worlds/world-broadcast-service.js +0 -296
  60. package/src/product-shell/worlds/world-routes.js +0 -403
  61. package/src/product-shell/worlds/world-service.js +0 -89
  62. package/src/product-shell/worlds/world-text.js +0 -75
@@ -1,74 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
-
4
- function normalizeText(value, fallback = null) {
5
- if (value == null) return fallback;
6
- const normalized = String(value).trim();
7
- return normalized || fallback;
8
- }
9
-
10
- function normalizePublicPath(filePath = '') {
11
- return String(filePath).split(path.sep).join('/');
12
- }
13
-
14
- function parseExpiryEpochSeconds(value) {
15
- const parsed = Number.parseInt(String(value || '').trim(), 10);
16
- return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
17
- }
18
-
19
- export function extractAgentCardExpiryEpochSeconds(relativeOrPublicPath = '') {
20
- const normalizedPath = normalizePublicPath(String(relativeOrPublicPath || '')).replace(/^\/+/, '');
21
- const parts = normalizedPath.split('/').filter(Boolean);
22
- if (parts[0] !== 'ephemeral') return null;
23
- return parseExpiryEpochSeconds(parts[1]);
24
- }
25
-
26
- export function createLocalPublicAgentCardStorage({
27
- publicDir,
28
- publicBasePath = '/public/agent-cards',
29
- } = {}) {
30
- const normalizedPublicDir = normalizeText(publicDir, null);
31
- if (!normalizedPublicDir) {
32
- throw new Error('agent_card_public_dir_required');
33
- }
34
-
35
- return {
36
- describeTarget({ contentHash, templateVersion = 'v1', expiresAt = null } = {}) {
37
- const normalizedHash = normalizeText(contentHash, null);
38
- if (!normalizedHash) throw new Error('agent_card_content_hash_required');
39
- const expiresAtEpochSeconds = expiresAt ? parseExpiryEpochSeconds(Math.floor(Date.parse(expiresAt) / 1000)) : null;
40
- const relativePath = expiresAtEpochSeconds
41
- ? path.join(
42
- 'ephemeral',
43
- String(expiresAtEpochSeconds),
44
- normalizeText(templateVersion, 'v1'),
45
- normalizedHash.slice(0, 2),
46
- normalizedHash.slice(2, 4),
47
- `${normalizedHash}.png`,
48
- )
49
- : path.join(
50
- normalizeText(templateVersion, 'v1'),
51
- normalizedHash.slice(0, 2),
52
- normalizedHash.slice(2, 4),
53
- `${normalizedHash}.png`,
54
- );
55
- return {
56
- contentHash: normalizedHash,
57
- expiresAt: expiresAtEpochSeconds ? new Date(expiresAtEpochSeconds * 1000).toISOString() : null,
58
- expiresAtEpochSeconds,
59
- isEphemeral: Boolean(expiresAtEpochSeconds),
60
- relativePath,
61
- absolutePath: path.join(normalizedPublicDir, relativePath),
62
- publicPath: `${normalizeText(publicBasePath, '/public/agent-cards')}/${normalizePublicPath(relativePath)}`,
63
- };
64
- },
65
- async exists(target) {
66
- return await fs.pathExists(target.absolutePath);
67
- },
68
- async writePng(target, pngBuffer) {
69
- await fs.ensureDir(path.dirname(target.absolutePath));
70
- await fs.writeFile(target.absolutePath, pngBuffer);
71
- return target;
72
- },
73
- };
74
- }
@@ -1,325 +0,0 @@
1
- import { Resvg } from '@resvg/resvg-js';
2
- import QRCode from 'qrcode';
3
-
4
- function normalizeText(value, fallback = null) {
5
- if (value == null) return fallback;
6
- const normalized = String(value).trim();
7
- return normalized || fallback;
8
- }
9
-
10
- function escapeXml(value) {
11
- return String(value || '')
12
- .replaceAll('&', '&')
13
- .replaceAll('<', '&lt;')
14
- .replaceAll('>', '&gt;')
15
- .replaceAll('"', '&quot;')
16
- .replaceAll("'", '&apos;');
17
- }
18
-
19
- function truncateText(value, maxLength, fallback = '') {
20
- const normalized = normalizeText(value, fallback) || fallback;
21
- if (normalized.length <= maxLength) return normalized;
22
- return `${normalized.slice(0, Math.max(1, maxLength - 1)).trimEnd()}…`;
23
- }
24
-
25
- function buildCtaMarkup(lines = []) {
26
- return lines
27
- .map((line, index) => {
28
- const dy = index === 0 ? '0' : '48';
29
- return `<tspan x="96" dy="${dy}">${escapeXml(truncateText(line, 84, ''))}</tspan>`;
30
- })
31
- .join('');
32
- }
33
-
34
- function buildSubtitleMarkup(text) {
35
- const normalized = truncateText(text, 180, 'Agent-to-agent worlds on OpenClaw.');
36
- const words = normalized.split(/\s+/g).filter(Boolean);
37
- const lines = [];
38
- let currentLine = '';
39
-
40
- for (const word of words) {
41
- const nextLine = currentLine ? `${currentLine} ${word}` : word;
42
- if (nextLine.length > 42 && currentLine) {
43
- lines.push(currentLine);
44
- currentLine = word;
45
- continue;
46
- }
47
- currentLine = nextLine;
48
- }
49
-
50
- if (currentLine) lines.push(currentLine);
51
-
52
- return lines.slice(0, 3).map((line, index) => {
53
- const dy = index === 0 ? '0' : '46';
54
- return `<tspan x="96" dy="${dy}">${escapeXml(line)}</tspan>`;
55
- }).join('');
56
- }
57
-
58
- async function buildQrDataUrl(targetUrl) {
59
- return await QRCode.toDataURL(targetUrl, {
60
- errorCorrectionLevel: 'M',
61
- margin: 1,
62
- width: 220,
63
- color: {
64
- dark: '#10203a',
65
- light: '#0000',
66
- },
67
- });
68
- }
69
-
70
- function applyTemplate(source, replacements) {
71
- return Object.entries(replacements).reduce(
72
- (result, [token, value]) => result.replaceAll(`{{${token}}}`, value),
73
- source,
74
- );
75
- }
76
-
77
- function splitGraphemes(value) {
78
- const normalized = String(value || '');
79
- if (!normalized) return [];
80
- if (typeof Intl?.Segmenter === 'function') {
81
- const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
82
- return Array.from(segmenter.segment(normalized), ({ segment }) => segment);
83
- }
84
- return Array.from(normalized);
85
- }
86
-
87
- function isFullWidthCodePoint(codePoint) {
88
- return (
89
- (codePoint >= 0x1100 && codePoint <= 0x115f) ||
90
- (codePoint >= 0x2329 && codePoint <= 0x232a) ||
91
- (codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||
92
- (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
93
- (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
94
- (codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
95
- (codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
96
- (codePoint >= 0xff01 && codePoint <= 0xff60) ||
97
- (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
98
- (codePoint >= 0x1f300 && codePoint <= 0x1fbff)
99
- );
100
- }
101
-
102
- function estimateGlyphWidthUnits(grapheme) {
103
- if (!grapheme) return 0;
104
- if (/^\s$/u.test(grapheme)) return 0.32;
105
- if (/^[.,:;!|`'"’“”‘]$/u.test(grapheme)) return 0.28;
106
- if (/^[()\[\]{}]$/u.test(grapheme)) return 0.36;
107
- if (/^[-_=+\\/<>]$/u.test(grapheme)) return 0.48;
108
- if (/^[#@%&*]$/u.test(grapheme)) return 0.72;
109
- if (/^[0-9]$/u.test(grapheme)) return 0.56;
110
- if (/^[A-Z]$/u.test(grapheme)) return /[MW]/u.test(grapheme) ? 0.9 : 0.68;
111
- if (/^[a-z]$/u.test(grapheme)) return /[mw]/u.test(grapheme) ? 0.78 : 0.56;
112
-
113
- const codePoint = grapheme.codePointAt(0);
114
- if (codePoint == null) return 0.6;
115
- if (isFullWidthCodePoint(codePoint)) return 1;
116
- return 0.62;
117
- }
118
-
119
- export function estimateTextWidth(text, fontSize, { letterSpacing = 0 } = {}) {
120
- if (!text || !fontSize) return 0;
121
- const graphemes = splitGraphemes(text);
122
- const glyphUnits = graphemes.reduce((total, grapheme) => total + estimateGlyphWidthUnits(grapheme), 0);
123
- const spacingWidth = Math.max(0, graphemes.length - 1) * letterSpacing;
124
- return (glyphUnits * fontSize) + spacingWidth;
125
- }
126
-
127
- function parseNumericAttribute(value, fallback = null) {
128
- if (value == null) return fallback;
129
- const parsed = Number.parseFloat(String(value));
130
- return Number.isFinite(parsed) ? parsed : fallback;
131
- }
132
-
133
- function parseLetterSpacing(value, fontSize) {
134
- if (!value) return 0;
135
- const normalized = String(value).trim();
136
- if (!normalized || normalized === 'normal') return 0;
137
- if (normalized.endsWith('em')) return parseNumericAttribute(normalized.slice(0, -2), 0) * fontSize;
138
- if (normalized.endsWith('px')) return parseNumericAttribute(normalized.slice(0, -2), 0);
139
- return parseNumericAttribute(normalized, 0);
140
- }
141
-
142
- function formatFontSize(value) {
143
- const rounded = Math.round(value * 10) / 10;
144
- return Number.isInteger(rounded) ? String(rounded) : String(rounded);
145
- }
146
-
147
- function shrinkTextToFitSingleLine(value, {
148
- maxWidth,
149
- maxFontSize,
150
- minFontSize,
151
- letterSpacing = 0,
152
- } = {}) {
153
- const text = String(value || '');
154
- if (!text) {
155
- return {
156
- text,
157
- fontSize: formatFontSize(maxFontSize || minFontSize || 16),
158
- };
159
- }
160
-
161
- const widthAtMax = estimateTextWidth(text, maxFontSize, { letterSpacing });
162
- if (!maxWidth || !maxFontSize || widthAtMax <= maxWidth) {
163
- return {
164
- text,
165
- fontSize: formatFontSize(maxFontSize || minFontSize || 16),
166
- };
167
- }
168
-
169
- const scaledFontSize = Math.max(
170
- minFontSize || maxFontSize,
171
- Math.min(maxFontSize, (maxFontSize * maxWidth) / Math.max(widthAtMax, 1)),
172
- );
173
-
174
- if (scaledFontSize > (minFontSize || maxFontSize)) {
175
- return {
176
- text,
177
- fontSize: formatFontSize(scaledFontSize),
178
- };
179
- }
180
-
181
- if (estimateTextWidth(text, minFontSize, { letterSpacing }) <= maxWidth) {
182
- return {
183
- text,
184
- fontSize: formatFontSize(minFontSize),
185
- };
186
- }
187
-
188
- const graphemes = splitGraphemes(text);
189
- let keptLength = graphemes.length;
190
- while (keptLength > 1) {
191
- const candidate = `${graphemes.slice(0, keptLength).join('').trimEnd()}…`;
192
- if (estimateTextWidth(candidate, minFontSize, { letterSpacing }) <= maxWidth) {
193
- return {
194
- text: candidate,
195
- fontSize: formatFontSize(minFontSize),
196
- };
197
- }
198
- keptLength -= 1;
199
- }
200
-
201
- return {
202
- text: '…',
203
- fontSize: formatFontSize(minFontSize || maxFontSize || 16),
204
- };
205
- }
206
-
207
- function parseTextTagAttributes(attributeSource) {
208
- const attributes = {};
209
- const pattern = /([:@\w.-]+)\s*=\s*(['"])(.*?)\2/gs;
210
- for (const match of attributeSource.matchAll(pattern)) {
211
- attributes[match[1]] = match[3];
212
- }
213
- return attributes;
214
- }
215
-
216
- function escapeRegExp(value) {
217
- return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
218
- }
219
-
220
- function setSvgAttribute(attributeSource, name, value) {
221
- const replacement = `${name}="${value}"`;
222
- const escapedName = escapeRegExp(name);
223
- const pattern = new RegExp(`(^|\\s)(${escapedName}\\s*=\\s*)(['"]).*?\\3`, 's');
224
- if (pattern.test(attributeSource)) {
225
- return attributeSource.replace(pattern, (match, prefix) => `${prefix}${replacement}`);
226
- }
227
- return `${attributeSource} ${replacement}`;
228
- }
229
-
230
- function resolveSlotTextValue({ field, token, spec }) {
231
- const fieldValue = field ? spec?.[field] : undefined;
232
- if (fieldValue != null && fieldValue !== '') return String(fieldValue);
233
-
234
- switch (token) {
235
- case 'DISPLAY_NAME':
236
- return spec?.displayName ?? 'Claworld Agent';
237
- case 'PUBLIC_HANDLE':
238
- return spec?.publicHandle ?? 'agent#0000';
239
- case 'TITLE':
240
- return spec?.title ?? 'Claworld Agent Card';
241
- case 'BADGE_TEXT':
242
- return spec?.badgeText ?? 'Preview';
243
- case 'FOOTER_LABEL':
244
- return spec?.footerLabel ?? 'claworld.love';
245
- case 'AVATAR_INITIALS':
246
- return spec?.avatarInitials ?? 'C';
247
- default:
248
- return null;
249
- }
250
- }
251
-
252
- export function applyAdaptiveTextSlots(source, { spec } = {}) {
253
- if (!source || !spec) return source;
254
-
255
- return source.replace(/<text\b(?<attributes>[^>]*)>(?<content>[\s\S]*?)<\/text>/g, (fullMatch, rawAttributes, rawContent) => {
256
- const attributes = parseTextTagAttributes(rawAttributes);
257
- const token = attributes['data-token'];
258
- const field = attributes['data-field'];
259
- if (!token && !field) return fullMatch;
260
-
261
- const resolvedValue = resolveSlotTextValue({ field, token, spec });
262
- if (resolvedValue == null) return fullMatch;
263
-
264
- const trimWhitespace = attributes['data-trim-whitespace'] === 'true';
265
- const normalizedValue = trimWhitespace ? normalizeText(resolvedValue, '') || '' : String(resolvedValue);
266
- const layout = attributes['data-layout'];
267
- const overflow = attributes['data-overflow'];
268
-
269
- let nextText = normalizedValue;
270
- let nextAttributes = rawAttributes;
271
-
272
- if (layout === 'single-line' && overflow === 'shrink-font-only') {
273
- const maxWidth = parseNumericAttribute(attributes['data-max-width']);
274
- const maxFontSize = parseNumericAttribute(attributes['data-max-font-size'], parseNumericAttribute(attributes['font-size'], 16));
275
- const minFontSize = parseNumericAttribute(attributes['data-min-font-size'], maxFontSize);
276
- const letterSpacing = parseLetterSpacing(attributes['letter-spacing'], maxFontSize);
277
- const fitted = shrinkTextToFitSingleLine(normalizedValue, {
278
- maxWidth,
279
- maxFontSize,
280
- minFontSize,
281
- letterSpacing,
282
- });
283
- nextText = fitted.text;
284
- nextAttributes = setSvgAttribute(nextAttributes, 'font-size', fitted.fontSize);
285
- }
286
-
287
- return `<text${nextAttributes}>${escapeXml(nextText)}</text>`;
288
- });
289
- }
290
-
291
- export function createAgentCardSvgRenderer() {
292
- return {
293
- async renderCard({ template, spec } = {}) {
294
- if (!template?.source) throw new Error('agent_card_template_source_missing');
295
- if (!spec || typeof spec !== 'object') throw new Error('agent_card_spec_missing');
296
-
297
- const qrDataUrl = await buildQrDataUrl(spec.qrTargetUrl);
298
- const svg = applyTemplate(applyAdaptiveTextSlots(template.source, { spec }), {
299
- DISPLAY_NAME: escapeXml(truncateText(spec.displayName, 40, 'Claworld Agent')),
300
- PUBLIC_HANDLE: escapeXml(truncateText(spec.publicHandle, 48, 'agent#0000')),
301
- TITLE: escapeXml(truncateText(spec.title, 56, 'Claworld Agent Card')),
302
- SUBTITLE_LINES: buildSubtitleMarkup(spec.subtitle),
303
- CTA_LINES: buildCtaMarkup(spec.ctaLines),
304
- FOOTER_LABEL: escapeXml(truncateText(spec.footerLabel, 80, 'claworld.love')),
305
- BADGE_TEXT: escapeXml(truncateText(spec.badgeText, 24, 'Preview')),
306
- AVATAR_INITIALS: escapeXml(truncateText(spec.avatarInitials, 2, 'C')),
307
- QR_DATA_URL: escapeXml(qrDataUrl),
308
- });
309
-
310
- const resvg = new Resvg(svg, {
311
- fitTo: {
312
- mode: 'width',
313
- value: template.width || 1200,
314
- },
315
- font: {
316
- loadSystemFonts: true,
317
- defaultFontFamily: 'Arial',
318
- },
319
- });
320
-
321
- const image = resvg.render();
322
- return image.asPng();
323
- },
324
- };
325
- }
@@ -1,131 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
-
7
- const TEMPLATE_DEFINITIONS = Object.freeze([
8
- {
9
- templateId: 'agent-card.figma-v2',
10
- templateVersion: 'v1',
11
- width: 1077,
12
- height: 1443,
13
- templatePath: path.join(__dirname, 'templates', 'agent-card.figma-v2.svg'),
14
- description: 'Embedded Figma-export card with centered name/handle over the illustrated shrimp poster.',
15
- },
16
- {
17
- templateId: 'agent-card.slot-01',
18
- templateVersion: 'v1',
19
- width: 1792,
20
- height: 2400,
21
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-01.svg'),
22
- description: 'Slot-driven poster card with wide centered name/handle fields and shrink-to-fit metadata.',
23
- },
24
- {
25
- templateId: 'agent-card.slot-02',
26
- templateVersion: 'v1',
27
- width: 1792,
28
- height: 2400,
29
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-02.svg'),
30
- description: 'Slot-driven right-side card with centered adaptive text fields and tighter width constraints.',
31
- },
32
- {
33
- templateId: 'agent-card.slot-03',
34
- templateVersion: 'v1',
35
- width: 1792,
36
- height: 2400,
37
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-03.svg'),
38
- description: 'Slot-driven wide centered card with larger adaptive text fields over an embedded background.',
39
- },
40
- {
41
- templateId: 'agent-card.slot-04',
42
- templateVersion: 'v1',
43
- width: 1792,
44
- height: 2400,
45
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-04.svg'),
46
- description: 'Slot-driven centered card with compact adaptive text fields over an embedded earthy background.',
47
- },
48
- {
49
- templateId: 'agent-card.slot-05',
50
- templateVersion: 'v1',
51
- width: 1792,
52
- height: 2400,
53
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-05.svg'),
54
- description: 'Figma-adjusted poster card with left-anchored adaptive name and handle slots.',
55
- },
56
- {
57
- templateId: 'agent-card.slot-06',
58
- templateVersion: 'v1',
59
- width: 1792,
60
- height: 2400,
61
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-06.svg'),
62
- description: 'Slot-driven right-column card with centered adaptive text fields over an embedded background.',
63
- },
64
- {
65
- templateId: 'agent-card.slot-07',
66
- templateVersion: 'v1',
67
- width: 1792,
68
- height: 2400,
69
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-07.svg'),
70
- description: 'Figma-adjusted warm poster card with large left-anchored adaptive name and handle slots.',
71
- },
72
- {
73
- templateId: 'agent-card.slot-08',
74
- templateVersion: 'v1',
75
- width: 1792,
76
- height: 2400,
77
- templatePath: path.join(__dirname, 'templates', 'agent-card.slot-08.svg'),
78
- description: 'Figma-adjusted rotated poster card with adaptive slanted name and handle slots.',
79
- },
80
- ]);
81
-
82
- function normalizeText(value, fallback = null) {
83
- if (value == null) return fallback;
84
- const normalized = String(value).trim();
85
- return normalized || fallback;
86
- }
87
-
88
- export function createAgentCardTemplateRegistry() {
89
- const sourceCache = new Map();
90
- const templatesById = new Map(TEMPLATE_DEFINITIONS.map((template) => [template.templateId, template]));
91
-
92
- async function loadSource(template) {
93
- const cached = sourceCache.get(template.templateId);
94
- if (cached) return cached;
95
- const source = await fs.readFile(template.templatePath, 'utf8');
96
- sourceCache.set(template.templateId, source);
97
- return source;
98
- }
99
-
100
- return {
101
- listTemplates() {
102
- return TEMPLATE_DEFINITIONS.map((template) => ({
103
- templateId: template.templateId,
104
- templateVersion: template.templateVersion,
105
- width: template.width,
106
- height: template.height,
107
- description: template.description,
108
- }));
109
- },
110
- async getTemplate(templateId = TEMPLATE_DEFINITIONS[0].templateId) {
111
- const normalizedTemplateId = normalizeText(templateId, TEMPLATE_DEFINITIONS[0].templateId);
112
- const template = templatesById.get(normalizedTemplateId) || null;
113
- if (!template) {
114
- const error = new Error(`unknown_agent_card_template:${normalizedTemplateId}`);
115
- error.code = 'unknown_agent_card_template';
116
- error.status = 404;
117
- error.responseBody = {
118
- error: error.code,
119
- message: 'agent card template not found',
120
- templateId: normalizedTemplateId,
121
- };
122
- throw error;
123
- }
124
-
125
- return {
126
- ...template,
127
- source: await loadSource(template),
128
- };
129
- },
130
- };
131
- }
@@ -1,38 +0,0 @@
1
- export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
2
- {
3
- worldId: 'dating-demo-world',
4
- displayName: 'Dating Demo World',
5
- summary: 'A lightweight social world for proving world-scoped join, review, and chat request flow.',
6
- description:
7
- 'A lightweight social world where agents compare fit briefly before deciding whether to continue beyond agent chat.',
8
- category: 'social',
9
- lifecycle: 'prototype',
10
- tags: ['dating', 'matching', 'a2a'],
11
- worldContextText:
12
- '世界:Dating Demo World [dating-demo-world]\n简介:A lightweight social world for proving world-scoped join, review, and chat request flow.\n互动规则:Clarify fit quickly, stay respectful, and stop once next steps are clear.\n禁止事项:Do not pressure, harass, manipulate, or ask for unsafe personal details.',
13
- },
14
- {
15
- worldId: 'skill-handoff-world',
16
- displayName: 'Skill Handoff World',
17
- summary: 'A small scoped skill-handoff world for proving service-fit review before direct contact.',
18
- description:
19
- 'A service marketplace world where agents quickly screen delivery fit before moving to direct human contact.',
20
- category: 'marketplace',
21
- lifecycle: 'prototype',
22
- tags: ['skills', 'services', 'matching'],
23
- worldContextText:
24
- '世界:Skill Handoff World [skill-handoff-world]\n简介:A service marketplace world where agents screen delivery fit before moving to direct human contact.\n互动规则:Clarify scope, constraints, and whether a handoff is justified.\n禁止事项:Do not misrepresent capabilities or hide delivery blockers.',
25
- },
26
- {
27
- worldId: 'job-match-world',
28
- displayName: 'Job Match World',
29
- summary: 'A hiring-fit world optimized for profile completeness, matching, and concise A2A screening.',
30
- description:
31
- 'A recruiting world for agent-assisted screening where both sides validate fit before escalating to direct human contact.',
32
- category: 'recruiting',
33
- lifecycle: 'prototype',
34
- tags: ['jobs', 'recruiting', 'screening'],
35
- worldContextText:
36
- '世界:Job Match World [job-match-world]\n简介:A recruiting world for agent-assisted screening before direct human contact.\n互动规则:Focus on fit, constraints, and whether a next step is justified.\n禁止事项:Do not fabricate experience, compensation, or hiring authority.',
37
- },
38
- ]);