@prmichaelsen/remember-mcp 3.19.3 → 4.0.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/AGENT.md +10 -3
- package/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/agent/commands/acp.artifact-glossary.md +530 -0
- package/agent/commands/acp.artifact-reference.md +591 -0
- package/agent/commands/acp.artifact-research.md +594 -0
- package/agent/commands/acp.audit.md +345 -0
- package/agent/commands/acp.clarification-address.md +185 -88
- package/agent/commands/acp.clarification-capture.md +44 -44
- package/agent/commands/acp.clarification-create.md +41 -42
- package/agent/commands/acp.command-create.md +49 -49
- package/agent/commands/acp.design-create.md +53 -35
- package/agent/commands/acp.design-reference.md +42 -42
- package/agent/commands/acp.handoff.md +35 -35
- package/agent/commands/acp.index.md +47 -47
- package/agent/commands/acp.init.md +105 -69
- package/agent/commands/acp.package-create.md +41 -41
- package/agent/commands/acp.package-info.md +40 -40
- package/agent/commands/acp.package-install.md +48 -48
- package/agent/commands/acp.package-list.md +40 -40
- package/agent/commands/acp.package-publish.md +62 -62
- package/agent/commands/acp.package-remove.md +41 -41
- package/agent/commands/acp.package-search.md +48 -48
- package/agent/commands/acp.package-update.md +50 -50
- package/agent/commands/acp.package-validate.md +52 -52
- package/agent/commands/acp.pattern-create.md +61 -43
- package/agent/commands/acp.plan.md +70 -47
- package/agent/commands/acp.proceed.md +188 -66
- package/agent/commands/acp.project-create.md +42 -42
- package/agent/commands/acp.project-info.md +46 -46
- package/agent/commands/acp.project-list.md +41 -41
- package/agent/commands/acp.project-remove.md +36 -36
- package/agent/commands/acp.project-set.md +33 -33
- package/agent/commands/acp.project-update.md +57 -57
- package/agent/commands/acp.projects-restore.md +37 -37
- package/agent/commands/acp.projects-sync.md +39 -39
- package/agent/commands/acp.report.md +50 -50
- package/agent/commands/acp.resume.md +36 -36
- package/agent/commands/acp.sessions.md +46 -46
- package/agent/commands/acp.status.md +43 -43
- package/agent/commands/acp.sync.md +109 -56
- package/agent/commands/acp.task-create.md +51 -49
- package/agent/commands/acp.update.md +66 -45
- package/agent/commands/acp.validate.md +110 -52
- package/agent/commands/acp.version-check-for-updates.md +40 -40
- package/agent/commands/acp.version-check.md +36 -36
- package/agent/commands/acp.version-update.md +43 -43
- package/agent/commands/command.template.md +40 -40
- package/agent/commands/git.commit.md +28 -28
- package/agent/commands/git.init.md +48 -48
- package/agent/design/design.template.md +9 -9
- package/agent/design/local.admin-debugging-tools.md +242 -0
- package/agent/design/requirements.template.md +8 -8
- package/agent/index/.gitkeep +0 -0
- package/agent/index/acp.core.yaml +137 -0
- package/agent/index/local.main.template.yaml +37 -0
- package/agent/index/local.main.yaml +48 -0
- package/agent/manifest.yaml +64 -0
- package/agent/milestones/milestone-1-{title}.template.md +8 -8
- package/agent/milestones/milestone-22-admin-debugging-tools.md +61 -0
- package/agent/milestones/milestone-23-trust-level-protection.md +122 -0
- package/agent/patterns/pattern.template.md +22 -22
- package/agent/progress.template.yaml +13 -3
- package/agent/progress.yaml +173 -3
- package/agent/schemas/package.schema.yaml +276 -0
- package/agent/scripts/acp.project-update.sh +5 -6
- package/agent/tasks/milestone-22-admin-debugging-tools/task-520-admin-gate-infrastructure.md +99 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-521-schema-and-collection-tools.md +108 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-522-memory-inspection-tools.md +120 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-523-user-inspection-tools.md +126 -0
- package/agent/tasks/milestone-22-admin-debugging-tools/task-524-health-and-drift-tools.md +120 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-525-remove-trust-from-create-update.md +69 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-526-add-request-set-trust-level-tool.md +108 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-527-update-confirm-deny-secret-token.md +60 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-528-update-trust-scale-references.md +73 -0
- package/agent/tasks/milestone-23-trust-level-protection/task-529-version-bump-and-release.md +87 -0
- package/agent/tasks/task-1-{title}.template.md +18 -18
- package/dist/server-factory.js +779 -87
- package/dist/server.js +141 -41
- package/dist/services/trust-validator.d.ts +16 -14
- package/dist/tools/admin-collection-stats.d.ts +24 -0
- package/dist/tools/admin-detect-weaviate-drift.d.ts +26 -0
- package/dist/tools/admin-get-weaviate-schema.d.ts +24 -0
- package/dist/tools/admin-health-drift.spec.d.ts +5 -0
- package/dist/tools/admin-health.d.ts +15 -0
- package/dist/tools/admin-inspect-memory.d.ts +29 -0
- package/dist/tools/admin-inspect-user.d.ts +73 -0
- package/dist/tools/admin-inspect-user.spec.d.ts +5 -0
- package/dist/tools/admin-list-collections.d.ts +23 -0
- package/dist/tools/admin-memory-inspection.spec.d.ts +7 -0
- package/dist/tools/admin-schema-collection.spec.d.ts +8 -0
- package/dist/tools/admin-search-across-users.d.ts +42 -0
- package/dist/tools/confirm.d.ts +1 -0
- package/dist/tools/confirm.spec.d.ts +5 -0
- package/dist/tools/create-internal-memory.d.ts +0 -7
- package/dist/tools/create-memory.d.ts +0 -7
- package/dist/tools/deny.d.ts +1 -0
- package/dist/tools/deny.spec.d.ts +5 -0
- package/dist/tools/query-memory.d.ts +2 -0
- package/dist/tools/request-set-trust-level.d.ts +32 -0
- package/dist/tools/request-set-trust-level.spec.d.ts +2 -0
- package/dist/tools/search-memory.d.ts +2 -0
- package/dist/tools/update-internal-memory.d.ts +0 -6
- package/dist/tools/update-memory.d.ts +0 -7
- package/dist/utils/admin.d.ts +21 -0
- package/dist/utils/admin.spec.d.ts +2 -0
- package/package.json +2 -2
- package/src/server-factory.ts +137 -42
- package/src/server.ts +6 -0
- package/src/services/trust-validator.spec.ts +57 -51
- package/src/services/trust-validator.ts +28 -26
- package/src/tools/admin-collection-stats.ts +67 -0
- package/src/tools/admin-detect-weaviate-drift.ts +110 -0
- package/src/tools/admin-get-weaviate-schema.ts +68 -0
- package/src/tools/admin-health-drift.spec.ts +193 -0
- package/src/tools/admin-health.ts +88 -0
- package/src/tools/admin-inspect-memory.ts +86 -0
- package/src/tools/admin-inspect-user.spec.ts +130 -0
- package/src/tools/admin-inspect-user.ts +148 -0
- package/src/tools/admin-list-collections.ts +73 -0
- package/src/tools/admin-memory-inspection.spec.ts +206 -0
- package/src/tools/admin-schema-collection.spec.ts +167 -0
- package/src/tools/admin-search-across-users.ts +104 -0
- package/src/tools/confirm.spec.ts +108 -0
- package/src/tools/confirm.ts +24 -1
- package/src/tools/create-internal-memory.ts +0 -3
- package/src/tools/create-memory.spec.ts +6 -2
- package/src/tools/create-memory.ts +1 -9
- package/src/tools/deny.spec.ts +59 -0
- package/src/tools/deny.ts +6 -1
- package/src/tools/ghost-config.ts +19 -19
- package/src/tools/query-memory.ts +4 -2
- package/src/tools/request-set-trust-level.spec.ts +87 -0
- package/src/tools/request-set-trust-level.ts +107 -0
- package/src/tools/search-memory.ts +4 -2
- package/src/tools/update-internal-memory.ts +0 -3
- package/src/tools/update-memory.ts +0 -8
- package/src/types/memory.ts +1 -1
- package/src/utils/admin.spec.ts +70 -0
- package/src/utils/admin.ts +27 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { requestSetTrustLevelTool, handleRequestSetTrustLevel } from './request-set-trust-level.js';
|
|
2
|
+
|
|
3
|
+
// Mock core services
|
|
4
|
+
jest.mock('../core-services.js', () => ({
|
|
5
|
+
createCoreServices: jest.fn(() => ({
|
|
6
|
+
memory: {
|
|
7
|
+
requestSetTrustLevel: jest.fn().mockResolvedValue({
|
|
8
|
+
token: 'test-token-123',
|
|
9
|
+
memory_id: 'mem-456',
|
|
10
|
+
current_trust_level: 5,
|
|
11
|
+
requested_trust_level: 2,
|
|
12
|
+
expires_at: '2026-03-20T10:05:00Z',
|
|
13
|
+
}),
|
|
14
|
+
},
|
|
15
|
+
})),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock('../utils/debug.js', () => ({
|
|
19
|
+
createDebugLogger: () => ({
|
|
20
|
+
info: jest.fn(),
|
|
21
|
+
trace: jest.fn(),
|
|
22
|
+
error: jest.fn(),
|
|
23
|
+
debug: jest.fn(),
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
describe('remember_request_set_trust_level', () => {
|
|
28
|
+
describe('tool definition', () => {
|
|
29
|
+
it('should have correct name', () => {
|
|
30
|
+
expect(requestSetTrustLevelTool.name).toBe('remember_request_set_trust_level');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should require memory_id and trust_level', () => {
|
|
34
|
+
expect(requestSetTrustLevelTool.inputSchema.required).toEqual(['memory_id', 'trust_level']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should constrain trust_level to 1-5 integer', () => {
|
|
38
|
+
const trustProp = requestSetTrustLevelTool.inputSchema.properties.trust_level;
|
|
39
|
+
expect(trustProp.type).toBe('integer');
|
|
40
|
+
expect(trustProp.minimum).toBe(1);
|
|
41
|
+
expect(trustProp.maximum).toBe(5);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('handleRequestSetTrustLevel', () => {
|
|
46
|
+
it('should return token on valid request', async () => {
|
|
47
|
+
const result = await handleRequestSetTrustLevel(
|
|
48
|
+
{ memory_id: 'mem-456', trust_level: 2 },
|
|
49
|
+
'user-123'
|
|
50
|
+
);
|
|
51
|
+
const parsed = JSON.parse(result!);
|
|
52
|
+
expect(parsed.token).toBe('test-token-123');
|
|
53
|
+
expect(parsed.memory_id).toBe('mem-456');
|
|
54
|
+
expect(parsed.current_trust_level).toBe(5);
|
|
55
|
+
expect(parsed.requested_trust_level).toBe(2);
|
|
56
|
+
expect(parsed.current_trust_name).toBe('SECRET');
|
|
57
|
+
expect(parsed.requested_trust_name).toBe('INTERNAL');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should reject non-integer trust level', async () => {
|
|
61
|
+
const result = await handleRequestSetTrustLevel(
|
|
62
|
+
{ memory_id: 'mem-456', trust_level: 1.5 },
|
|
63
|
+
'user-123'
|
|
64
|
+
);
|
|
65
|
+
const parsed = JSON.parse(result!);
|
|
66
|
+
expect(parsed.error).toBe('Invalid trust level');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should reject trust level 0', async () => {
|
|
70
|
+
const result = await handleRequestSetTrustLevel(
|
|
71
|
+
{ memory_id: 'mem-456', trust_level: 0 },
|
|
72
|
+
'user-123'
|
|
73
|
+
);
|
|
74
|
+
const parsed = JSON.parse(result!);
|
|
75
|
+
expect(parsed.error).toBe('Invalid trust level');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should reject trust level 6', async () => {
|
|
79
|
+
const result = await handleRequestSetTrustLevel(
|
|
80
|
+
{ memory_id: 'mem-456', trust_level: 6 },
|
|
81
|
+
'user-123'
|
|
82
|
+
);
|
|
83
|
+
const parsed = JSON.parse(result!);
|
|
84
|
+
expect(parsed.error).toBe('Invalid trust level');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remember_request_set_trust_level tool
|
|
3
|
+
* Requests a trust level change for a memory via confirmation flow.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { handleToolError } from '../utils/error-handler.js';
|
|
7
|
+
import { createDebugLogger } from '../utils/debug.js';
|
|
8
|
+
import type { AuthContext } from '../types/auth.js';
|
|
9
|
+
import { createCoreServices } from '../core-services.js';
|
|
10
|
+
|
|
11
|
+
export const requestSetTrustLevelTool = {
|
|
12
|
+
name: 'remember_request_set_trust_level',
|
|
13
|
+
description: `Request a trust level change for a memory. Returns a confirmation token.
|
|
14
|
+
|
|
15
|
+
Trust levels (1-5 integer scale):
|
|
16
|
+
1 = PUBLIC — anyone can see
|
|
17
|
+
2 = INTERNAL — friends/known users
|
|
18
|
+
3 = CONFIDENTIAL — trusted friends
|
|
19
|
+
4 = RESTRICTED — close/intimate contacts
|
|
20
|
+
5 = SECRET — owner only (default for new memories)
|
|
21
|
+
|
|
22
|
+
After requesting, use remember_confirm with the returned token to apply the change.
|
|
23
|
+
Lowering trust (e.g. 5→1) makes the memory MORE visible. Raising trust makes it LESS visible.
|
|
24
|
+
|
|
25
|
+
This is the ONLY way to change a memory's trust level. Trust cannot be set during creation or update.`,
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
memory_id: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'ID of the memory to change trust level for',
|
|
32
|
+
},
|
|
33
|
+
trust_level: {
|
|
34
|
+
type: 'integer',
|
|
35
|
+
description: 'New trust level (1-5)',
|
|
36
|
+
minimum: 1,
|
|
37
|
+
maximum: 5,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['memory_id', 'trust_level'],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
interface RequestSetTrustLevelArgs {
|
|
45
|
+
memory_id: string;
|
|
46
|
+
trust_level: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function handleRequestSetTrustLevel(
|
|
50
|
+
args: RequestSetTrustLevelArgs,
|
|
51
|
+
userId: string,
|
|
52
|
+
authContext?: AuthContext
|
|
53
|
+
): Promise<string> {
|
|
54
|
+
const debug = createDebugLogger({
|
|
55
|
+
tool: 'remember_request_set_trust_level',
|
|
56
|
+
userId,
|
|
57
|
+
operation: 'request set trust level',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
debug.info('Tool invoked');
|
|
62
|
+
debug.trace('Arguments', { args });
|
|
63
|
+
|
|
64
|
+
// Validate trust level is integer 1-5
|
|
65
|
+
if (!Number.isInteger(args.trust_level) || args.trust_level < 1 || args.trust_level > 5) {
|
|
66
|
+
return JSON.stringify({
|
|
67
|
+
error: 'Invalid trust level',
|
|
68
|
+
message: 'Trust level must be an integer from 1 (PUBLIC) to 5 (SECRET).',
|
|
69
|
+
}, null, 2);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { memory } = createCoreServices(userId);
|
|
73
|
+
|
|
74
|
+
const result = await memory.requestSetTrustLevel({
|
|
75
|
+
memory_id: args.memory_id,
|
|
76
|
+
trust_level: args.trust_level,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const TRUST_NAMES: Record<number, string> = {
|
|
80
|
+
1: 'PUBLIC',
|
|
81
|
+
2: 'INTERNAL',
|
|
82
|
+
3: 'CONFIDENTIAL',
|
|
83
|
+
4: 'RESTRICTED',
|
|
84
|
+
5: 'SECRET',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return JSON.stringify({
|
|
88
|
+
token: result.token,
|
|
89
|
+
memory_id: result.memory_id,
|
|
90
|
+
current_trust_level: result.current_trust_level,
|
|
91
|
+
requested_trust_level: result.requested_trust_level,
|
|
92
|
+
current_trust_name: TRUST_NAMES[result.current_trust_level] || 'UNKNOWN',
|
|
93
|
+
requested_trust_name: TRUST_NAMES[result.requested_trust_level] || 'UNKNOWN',
|
|
94
|
+
expires_at: result.expires_at,
|
|
95
|
+
message: `Trust level change requested: ${TRUST_NAMES[result.current_trust_level] || result.current_trust_level} (${result.current_trust_level}) → ${TRUST_NAMES[result.requested_trust_level] || result.requested_trust_level} (${result.requested_trust_level}). Confirm with token to apply.`,
|
|
96
|
+
}, null, 2);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
debug.error('Tool failed', { error: error instanceof Error ? error.message : String(error) });
|
|
99
|
+
handleToolError(error, {
|
|
100
|
+
toolName: 'remember_request_set_trust_level',
|
|
101
|
+
operation: 'request set trust level',
|
|
102
|
+
userId,
|
|
103
|
+
memoryId: args.memory_id,
|
|
104
|
+
});
|
|
105
|
+
return JSON.stringify({ error: 'Unexpected error' });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -101,8 +101,10 @@ export const searchMemoryTool = {
|
|
|
101
101
|
description: 'Minimum weight (0-1)',
|
|
102
102
|
},
|
|
103
103
|
trust_min: {
|
|
104
|
-
type: '
|
|
105
|
-
description: 'Minimum trust level (
|
|
104
|
+
type: 'integer',
|
|
105
|
+
description: 'Minimum trust level (1-5: 1=PUBLIC, 2=INTERNAL, 3=CONFIDENTIAL, 4=RESTRICTED, 5=SECRET)',
|
|
106
|
+
minimum: 1,
|
|
107
|
+
maximum: 5,
|
|
106
108
|
},
|
|
107
109
|
date_from: {
|
|
108
110
|
type: 'string',
|
|
@@ -21,7 +21,6 @@ export const updateInternalMemoryTool = {
|
|
|
21
21
|
title: { type: 'string' },
|
|
22
22
|
tags: { type: 'array', items: { type: 'string' } },
|
|
23
23
|
weight: { type: 'number', minimum: 0, maximum: 1 },
|
|
24
|
-
trust: { type: 'number', minimum: 0, maximum: 1 },
|
|
25
24
|
},
|
|
26
25
|
required: ['memory_id'],
|
|
27
26
|
},
|
|
@@ -33,7 +32,6 @@ export interface UpdateInternalMemoryArgs {
|
|
|
33
32
|
title?: string;
|
|
34
33
|
tags?: string[];
|
|
35
34
|
weight?: number;
|
|
36
|
-
trust?: number;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
export async function handleUpdateInternalMemory(
|
|
@@ -70,7 +68,6 @@ export async function handleUpdateInternalMemory(
|
|
|
70
68
|
title: args.title,
|
|
71
69
|
tags: args.tags,
|
|
72
70
|
weight: args.weight,
|
|
73
|
-
trust: args.trust,
|
|
74
71
|
});
|
|
75
72
|
|
|
76
73
|
return JSON.stringify({
|
|
@@ -50,12 +50,6 @@ export const updateMemoryTool = {
|
|
|
50
50
|
minimum: 0,
|
|
51
51
|
maximum: 1,
|
|
52
52
|
},
|
|
53
|
-
trust: {
|
|
54
|
-
type: 'number',
|
|
55
|
-
description: 'Updated access control level (0-1)',
|
|
56
|
-
minimum: 0,
|
|
57
|
-
maximum: 1,
|
|
58
|
-
},
|
|
59
53
|
tags: {
|
|
60
54
|
type: 'array',
|
|
61
55
|
items: { type: 'string' },
|
|
@@ -138,7 +132,6 @@ export interface UpdateMemoryArgs {
|
|
|
138
132
|
title?: string;
|
|
139
133
|
type?: string;
|
|
140
134
|
weight?: number;
|
|
141
|
-
trust?: number;
|
|
142
135
|
tags?: string[];
|
|
143
136
|
references?: string[];
|
|
144
137
|
structured_content?: Record<string, any>;
|
|
@@ -190,7 +183,6 @@ export async function handleUpdateMemory(
|
|
|
190
183
|
title: args.title,
|
|
191
184
|
type: args.type,
|
|
192
185
|
weight: args.weight,
|
|
193
|
-
trust: args.trust,
|
|
194
186
|
tags: args.tags,
|
|
195
187
|
references: args.references,
|
|
196
188
|
parent_id: args.parent_id,
|
package/src/types/memory.ts
CHANGED
|
@@ -159,7 +159,7 @@ export interface Memory {
|
|
|
159
159
|
|
|
160
160
|
// Significance & Trust
|
|
161
161
|
weight: number; // 0-1, significance/priority
|
|
162
|
-
trust: number; //
|
|
162
|
+
trust: number; // 1-5 integer, access control level (1=PUBLIC, 5=SECRET)
|
|
163
163
|
confidence?: number; // 0-1, system confidence in accuracy
|
|
164
164
|
|
|
165
165
|
// Location (from platform)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isAdmin, adminPermissionError } from './admin.js';
|
|
2
|
+
|
|
3
|
+
describe('isAdmin', () => {
|
|
4
|
+
const originalEnv = process.env.ADMIN_USER_IDS;
|
|
5
|
+
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
if (originalEnv !== undefined) {
|
|
8
|
+
process.env.ADMIN_USER_IDS = originalEnv;
|
|
9
|
+
} else {
|
|
10
|
+
delete process.env.ADMIN_USER_IDS;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('returns true when userId matches a single admin ID', () => {
|
|
15
|
+
process.env.ADMIN_USER_IDS = 'user123';
|
|
16
|
+
expect(isAdmin('user123')).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns false when userId does not match', () => {
|
|
20
|
+
process.env.ADMIN_USER_IDS = 'user123';
|
|
21
|
+
expect(isAdmin('other_user')).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns true when userId matches one of multiple admin IDs', () => {
|
|
25
|
+
process.env.ADMIN_USER_IDS = 'user1,user2,user3';
|
|
26
|
+
expect(isAdmin('user2')).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns false when ADMIN_USER_IDS is empty string', () => {
|
|
30
|
+
process.env.ADMIN_USER_IDS = '';
|
|
31
|
+
expect(isAdmin('user123')).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns false when ADMIN_USER_IDS is undefined', () => {
|
|
35
|
+
delete process.env.ADMIN_USER_IDS;
|
|
36
|
+
expect(isAdmin('user123')).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('trims whitespace around IDs', () => {
|
|
40
|
+
process.env.ADMIN_USER_IDS = ' user1 , user2 , user3 ';
|
|
41
|
+
expect(isAdmin('user2')).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('filters out empty entries from trailing commas', () => {
|
|
45
|
+
process.env.ADMIN_USER_IDS = 'user1,,user2,';
|
|
46
|
+
expect(isAdmin('user1')).toBe(true);
|
|
47
|
+
expect(isAdmin('user2')).toBe(true);
|
|
48
|
+
expect(isAdmin('')).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('re-reads env var on each call (not cached)', () => {
|
|
52
|
+
process.env.ADMIN_USER_IDS = 'user1';
|
|
53
|
+
expect(isAdmin('user1')).toBe(true);
|
|
54
|
+
expect(isAdmin('user2')).toBe(false);
|
|
55
|
+
|
|
56
|
+
process.env.ADMIN_USER_IDS = 'user2';
|
|
57
|
+
expect(isAdmin('user1')).toBe(false);
|
|
58
|
+
expect(isAdmin('user2')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('adminPermissionError', () => {
|
|
63
|
+
it('returns correct error structure', () => {
|
|
64
|
+
const result = adminPermissionError();
|
|
65
|
+
expect(result).toEqual({
|
|
66
|
+
content: [{ type: 'text', text: 'Permission denied: admin access required' }],
|
|
67
|
+
isError: true,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin gate utilities
|
|
3
|
+
* Checks if a userId is in the ADMIN_USER_IDS env var.
|
|
4
|
+
* Re-reads env on each call (not cached) for hot-reload support.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check if a userId is an admin.
|
|
9
|
+
* Reads ADMIN_USER_IDS env var on each call.
|
|
10
|
+
*/
|
|
11
|
+
export function isAdmin(userId: string): boolean {
|
|
12
|
+
const adminIds = (process.env.ADMIN_USER_IDS || '')
|
|
13
|
+
.split(',')
|
|
14
|
+
.map(id => id.trim())
|
|
15
|
+
.filter(Boolean);
|
|
16
|
+
return adminIds.includes(userId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Standard permission error response for non-admin users.
|
|
21
|
+
*/
|
|
22
|
+
export function adminPermissionError() {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text' as const, text: 'Permission denied: admin access required' }],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|