@xfxstudio/claworld 0.2.10-beta.3 → 0.2.12
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/openclaw/installer/cli.js +4 -2
- package/src/openclaw/installer/core.js +7 -1
- package/src/openclaw/installer/doctor.js +3 -3
- package/src/openclaw/plugin/claworld-channel-plugin.js +94 -9
- package/src/openclaw/plugin/onboarding.js +1 -1
- package/src/openclaw/plugin/register.js +210 -36
- package/src/openclaw/runtime/tool-inventory.js +1 -2
- package/src/product-shell/agent-cards/card-routes.js +64 -0
- package/src/product-shell/agent-cards/card-service.js +287 -0
- package/src/product-shell/agent-cards/spec-builder.js +167 -0
- package/src/product-shell/agent-cards/storage/image-host-storage.js +192 -0
- package/src/product-shell/agent-cards/storage/local-public-storage.js +74 -0
- package/src/product-shell/agent-cards/svg-renderer.js +325 -0
- package/src/product-shell/agent-cards/template-registry.js +131 -0
- package/src/product-shell/index.js +24 -3
- package/src/product-shell/onboarding/onboarding-service.js +4 -4
- package/src/product-shell/profile/profile-service.js +142 -0
- package/src/product-shell/profile/public-identity-routes.js +100 -0
- package/src/product-shell/profile/public-identity-service.js +4 -2
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
}
|
|
@@ -7,6 +7,7 @@ import { DEFAULT_WORLD_MANIFESTS } from './catalog/default-world-catalog.js';
|
|
|
7
7
|
import { createOnboardingService } from './onboarding/onboarding-service.js';
|
|
8
8
|
import { registerOnboardingRoutes } from './onboarding/onboarding-routes.js';
|
|
9
9
|
import { createPublicIdentityService } from './profile/public-identity-service.js';
|
|
10
|
+
import { createProfileService } from './profile/profile-service.js';
|
|
10
11
|
import { registerPublicIdentityRoutes } from './profile/public-identity-routes.js';
|
|
11
12
|
import { createMembershipService } from './membership/membership-service.js';
|
|
12
13
|
import { createMatchmakingService } from './matching/matchmaking-service.js';
|
|
@@ -21,22 +22,25 @@ import { registerChatRequestRoutes } from './social/chat-request-routes.js';
|
|
|
21
22
|
import { createFeedbackService } from './feedback/feedback-service.js';
|
|
22
23
|
import { registerFeedbackRoutes } from './feedback/feedback-routes.js';
|
|
23
24
|
import { createConversationFeedbackService } from './conversation-feedback/conversation-feedback-service.js';
|
|
25
|
+
import { createAgentCardService } from './agent-cards/card-service.js';
|
|
26
|
+
import { registerAgentCardRoutes } from './agent-cards/card-routes.js';
|
|
24
27
|
|
|
25
28
|
export function createClaworldProductShell({
|
|
26
29
|
worldCatalog = DEFAULT_WORLD_MANIFESTS,
|
|
27
30
|
relay = null,
|
|
28
31
|
store = null,
|
|
29
32
|
presence = null,
|
|
33
|
+
agentCard = {},
|
|
30
34
|
} = {}) {
|
|
31
35
|
const worldService = createWorldService({ worldCatalog, store });
|
|
36
|
+
const conversationFeedbackService = createConversationFeedbackService({ store });
|
|
32
37
|
const onboardingService = createOnboardingService({ worldService, store });
|
|
33
|
-
const publicIdentityService = createPublicIdentityService({ store });
|
|
38
|
+
const publicIdentityService = createPublicIdentityService({ store, conversationFeedbackService });
|
|
34
39
|
const membershipService = createMembershipService({ worldService, store, publicIdentityService });
|
|
35
40
|
const worldAuthorizationService = createWorldAuthorizationService({
|
|
36
41
|
worldService,
|
|
37
42
|
membershipService,
|
|
38
43
|
});
|
|
39
|
-
const conversationFeedbackService = createConversationFeedbackService({ store });
|
|
40
44
|
const matchmakingService = createMatchmakingService({
|
|
41
45
|
worldService,
|
|
42
46
|
worldAuthorizationService,
|
|
@@ -77,7 +81,14 @@ export function createClaworldProductShell({
|
|
|
77
81
|
const friendService = createFriendService({ store, policy: relay?.policy, publicIdentityService });
|
|
78
82
|
const socialLookupService = createSocialService({ worldService, store });
|
|
79
83
|
const feedbackService = createFeedbackService({ store });
|
|
80
|
-
const
|
|
84
|
+
const agentCardService = createAgentCardService({
|
|
85
|
+
store,
|
|
86
|
+
...agentCard,
|
|
87
|
+
});
|
|
88
|
+
const profileService = createProfileService({
|
|
89
|
+
publicIdentityService,
|
|
90
|
+
agentCardService,
|
|
91
|
+
});
|
|
81
92
|
const socialService = {
|
|
82
93
|
...friendService,
|
|
83
94
|
lookupAgentByIdentity(input) {
|
|
@@ -103,6 +114,8 @@ export function createClaworldProductShell({
|
|
|
103
114
|
moderation: worldAdminService,
|
|
104
115
|
feedback: feedbackService,
|
|
105
116
|
conversationFeedback: conversationFeedbackService,
|
|
117
|
+
agentCards: agentCardService,
|
|
118
|
+
conversationFeedback: conversationFeedbackService,
|
|
106
119
|
orchestration: worldConversationOrchestrator,
|
|
107
120
|
},
|
|
108
121
|
registerRoutes(app) {
|
|
@@ -123,6 +136,7 @@ export function createClaworldProductShell({
|
|
|
123
136
|
});
|
|
124
137
|
registerSocialRoutes(app, { socialService: socialLookupService });
|
|
125
138
|
registerFeedbackRoutes(app, { store, feedbackService });
|
|
139
|
+
registerAgentCardRoutes(app, { store, agentCardService });
|
|
126
140
|
},
|
|
127
141
|
describe() {
|
|
128
142
|
return {
|
|
@@ -141,11 +155,18 @@ export function createClaworldProductShell({
|
|
|
141
155
|
'moderation',
|
|
142
156
|
'feedback',
|
|
143
157
|
'conversationFeedback',
|
|
158
|
+
'agentCards',
|
|
159
|
+
'conversationFeedback',
|
|
160
|
+
'agentCards',
|
|
144
161
|
'orchestration',
|
|
145
162
|
],
|
|
146
163
|
routes: [
|
|
147
164
|
'GET /v1/meta/product-shell',
|
|
165
|
+
'GET /v1/meta/agent-cards',
|
|
148
166
|
'GET /v1/meta/install',
|
|
167
|
+
'POST /v1/agent-cards/render',
|
|
168
|
+
'GET /v1/profile',
|
|
169
|
+
'POST /v1/profile',
|
|
149
170
|
'GET /v1/onboarding/plan',
|
|
150
171
|
'POST /v1/onboarding/activate',
|
|
151
172
|
'GET /v1/profile/public-identity',
|
|
@@ -165,8 +165,8 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
165
165
|
'installer verifies or installs the claworld OpenClaw plugin package',
|
|
166
166
|
'installer writes or refreshes the managed claworld channel config and binds it to the local main agent by default',
|
|
167
167
|
'installer reloads or starts the runtime and verifies the managed channel shape without requiring backend activation',
|
|
168
|
-
'first-run readiness goes through claworld_pair_agent and then
|
|
169
|
-
'
|
|
168
|
+
'first-run readiness goes through claworld_pair_agent and then claworld_profile with action=update_identity',
|
|
169
|
+
'claworld_profile(action=update_identity) performs activation when needed and completes the public displayName#code identity',
|
|
170
170
|
`ongoing lifecycle uses ${CLAWORLD_UPDATE_COMMAND} for tracked package updates plus managed repair, then ${CLAWORLD_DOCTOR_COMMAND} for health confirmation`,
|
|
171
171
|
],
|
|
172
172
|
recommendedWorlds,
|
|
@@ -183,8 +183,8 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
183
183
|
actions: [
|
|
184
184
|
'install the claworld plugin and write the managed config shape',
|
|
185
185
|
'run claworld_pair_agent to confirm readiness after install',
|
|
186
|
-
'if public identity is pending, call
|
|
187
|
-
'
|
|
186
|
+
'if public identity is pending, call claworld_profile with action=update_identity',
|
|
187
|
+
'claworld_profile(action=update_identity) activates the backend binding when needed and persists the returned appToken',
|
|
188
188
|
'collect required world profile fields',
|
|
189
189
|
'validate world membership eligibility',
|
|
190
190
|
'start the first A2A loop or deliver world content',
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
function normalizeBoolean(value, fallback = false) {
|
|
2
|
+
if (typeof value === 'boolean') return value;
|
|
3
|
+
return fallback;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function normalizePositiveInteger(value, fallback = null) {
|
|
7
|
+
const normalized = Number(value);
|
|
8
|
+
if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
|
|
9
|
+
return Math.floor(normalized);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function maybeGenerateShareCard({
|
|
13
|
+
agentCardService,
|
|
14
|
+
agentId,
|
|
15
|
+
identityStatus,
|
|
16
|
+
generateShareCard = false,
|
|
17
|
+
expiresInSeconds = null,
|
|
18
|
+
forceRegenerate = true,
|
|
19
|
+
}) {
|
|
20
|
+
if (!generateShareCard) return undefined;
|
|
21
|
+
if (identityStatus?.ready !== true) {
|
|
22
|
+
return {
|
|
23
|
+
status: 'unavailable',
|
|
24
|
+
reason: 'public_identity_not_ready',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
return await agentCardService.renderCard({
|
|
30
|
+
agentId,
|
|
31
|
+
expiresInSeconds: normalizePositiveInteger(expiresInSeconds, null),
|
|
32
|
+
forceRegenerate: normalizeBoolean(forceRegenerate, true),
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return {
|
|
36
|
+
status: 'unavailable',
|
|
37
|
+
reason: 'generation_failed',
|
|
38
|
+
message: error?.publicMessage || error?.message || 'share card generation failed',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createProfileService({
|
|
44
|
+
publicIdentityService,
|
|
45
|
+
agentCardService,
|
|
46
|
+
} = {}) {
|
|
47
|
+
if (!publicIdentityService) {
|
|
48
|
+
throw new Error('profile_service_requires_public_identity_service');
|
|
49
|
+
}
|
|
50
|
+
if (!agentCardService) {
|
|
51
|
+
throw new Error('profile_service_requires_agent_card_service');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
getPublicIdentityStatus(input = {}) {
|
|
56
|
+
return publicIdentityService.getPublicIdentityStatus(input);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
updatePublicIdentity(input = {}) {
|
|
60
|
+
return publicIdentityService.updatePublicIdentity(input);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
assertPublicIdentityReady(input = {}) {
|
|
64
|
+
return publicIdentityService.assertPublicIdentityReady(input);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async getProfile({
|
|
68
|
+
agentId,
|
|
69
|
+
generateShareCard = false,
|
|
70
|
+
expiresInSeconds = null,
|
|
71
|
+
forceRegenerate = true,
|
|
72
|
+
} = {}) {
|
|
73
|
+
const identityStatus = publicIdentityService.getPublicIdentityStatus({ agentId });
|
|
74
|
+
const shareCard = await maybeGenerateShareCard({
|
|
75
|
+
agentCardService,
|
|
76
|
+
agentId,
|
|
77
|
+
identityStatus,
|
|
78
|
+
generateShareCard,
|
|
79
|
+
expiresInSeconds,
|
|
80
|
+
forceRegenerate,
|
|
81
|
+
});
|
|
82
|
+
return shareCard === undefined
|
|
83
|
+
? identityStatus
|
|
84
|
+
: {
|
|
85
|
+
...identityStatus,
|
|
86
|
+
shareCard,
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
async updateProfileIdentity({
|
|
91
|
+
agentId,
|
|
92
|
+
displayName,
|
|
93
|
+
generateShareCard = true,
|
|
94
|
+
expiresInSeconds = null,
|
|
95
|
+
forceRegenerate = true,
|
|
96
|
+
} = {}) {
|
|
97
|
+
const identityStatus = await publicIdentityService.updatePublicIdentity({
|
|
98
|
+
agentId,
|
|
99
|
+
displayName,
|
|
100
|
+
});
|
|
101
|
+
const shareCard = await maybeGenerateShareCard({
|
|
102
|
+
agentCardService,
|
|
103
|
+
agentId,
|
|
104
|
+
identityStatus,
|
|
105
|
+
generateShareCard,
|
|
106
|
+
expiresInSeconds,
|
|
107
|
+
forceRegenerate,
|
|
108
|
+
});
|
|
109
|
+
return shareCard === undefined
|
|
110
|
+
? identityStatus
|
|
111
|
+
: {
|
|
112
|
+
...identityStatus,
|
|
113
|
+
shareCard,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
async executeProfileAction({
|
|
118
|
+
action = 'view',
|
|
119
|
+
agentId,
|
|
120
|
+
displayName = null,
|
|
121
|
+
generateShareCard = false,
|
|
122
|
+
expiresInSeconds = null,
|
|
123
|
+
forceRegenerate = true,
|
|
124
|
+
} = {}) {
|
|
125
|
+
if (action === 'update_identity') {
|
|
126
|
+
return this.updateProfileIdentity({
|
|
127
|
+
agentId,
|
|
128
|
+
displayName,
|
|
129
|
+
generateShareCard,
|
|
130
|
+
expiresInSeconds,
|
|
131
|
+
forceRegenerate,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return this.getProfile({
|
|
135
|
+
agentId,
|
|
136
|
+
generateShareCard,
|
|
137
|
+
expiresInSeconds,
|
|
138
|
+
forceRegenerate,
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { resolveAuthenticatedAgentId } from '../../lib/http-auth.js';
|
|
2
2
|
|
|
3
|
+
function normalizeBoolean(value, fallback = false) {
|
|
4
|
+
if (typeof value === 'boolean') return value;
|
|
5
|
+
if (typeof value === 'string') {
|
|
6
|
+
const normalized = value.trim().toLowerCase();
|
|
7
|
+
if (normalized === 'true') return true;
|
|
8
|
+
if (normalized === 'false') return false;
|
|
9
|
+
}
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizePositiveInteger(value, fallback = null) {
|
|
14
|
+
const normalized = Number(value);
|
|
15
|
+
if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
|
|
16
|
+
return Math.floor(normalized);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildAbsoluteUrl(req, publicPath) {
|
|
20
|
+
return `${req.protocol}://${req.get('host')}${publicPath}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeShareCard(req, shareCard = undefined) {
|
|
24
|
+
if (shareCard === undefined) return undefined;
|
|
25
|
+
if (!shareCard || typeof shareCard !== 'object') return shareCard;
|
|
26
|
+
|
|
27
|
+
const card = shareCard.card && typeof shareCard.card === 'object' ? shareCard.card : null;
|
|
28
|
+
if (!card) return shareCard;
|
|
29
|
+
|
|
30
|
+
const imageUrl = card.imageUrl || (card.publicPath ? buildAbsoluteUrl(req, card.publicPath) : null);
|
|
31
|
+
const downloadUrl = card.downloadUrl || imageUrl;
|
|
32
|
+
if (!imageUrl && !downloadUrl) return shareCard;
|
|
33
|
+
return {
|
|
34
|
+
...shareCard,
|
|
35
|
+
card: {
|
|
36
|
+
...card,
|
|
37
|
+
...(imageUrl ? { imageUrl } : {}),
|
|
38
|
+
...(downloadUrl ? { downloadUrl } : {}),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
3
43
|
function sendProfileError(res, error) {
|
|
4
44
|
const status = Number.isInteger(error?.status) ? error.status : 500;
|
|
5
45
|
if (error?.responseBody && typeof error.responseBody === 'object') {
|
|
@@ -17,6 +57,66 @@ function sendMissingAgentIdentity(res) {
|
|
|
17
57
|
}
|
|
18
58
|
|
|
19
59
|
export function registerPublicIdentityRoutes(app, { publicIdentityService, store }) {
|
|
60
|
+
app.get('/v1/profile', async (req, res) => {
|
|
61
|
+
const authAgent = resolveAuthenticatedAgentId({
|
|
62
|
+
store,
|
|
63
|
+
req,
|
|
64
|
+
providedAgentId: req.query.agentId,
|
|
65
|
+
fieldName: 'agentId',
|
|
66
|
+
});
|
|
67
|
+
if (!authAgent.ok) return res.status(authAgent.status).json(authAgent.body);
|
|
68
|
+
if (!authAgent.agentId) return sendMissingAgentIdentity(res);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const result = await publicIdentityService.getProfile({
|
|
72
|
+
agentId: authAgent.agentId,
|
|
73
|
+
generateShareCard: normalizeBoolean(req.query.generateShareCard, false),
|
|
74
|
+
expiresInSeconds: normalizePositiveInteger(req.query.expiresInSeconds, null),
|
|
75
|
+
forceRegenerate: normalizeBoolean(req.query.forceRegenerate, true),
|
|
76
|
+
});
|
|
77
|
+
return res.json({
|
|
78
|
+
...result,
|
|
79
|
+
...(Object.prototype.hasOwnProperty.call(result, 'shareCard')
|
|
80
|
+
? { shareCard: normalizeShareCard(req, result.shareCard) }
|
|
81
|
+
: {}),
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return sendProfileError(res, error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
app.post('/v1/profile', async (req, res) => {
|
|
89
|
+
const authAgent = resolveAuthenticatedAgentId({
|
|
90
|
+
store,
|
|
91
|
+
req,
|
|
92
|
+
providedAgentId: req.body?.agentId,
|
|
93
|
+
fieldName: 'agentId',
|
|
94
|
+
});
|
|
95
|
+
if (!authAgent.ok) return res.status(authAgent.status).json(authAgent.body);
|
|
96
|
+
if (!authAgent.agentId) return sendMissingAgentIdentity(res);
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const action = String(req.body?.action || '').trim() || (req.body?.displayName ? 'update_identity' : 'view');
|
|
100
|
+
const result = await publicIdentityService.executeProfileAction({
|
|
101
|
+
action,
|
|
102
|
+
agentId: authAgent.agentId,
|
|
103
|
+
displayName: req.body?.displayName,
|
|
104
|
+
generateShareCard: normalizeBoolean(req.body?.generateShareCard, action === 'update_identity'),
|
|
105
|
+
expiresInSeconds: normalizePositiveInteger(req.body?.expiresInSeconds, null),
|
|
106
|
+
forceRegenerate: normalizeBoolean(req.body?.forceRegenerate, true),
|
|
107
|
+
});
|
|
108
|
+
return res.json({
|
|
109
|
+
action,
|
|
110
|
+
...result,
|
|
111
|
+
...(Object.prototype.hasOwnProperty.call(result, 'shareCard')
|
|
112
|
+
? { shareCard: normalizeShareCard(req, result.shareCard) }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return sendProfileError(res, error);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
20
120
|
app.get('/v1/profile/public-identity', (req, res) => {
|
|
21
121
|
const authAgent = resolveAuthenticatedAgentId({
|
|
22
122
|
store,
|
|
@@ -49,9 +49,11 @@ function createInvalidPublicIdentityRequest(fieldId, message, code = 'invalid_pu
|
|
|
49
49
|
return error;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
const DEFAULT_PUBLIC_IDENTITY_NEXT_TOOL = 'claworld_profile';
|
|
53
|
+
|
|
52
54
|
function createPublicIdentityIncompleteError(agent, {
|
|
53
55
|
capability = null,
|
|
54
|
-
nextTool =
|
|
56
|
+
nextTool = DEFAULT_PUBLIC_IDENTITY_NEXT_TOOL,
|
|
55
57
|
} = {}) {
|
|
56
58
|
const projected = projectPublicIdentityStatus(agent, { nextTool });
|
|
57
59
|
const capabilityLabel = normalizeText(capability, 'this Claworld capability');
|
|
@@ -73,7 +75,7 @@ function createPublicIdentityIncompleteError(agent, {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
function projectPublicIdentityStatus(agent, {
|
|
76
|
-
nextTool =
|
|
78
|
+
nextTool = DEFAULT_PUBLIC_IDENTITY_NEXT_TOOL,
|
|
77
79
|
conversationFeedbackService = null,
|
|
78
80
|
} = {}) {
|
|
79
81
|
const publicIdentity = resolvePublicIdentity(agent);
|