@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.
- package/README.md +45 -19
- package/index.js +0 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -5
- package/skills/claworld-help/SKILL.md +84 -91
- package/skills/claworld-join-and-chat/SKILL.md +9 -9
- package/src/openclaw/index.js +0 -3
- package/src/openclaw/plugin/account-identity.js +0 -1
- package/src/openclaw/plugin/claworld-channel-plugin.js +8 -253
- package/src/openclaw/plugin/managed-config.js +1 -7
- package/src/openclaw/plugin/onboarding.js +128 -103
- package/src/openclaw/plugin/register.js +183 -232
- package/src/openclaw/plugin/relay-client.js +8 -5
- package/src/openclaw/runtime/product-shell-helper.js +11 -364
- package/src/openclaw/runtime/tool-contracts.js +0 -182
- package/src/openclaw/runtime/tool-inventory.js +4 -27
- package/bin/claworld.mjs +0 -9
- package/src/lib/agent-profile.js +0 -74
- package/src/lib/http-auth.js +0 -151
- package/src/lib/policy.js +0 -114
- package/src/openclaw/installer/cli.js +0 -406
- package/src/openclaw/installer/constants.js +0 -14
- package/src/openclaw/installer/core.js +0 -2122
- package/src/openclaw/installer/doctor.js +0 -876
- package/src/openclaw/installer/workspace-contract.js +0 -427
- package/src/product-shell/agent-cards/card-routes.js +0 -64
- package/src/product-shell/agent-cards/card-service.js +0 -287
- package/src/product-shell/agent-cards/spec-builder.js +0 -167
- package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
- package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
- package/src/product-shell/agent-cards/svg-renderer.js +0 -325
- package/src/product-shell/agent-cards/template-registry.js +0 -131
- package/src/product-shell/catalog/default-world-catalog.js +0 -38
- package/src/product-shell/contracts/candidate-feed.js +0 -393
- package/src/product-shell/contracts/world-manifest.js +0 -369
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
- package/src/product-shell/feedback/feedback-contract.js +0 -13
- package/src/product-shell/feedback/feedback-routes.js +0 -98
- package/src/product-shell/feedback/feedback-service.js +0 -252
- package/src/product-shell/index.js +0 -212
- package/src/product-shell/matching/matchmaking-service.js +0 -395
- package/src/product-shell/membership/membership-service.js +0 -284
- package/src/product-shell/onboarding/onboarding-routes.js +0 -37
- package/src/product-shell/onboarding/onboarding-service.js +0 -220
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
- package/src/product-shell/profile/profile-service.js +0 -142
- package/src/product-shell/profile/public-identity-routes.js +0 -160
- package/src/product-shell/profile/public-identity-service.js +0 -192
- package/src/product-shell/search/search-service.js +0 -393
- package/src/product-shell/social/chat-request-approval-policy.js +0 -332
- package/src/product-shell/social/chat-request-routes.js +0 -130
- package/src/product-shell/social/chat-request-service.js +0 -723
- package/src/product-shell/social/friend-routes.js +0 -82
- package/src/product-shell/social/friend-service.js +0 -557
- package/src/product-shell/social/social-routes.js +0 -21
- package/src/product-shell/social/social-service.js +0 -136
- package/src/product-shell/worlds/world-admin-service.js +0 -486
- package/src/product-shell/worlds/world-authorization.js +0 -136
- package/src/product-shell/worlds/world-broadcast-service.js +0 -296
- package/src/product-shell/worlds/world-routes.js +0 -403
- package/src/product-shell/worlds/world-service.js +0 -89
- package/src/product-shell/worlds/world-text.js +0 -75
|
@@ -3,16 +3,10 @@ export const CLAWORLD_TOOL_CONTRACT_VERSION = 'v1';
|
|
|
3
3
|
export const CLAWORLD_CHAT_REQUEST_TOOL_NAMES = Object.freeze([
|
|
4
4
|
'claworld_request_chat',
|
|
5
5
|
'claworld_chat_inbox',
|
|
6
|
-
'claworld_accept_chat_request',
|
|
7
|
-
'claworld_reject_chat_request',
|
|
8
6
|
]);
|
|
9
7
|
|
|
10
|
-
export const
|
|
11
|
-
'
|
|
12
|
-
]);
|
|
13
|
-
|
|
14
|
-
export const CLAWORLD_PROFILE_TOOL_NAMES = Object.freeze([
|
|
15
|
-
'claworld_profile',
|
|
8
|
+
export const CLAWORLD_ACCOUNT_TOOL_NAMES = Object.freeze([
|
|
9
|
+
'claworld_account',
|
|
16
10
|
]);
|
|
17
11
|
|
|
18
12
|
export const CLAWORLD_FEEDBACK_TOOL_NAMES = Object.freeze([
|
|
@@ -30,24 +24,8 @@ export const CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES = Object.freeze([
|
|
|
30
24
|
'claworld_manage_world',
|
|
31
25
|
]);
|
|
32
26
|
|
|
33
|
-
export const CLAWORLD_COMPATIBILITY_TOOL_NAMES = Object.freeze([
|
|
34
|
-
'claworld_list_owned_worlds',
|
|
35
|
-
'claworld_prepare_world_join',
|
|
36
|
-
'claworld_search_world',
|
|
37
|
-
]);
|
|
38
|
-
|
|
39
|
-
export const CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES = Object.freeze([
|
|
40
|
-
'claworld_send_friend_request',
|
|
41
|
-
'claworld_list_friend_requests',
|
|
42
|
-
'claworld_accept_friend_request',
|
|
43
|
-
'claworld_reject_friend_request',
|
|
44
|
-
'claworld_broadcast_world',
|
|
45
|
-
'claworld_resolve_agent',
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
27
|
export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
|
|
49
|
-
...
|
|
50
|
-
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
28
|
+
...CLAWORLD_ACCOUNT_TOOL_NAMES,
|
|
51
29
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
52
30
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
53
31
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|
|
@@ -71,8 +49,7 @@ export const CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES = Object.freeze([
|
|
|
71
49
|
]);
|
|
72
50
|
|
|
73
51
|
export const CLAWORLD_PLUGIN_SMOKE_REQUIRED_TOOL_NAMES = Object.freeze([
|
|
74
|
-
...
|
|
75
|
-
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
52
|
+
...CLAWORLD_ACCOUNT_TOOL_NAMES,
|
|
76
53
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
77
54
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
78
55
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|
package/bin/claworld.mjs
DELETED
package/src/lib/agent-profile.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { resolvePublicIdentity } from './public-identity.js';
|
|
2
|
-
|
|
3
|
-
function normalizeOptionalString(value, { maxLength = 280 } = {}) {
|
|
4
|
-
if (value == null) return null;
|
|
5
|
-
const normalized = String(value).trim();
|
|
6
|
-
if (!normalized) return null;
|
|
7
|
-
return normalized.slice(0, maxLength);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function normalizeTagList(rawTags, { maxItems = 8, maxLength = 24 } = {}) {
|
|
11
|
-
if (!Array.isArray(rawTags)) return [];
|
|
12
|
-
const seen = new Set();
|
|
13
|
-
const tags = [];
|
|
14
|
-
for (const value of rawTags) {
|
|
15
|
-
const normalized = normalizeOptionalString(value, { maxLength });
|
|
16
|
-
if (!normalized) continue;
|
|
17
|
-
const key = normalized.toLowerCase();
|
|
18
|
-
if (seen.has(key)) continue;
|
|
19
|
-
seen.add(key);
|
|
20
|
-
tags.push(normalized);
|
|
21
|
-
if (tags.length >= maxItems) break;
|
|
22
|
-
}
|
|
23
|
-
return tags;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function normalizeBooleanFlag(value, fallback = true) {
|
|
27
|
-
if (typeof value === 'boolean') return value;
|
|
28
|
-
if (typeof value === 'number') {
|
|
29
|
-
if (value === 1) return true;
|
|
30
|
-
if (value === 0) return false;
|
|
31
|
-
}
|
|
32
|
-
if (typeof value === 'string') {
|
|
33
|
-
const normalized = value.trim().toLowerCase();
|
|
34
|
-
if (normalized === 'true' || normalized === '1') return true;
|
|
35
|
-
if (normalized === 'false' || normalized === '0') return false;
|
|
36
|
-
}
|
|
37
|
-
return fallback;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function resolveAgentDisplayName(agent = {}) {
|
|
41
|
-
const publicIdentityDisplayName = normalizeOptionalString(resolvePublicIdentity(agent).displayName, { maxLength: 80 });
|
|
42
|
-
if (publicIdentityDisplayName) return publicIdentityDisplayName;
|
|
43
|
-
const displayName = normalizeOptionalString(agent.displayName, { maxLength: 80 });
|
|
44
|
-
if (displayName) return displayName;
|
|
45
|
-
return normalizeOptionalString(agent.agentId, { maxLength: 80 })
|
|
46
|
-
|| 'agent';
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function normalizeAgentProfile(profile) {
|
|
50
|
-
const source = profile && typeof profile === 'object' ? profile : {};
|
|
51
|
-
return {
|
|
52
|
-
headline: normalizeOptionalString(source.headline, { maxLength: 120 }),
|
|
53
|
-
bio: normalizeOptionalString(source.bio, { maxLength: 300 }),
|
|
54
|
-
avatarUrl: normalizeOptionalString(source.avatarUrl, { maxLength: 512 }),
|
|
55
|
-
tags: normalizeTagList(source.tags),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function resolveAgentVisibility(agent = {}) {
|
|
60
|
-
const discoverable = normalizeBooleanFlag(agent.discoverable, true);
|
|
61
|
-
const requestedContactable = normalizeBooleanFlag(agent.contactable, true);
|
|
62
|
-
return {
|
|
63
|
-
discoverable,
|
|
64
|
-
contactable: discoverable ? requestedContactable : false,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function normalizeAgentInputMetadata({ displayName, profile, discoverable, contactable } = {}) {
|
|
69
|
-
return {
|
|
70
|
-
displayName: resolveAgentDisplayName({ displayName }),
|
|
71
|
-
profile: normalizeAgentProfile(profile),
|
|
72
|
-
...resolveAgentVisibility({ discoverable, contactable }),
|
|
73
|
-
};
|
|
74
|
-
}
|
package/src/lib/http-auth.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
function unauthorized(reason) {
|
|
2
|
-
return {
|
|
3
|
-
error: 'not_authenticated',
|
|
4
|
-
reason,
|
|
5
|
-
};
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function forbidden(reason, extra = {}) {
|
|
9
|
-
return {
|
|
10
|
-
error: 'forbidden',
|
|
11
|
-
reason,
|
|
12
|
-
...extra,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function isExpired(isoTs, nowMs = Date.now()) {
|
|
17
|
-
if (!isoTs) return false;
|
|
18
|
-
const expiresAtMs = Date.parse(isoTs);
|
|
19
|
-
if (Number.isNaN(expiresAtMs)) return false;
|
|
20
|
-
return expiresAtMs <= nowMs;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function normalizeHeaderValue(value) {
|
|
24
|
-
if (Array.isArray(value)) {
|
|
25
|
-
const [first] = value;
|
|
26
|
-
return typeof first === 'string' ? first.trim() : '';
|
|
27
|
-
}
|
|
28
|
-
if (typeof value === 'string') return value.trim();
|
|
29
|
-
return '';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function readAppTokenFromRequest(req) {
|
|
33
|
-
const authorization = normalizeHeaderValue(req?.headers?.authorization);
|
|
34
|
-
if (/^bearer\s+/i.test(authorization)) {
|
|
35
|
-
const token = authorization.replace(/^bearer\s+/i, '').trim();
|
|
36
|
-
if (token) return { token, source: 'authorization' };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const appTokenHeader = normalizeHeaderValue(req?.headers?.['x-claworld-app-token']);
|
|
40
|
-
if (appTokenHeader) return { token: appTokenHeader, source: 'x-claworld-app-token' };
|
|
41
|
-
|
|
42
|
-
const legacyRelayToken = normalizeHeaderValue(req?.headers?.['x-relay-token']);
|
|
43
|
-
if (legacyRelayToken) return { token: legacyRelayToken, source: 'x-relay-token' };
|
|
44
|
-
|
|
45
|
-
return { token: null, source: null };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function authenticateAppTokenRequest({ store, req }) {
|
|
49
|
-
const { token, source } = readAppTokenFromRequest(req);
|
|
50
|
-
if (!token) {
|
|
51
|
-
return {
|
|
52
|
-
present: false,
|
|
53
|
-
ok: false,
|
|
54
|
-
source: null,
|
|
55
|
-
token: null,
|
|
56
|
-
agent: null,
|
|
57
|
-
credential: null,
|
|
58
|
-
error: null,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const credential = store.getCredentialByToken(token);
|
|
63
|
-
if (!credential) {
|
|
64
|
-
return {
|
|
65
|
-
present: true,
|
|
66
|
-
ok: false,
|
|
67
|
-
source,
|
|
68
|
-
token,
|
|
69
|
-
agent: null,
|
|
70
|
-
credential: null,
|
|
71
|
-
error: unauthorized('credential_invalid'),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
if (credential.status !== 'active' || credential.revokedAt) {
|
|
75
|
-
return {
|
|
76
|
-
present: true,
|
|
77
|
-
ok: false,
|
|
78
|
-
source,
|
|
79
|
-
token,
|
|
80
|
-
agent: null,
|
|
81
|
-
credential,
|
|
82
|
-
error: unauthorized('credential_revoked'),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
if (isExpired(credential.expiresAt)) {
|
|
86
|
-
return {
|
|
87
|
-
present: true,
|
|
88
|
-
ok: false,
|
|
89
|
-
source,
|
|
90
|
-
token,
|
|
91
|
-
agent: null,
|
|
92
|
-
credential,
|
|
93
|
-
error: unauthorized('credential_expired'),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const agent = store.getAgent(credential.agentId);
|
|
98
|
-
if (!agent) {
|
|
99
|
-
return {
|
|
100
|
-
present: true,
|
|
101
|
-
ok: false,
|
|
102
|
-
source,
|
|
103
|
-
token,
|
|
104
|
-
agent: null,
|
|
105
|
-
credential,
|
|
106
|
-
error: unauthorized('credential_invalid'),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
present: true,
|
|
112
|
-
ok: true,
|
|
113
|
-
source,
|
|
114
|
-
token,
|
|
115
|
-
agent,
|
|
116
|
-
credential,
|
|
117
|
-
error: null,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function resolveAuthenticatedAgentId({ store, req, providedAgentId = null, fieldName = 'agentId' } = {}) {
|
|
122
|
-
const auth = authenticateAppTokenRequest({ store, req });
|
|
123
|
-
if (auth.present && !auth.ok) {
|
|
124
|
-
return {
|
|
125
|
-
ok: false,
|
|
126
|
-
status: 401,
|
|
127
|
-
body: auth.error,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const explicitAgentId = String(providedAgentId || '').trim() || null;
|
|
132
|
-
const authenticatedAgentId = auth.ok ? auth.agent.agentId : null;
|
|
133
|
-
|
|
134
|
-
if (explicitAgentId && authenticatedAgentId && explicitAgentId !== authenticatedAgentId) {
|
|
135
|
-
return {
|
|
136
|
-
ok: false,
|
|
137
|
-
status: 403,
|
|
138
|
-
body: forbidden('agent_identity_mismatch', {
|
|
139
|
-
field: fieldName,
|
|
140
|
-
authenticatedAgentId,
|
|
141
|
-
providedAgentId: explicitAgentId,
|
|
142
|
-
}),
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
ok: true,
|
|
148
|
-
auth,
|
|
149
|
-
agentId: explicitAgentId || authenticatedAgentId || null,
|
|
150
|
-
};
|
|
151
|
-
}
|
package/src/lib/policy.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { resolveAgentVisibility } from './agent-profile.js';
|
|
2
|
-
const ALLOW_DECISION = Object.freeze({ allowed: true });
|
|
3
|
-
const EMPTY_SET = Object.freeze(new Set());
|
|
4
|
-
|
|
5
|
-
function parsePolicyCsvSet(rawValue) {
|
|
6
|
-
if (typeof rawValue !== 'string') return EMPTY_SET;
|
|
7
|
-
const values = rawValue
|
|
8
|
-
.split(',')
|
|
9
|
-
.map((value) => value.trim().toLowerCase())
|
|
10
|
-
.filter(Boolean);
|
|
11
|
-
return values.length > 0 ? new Set(values) : EMPTY_SET;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function buildRequestDenyPolicyFromEnv(env) {
|
|
15
|
-
return Object.freeze({
|
|
16
|
-
blockedAgentIds: parsePolicyCsvSet(env.RELAY_POLICY_BLOCKED_AGENT_IDS),
|
|
17
|
-
deniedAgentIds: parsePolicyCsvSet(env.RELAY_POLICY_DENIED_AGENT_IDS),
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function agentInPolicySet(values, candidate) {
|
|
22
|
-
const normalized = String(candidate || '').trim().toLowerCase();
|
|
23
|
-
return normalized ? values.has(normalized) : false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const requestDenyPolicy = buildRequestDenyPolicyFromEnv(process.env);
|
|
27
|
-
|
|
28
|
-
function deny(status, error, extras = {}) {
|
|
29
|
-
return { allowed: false, status, error, ...extras };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function defaultCanRequest({ fromAgentId, fromAgent, toAgent }) {
|
|
33
|
-
const visibility = resolveAgentVisibility(toAgent);
|
|
34
|
-
if (agentInPolicySet(requestDenyPolicy.blockedAgentIds, fromAgentId)) {
|
|
35
|
-
return deny(403, 'request_blocked_by_policy');
|
|
36
|
-
}
|
|
37
|
-
if (agentInPolicySet(requestDenyPolicy.deniedAgentIds, fromAgentId)) {
|
|
38
|
-
return deny(403, 'request_denied_by_policy');
|
|
39
|
-
}
|
|
40
|
-
if (toAgent.agentId === fromAgentId) return deny(400, 'self_request_not_allowed');
|
|
41
|
-
if (!visibility.discoverable) return deny(403, 'target_not_discoverable');
|
|
42
|
-
if (!visibility.contactable) return deny(403, 'target_not_contactable');
|
|
43
|
-
return ALLOW_DECISION;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function defaultCanAccept() {
|
|
47
|
-
return ALLOW_DECISION;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function defaultCanStartSession() {
|
|
51
|
-
return ALLOW_DECISION;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function defaultCanDeliverTurn({ session, roundBudget, fromAgentId }) {
|
|
55
|
-
if (session.state !== 'active') {
|
|
56
|
-
const terminationReason = typeof session?.terminationReason === 'string' && session.terminationReason.trim()
|
|
57
|
-
? session.terminationReason.trim()
|
|
58
|
-
: null;
|
|
59
|
-
return deny(terminationReason === 'session_timeout' ? 409 : 400, terminationReason || 'session_not_active');
|
|
60
|
-
}
|
|
61
|
-
if (roundBudget?.hasExplicitBudget && Number(roundBudget.remainingTurns) <= 0) {
|
|
62
|
-
return deny(400, 'max_turns_reached');
|
|
63
|
-
}
|
|
64
|
-
if (fromAgentId !== session.currentSpeakerAgentId) return deny(400, 'not_current_speaker');
|
|
65
|
-
return ALLOW_DECISION;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function createRelayPolicyHooks(policy) {
|
|
69
|
-
const hooks = policy && typeof policy === 'object' ? policy : {};
|
|
70
|
-
return {
|
|
71
|
-
canRequest: typeof hooks.canRequest === 'function' ? hooks.canRequest : defaultCanRequest,
|
|
72
|
-
canAccept: typeof hooks.canAccept === 'function' ? hooks.canAccept : defaultCanAccept,
|
|
73
|
-
canStartSession: typeof hooks.canStartSession === 'function' ? hooks.canStartSession : defaultCanStartSession,
|
|
74
|
-
canDeliverTurn: typeof hooks.canDeliverTurn === 'function' ? hooks.canDeliverTurn : defaultCanDeliverTurn,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function resolvePolicyDecision(decision, { defaultStatus = 403, defaultError = 'forbidden' } = {}) {
|
|
79
|
-
if (decision == null || decision === true) return { allowed: true };
|
|
80
|
-
if (decision === false) return { allowed: false, status: defaultStatus, body: { error: defaultError } };
|
|
81
|
-
if (typeof decision !== 'object') return { allowed: true };
|
|
82
|
-
|
|
83
|
-
const hasDenyShape = decision.allowed === false
|
|
84
|
-
|| (decision.allowed === undefined && (decision.status !== undefined || decision.error !== undefined || decision.body !== undefined));
|
|
85
|
-
|
|
86
|
-
if (!hasDenyShape) return { allowed: true };
|
|
87
|
-
|
|
88
|
-
const status = Number.isInteger(decision.status) ? decision.status : defaultStatus;
|
|
89
|
-
if (decision.body && typeof decision.body === 'object') return { allowed: false, status, body: decision.body };
|
|
90
|
-
const error = typeof decision.error === 'string' && decision.error.trim() ? decision.error : defaultError;
|
|
91
|
-
return { allowed: false, status, body: { error } };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function evaluatePolicyHook({
|
|
95
|
-
hook,
|
|
96
|
-
context,
|
|
97
|
-
hookName,
|
|
98
|
-
defaultStatus = 403,
|
|
99
|
-
defaultError = 'forbidden',
|
|
100
|
-
}) {
|
|
101
|
-
try {
|
|
102
|
-
return resolvePolicyDecision(hook(context), { defaultStatus, defaultError });
|
|
103
|
-
} catch (error) {
|
|
104
|
-
return {
|
|
105
|
-
allowed: false,
|
|
106
|
-
status: 500,
|
|
107
|
-
body: {
|
|
108
|
-
error: 'policy_hook_error',
|
|
109
|
-
hook: hookName,
|
|
110
|
-
message: error instanceof Error ? error.message : String(error),
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
}
|