@vurb/core 3.12.3 → 3.12.5
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 +1118 -1118
- package/dist/cli/commands/deploy.d.ts.map +1 -1
- package/dist/cli/commands/deploy.js +5 -1
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/introspect.d.ts +2 -1
- package/dist/cli/commands/introspect.d.ts.map +1 -1
- package/dist/cli/commands/introspect.js +9 -3
- package/dist/cli/commands/introspect.js.map +1 -1
- package/dist/cli/commands/validate.js +1 -1
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/constants.js +79 -79
- package/dist/cli/templates/cloudflare.js +225 -225
- package/dist/cli/templates/config.js +26 -26
- package/dist/cli/templates/core.js +95 -95
- package/dist/cli/templates/middleware.js +25 -25
- package/dist/cli/templates/model.js +22 -22
- package/dist/cli/templates/readme.js +144 -144
- package/dist/cli/templates/testing.js +84 -84
- package/dist/cli/templates/tools.js +46 -46
- package/dist/cli/templates/vectors/database.js +69 -69
- package/dist/cli/templates/vectors/oauth.js +63 -63
- package/dist/cli/templates/vectors/openapi.js +97 -97
- package/dist/cli/templates/vercel.js +190 -190
- package/dist/core/middleware/InputFirewall.js +28 -28
- package/dist/introspection/SemanticProbe.js +49 -49
- package/dist/presenter/PromptFirewall.js +28 -28
- package/package.json +153 -153
|
@@ -4,98 +4,98 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Generate `vitest.config.ts` */
|
|
6
6
|
export function vitestConfig() {
|
|
7
|
-
return `import { defineConfig } from 'vitest/config';
|
|
8
|
-
|
|
9
|
-
export default defineConfig({
|
|
10
|
-
test: {
|
|
11
|
-
include: ['tests/**/*.test.ts'],
|
|
12
|
-
},
|
|
13
|
-
});
|
|
7
|
+
return `import { defineConfig } from 'vitest/config';
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
test: {
|
|
11
|
+
include: ['tests/**/*.test.ts'],
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
14
|
`;
|
|
15
15
|
}
|
|
16
16
|
/** Generate `tests/setup.ts` — Test infrastructure */
|
|
17
17
|
export function testSetupTs() {
|
|
18
|
-
return `/**
|
|
19
|
-
* Test Setup — In-Memory MVA Emulator
|
|
20
|
-
*
|
|
21
|
-
* Creates a VurbTester that runs the full pipeline
|
|
22
|
-
* (Zod → Middleware → Handler → Egress Firewall)
|
|
23
|
-
* without any network transport.
|
|
24
|
-
*
|
|
25
|
-
* 2ms per test. $0.00 in tokens. Zero servers.
|
|
26
|
-
*/
|
|
27
|
-
import { fileURLToPath } from 'node:url';
|
|
28
|
-
import { createVurbTester } from '@vurb/testing';
|
|
29
|
-
import { autoDiscover } from '@vurb/core';
|
|
30
|
-
import { f } from '../src/vurb.js';
|
|
31
|
-
|
|
32
|
-
const registry = f.registry();
|
|
33
|
-
await autoDiscover(registry, fileURLToPath(new URL('../src/tools', import.meta.url)));
|
|
34
|
-
|
|
35
|
-
export const tester = createVurbTester(registry, {
|
|
36
|
-
contextFactory: () => ({
|
|
37
|
-
role: 'ADMIN' as const,
|
|
38
|
-
tenantId: 'test-tenant',
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
18
|
+
return `/**
|
|
19
|
+
* Test Setup — In-Memory MVA Emulator
|
|
20
|
+
*
|
|
21
|
+
* Creates a VurbTester that runs the full pipeline
|
|
22
|
+
* (Zod → Middleware → Handler → Egress Firewall)
|
|
23
|
+
* without any network transport.
|
|
24
|
+
*
|
|
25
|
+
* 2ms per test. $0.00 in tokens. Zero servers.
|
|
26
|
+
*/
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { createVurbTester } from '@vurb/testing';
|
|
29
|
+
import { autoDiscover } from '@vurb/core';
|
|
30
|
+
import { f } from '../src/vurb.js';
|
|
31
|
+
|
|
32
|
+
const registry = f.registry();
|
|
33
|
+
await autoDiscover(registry, fileURLToPath(new URL('../src/tools', import.meta.url)));
|
|
34
|
+
|
|
35
|
+
export const tester = createVurbTester(registry, {
|
|
36
|
+
contextFactory: () => ({
|
|
37
|
+
role: 'ADMIN' as const,
|
|
38
|
+
tenantId: 'test-tenant',
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
41
|
`;
|
|
42
42
|
}
|
|
43
43
|
/** Generate `tests/system.test.ts` — Egress Firewall + RBAC tests */
|
|
44
44
|
export function systemTestTs() {
|
|
45
|
-
return `/**
|
|
46
|
-
* System Tools — Egress Firewall & RBAC Tests
|
|
47
|
-
*
|
|
48
|
-
* Proves that:
|
|
49
|
-
* 1. The Presenter strips undeclared fields (SOC2 CC6.1)
|
|
50
|
-
* 2. RBAC middleware blocks GUEST access (SOC2 CC6.3)
|
|
51
|
-
* 3. System rules from .describe() are injected
|
|
52
|
-
*/
|
|
53
|
-
import { describe, it, expect } from 'vitest';
|
|
54
|
-
import { tester } from './setup.js';
|
|
55
|
-
|
|
56
|
-
describe('System Tools', () => {
|
|
57
|
-
describe('Egress Firewall', () => {
|
|
58
|
-
it('should return validated health data through the Presenter', async () => {
|
|
59
|
-
const result = await tester.callAction('system', 'health');
|
|
60
|
-
|
|
61
|
-
expect(result.isError).toBe(false);
|
|
62
|
-
expect(result.data).toHaveProperty('status');
|
|
63
|
-
expect(result.data).toHaveProperty('uptime');
|
|
64
|
-
expect(result.data).toHaveProperty('version');
|
|
65
|
-
expect(result.data).toHaveProperty('timestamp');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should strip undeclared fields (tenant must NOT leak)', async () => {
|
|
69
|
-
const result = await tester.callAction('system', 'health');
|
|
70
|
-
|
|
71
|
-
expect(result.isError).toBe(false);
|
|
72
|
-
// The handler returns 'tenant' but the Presenter schema
|
|
73
|
-
// does not declare it → stripped by Egress Firewall
|
|
74
|
-
expect(result.data).not.toHaveProperty('tenant');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should include JIT system rules from .describe()', async () => {
|
|
78
|
-
const result = await tester.callAction('system', 'health');
|
|
79
|
-
|
|
80
|
-
expect(result.systemRules.length).toBeGreaterThan(0);
|
|
81
|
-
expect(result.systemRules.some(
|
|
82
|
-
(r: string) => r.includes('uptime') || r.includes('Uptime')
|
|
83
|
-
)).toBe(true);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('Echo Tool', () => {
|
|
88
|
-
it('should echo the message back', async () => {
|
|
89
|
-
const result = await tester.callAction('system', 'echo', {
|
|
90
|
-
message: 'hello vurb',
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
expect(result.isError).toBe(false);
|
|
94
|
-
expect(result.data).toHaveProperty('echo', 'hello vurb');
|
|
95
|
-
expect(result.data).toHaveProperty('receivedAt');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
45
|
+
return `/**
|
|
46
|
+
* System Tools — Egress Firewall & RBAC Tests
|
|
47
|
+
*
|
|
48
|
+
* Proves that:
|
|
49
|
+
* 1. The Presenter strips undeclared fields (SOC2 CC6.1)
|
|
50
|
+
* 2. RBAC middleware blocks GUEST access (SOC2 CC6.3)
|
|
51
|
+
* 3. System rules from .describe() are injected
|
|
52
|
+
*/
|
|
53
|
+
import { describe, it, expect } from 'vitest';
|
|
54
|
+
import { tester } from './setup.js';
|
|
55
|
+
|
|
56
|
+
describe('System Tools', () => {
|
|
57
|
+
describe('Egress Firewall', () => {
|
|
58
|
+
it('should return validated health data through the Presenter', async () => {
|
|
59
|
+
const result = await tester.callAction('system', 'health');
|
|
60
|
+
|
|
61
|
+
expect(result.isError).toBe(false);
|
|
62
|
+
expect(result.data).toHaveProperty('status');
|
|
63
|
+
expect(result.data).toHaveProperty('uptime');
|
|
64
|
+
expect(result.data).toHaveProperty('version');
|
|
65
|
+
expect(result.data).toHaveProperty('timestamp');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should strip undeclared fields (tenant must NOT leak)', async () => {
|
|
69
|
+
const result = await tester.callAction('system', 'health');
|
|
70
|
+
|
|
71
|
+
expect(result.isError).toBe(false);
|
|
72
|
+
// The handler returns 'tenant' but the Presenter schema
|
|
73
|
+
// does not declare it → stripped by Egress Firewall
|
|
74
|
+
expect(result.data).not.toHaveProperty('tenant');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should include JIT system rules from .describe()', async () => {
|
|
78
|
+
const result = await tester.callAction('system', 'health');
|
|
79
|
+
|
|
80
|
+
expect(result.systemRules.length).toBeGreaterThan(0);
|
|
81
|
+
expect(result.systemRules.some(
|
|
82
|
+
(r: string) => r.includes('uptime') || r.includes('Uptime')
|
|
83
|
+
)).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('Echo Tool', () => {
|
|
88
|
+
it('should echo the message back', async () => {
|
|
89
|
+
const result = await tester.callAction('system', 'echo', {
|
|
90
|
+
message: 'hello vurb',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(result.isError).toBe(false);
|
|
94
|
+
expect(result.data).toHaveProperty('echo', 'hello vurb');
|
|
95
|
+
expect(result.data).toHaveProperty('receivedAt');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
99
|
`;
|
|
100
100
|
}
|
|
101
101
|
//# sourceMappingURL=testing.js.map
|
|
@@ -8,56 +8,56 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** Generate `src/tools/system/health.ts` — Health check with Presenter */
|
|
10
10
|
export function healthToolTs() {
|
|
11
|
-
return `/**
|
|
12
|
-
* System Health Tool — Full MVA Pipeline (Fluent API)
|
|
13
|
-
*
|
|
14
|
-
* Demonstrates:
|
|
15
|
-
* - f.query() — read-only semantic verb (auto-sets readOnlyHint)
|
|
16
|
-
* - .describe() — LLM-facing description
|
|
17
|
-
* - .returns() — Presenter (Egress Firewall + system rules + UI)
|
|
18
|
-
* - .handle(input, ctx) — fully typed handler
|
|
19
|
-
* - export default for autoDiscover()
|
|
20
|
-
*/
|
|
21
|
-
import { f } from '../../vurb.js';
|
|
22
|
-
import { SystemPresenter } from '../../presenters/SystemPresenter.js';
|
|
23
|
-
|
|
24
|
-
export default f.query('system.health')
|
|
25
|
-
.describe('Real-time server health status')
|
|
26
|
-
.returns(SystemPresenter)
|
|
27
|
-
.handle(async (_input, ctx) => {
|
|
28
|
-
// Return raw data — the Presenter validates, strips
|
|
29
|
-
// undeclared fields, injects rules, and renders UI.
|
|
30
|
-
return {
|
|
31
|
-
status: 'healthy',
|
|
32
|
-
uptime: process.uptime(),
|
|
33
|
-
version: '0.1.0',
|
|
34
|
-
timestamp: new Date().toISOString(),
|
|
35
|
-
tenant: ctx.tenantId,
|
|
36
|
-
};
|
|
37
|
-
});
|
|
11
|
+
return `/**
|
|
12
|
+
* System Health Tool — Full MVA Pipeline (Fluent API)
|
|
13
|
+
*
|
|
14
|
+
* Demonstrates:
|
|
15
|
+
* - f.query() — read-only semantic verb (auto-sets readOnlyHint)
|
|
16
|
+
* - .describe() — LLM-facing description
|
|
17
|
+
* - .returns() — Presenter (Egress Firewall + system rules + UI)
|
|
18
|
+
* - .handle(input, ctx) — fully typed handler
|
|
19
|
+
* - export default for autoDiscover()
|
|
20
|
+
*/
|
|
21
|
+
import { f } from '../../vurb.js';
|
|
22
|
+
import { SystemPresenter } from '../../presenters/SystemPresenter.js';
|
|
23
|
+
|
|
24
|
+
export default f.query('system.health')
|
|
25
|
+
.describe('Real-time server health status')
|
|
26
|
+
.returns(SystemPresenter)
|
|
27
|
+
.handle(async (_input, ctx) => {
|
|
28
|
+
// Return raw data — the Presenter validates, strips
|
|
29
|
+
// undeclared fields, injects rules, and renders UI.
|
|
30
|
+
return {
|
|
31
|
+
status: 'healthy',
|
|
32
|
+
uptime: process.uptime(),
|
|
33
|
+
version: '0.1.0',
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
tenant: ctx.tenantId,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
38
|
`;
|
|
39
39
|
}
|
|
40
40
|
/** Generate `src/tools/system/echo.ts` — Simple echo tool */
|
|
41
41
|
export function echoToolTs() {
|
|
42
|
-
return `/**
|
|
43
|
-
* Echo Tool — Connectivity Testing (Fluent API)
|
|
44
|
-
*
|
|
45
|
-
* Demonstrates:
|
|
46
|
-
* - f.query() with .withString() typed parameter
|
|
47
|
-
* - .handle(input, ctx) — input.message is typed as string
|
|
48
|
-
* - Implicit success() wrapping — return raw data, framework wraps it
|
|
49
|
-
*/
|
|
50
|
-
import { f } from '../../vurb.js';
|
|
51
|
-
|
|
52
|
-
export default f.query('system.echo')
|
|
53
|
-
.describe('Echo a message back (connectivity test)')
|
|
54
|
-
.withString('message', 'Message to echo back')
|
|
55
|
-
.handle(async (input) => {
|
|
56
|
-
return {
|
|
57
|
-
echo: input['message'],
|
|
58
|
-
receivedAt: new Date().toISOString(),
|
|
59
|
-
};
|
|
60
|
-
});
|
|
42
|
+
return `/**
|
|
43
|
+
* Echo Tool — Connectivity Testing (Fluent API)
|
|
44
|
+
*
|
|
45
|
+
* Demonstrates:
|
|
46
|
+
* - f.query() with .withString() typed parameter
|
|
47
|
+
* - .handle(input, ctx) — input.message is typed as string
|
|
48
|
+
* - Implicit success() wrapping — return raw data, framework wraps it
|
|
49
|
+
*/
|
|
50
|
+
import { f } from '../../vurb.js';
|
|
51
|
+
|
|
52
|
+
export default f.query('system.echo')
|
|
53
|
+
.describe('Echo a message back (connectivity test)')
|
|
54
|
+
.withString('message', 'Message to echo back')
|
|
55
|
+
.handle(async (input) => {
|
|
56
|
+
return {
|
|
57
|
+
echo: input['message'],
|
|
58
|
+
receivedAt: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
61
|
`;
|
|
62
62
|
}
|
|
63
63
|
//# sourceMappingURL=tools.js.map
|
|
@@ -4,79 +4,79 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Generate `prisma/schema.prisma` */
|
|
6
6
|
export function prismaSchema() {
|
|
7
|
-
return `// Prisma Schema — Database-Driven MCP Server
|
|
8
|
-
//
|
|
9
|
-
// The @vurb/prisma-gen generator reads annotations
|
|
10
|
-
// and auto-generates Presenters + ToolBuilders with:
|
|
11
|
-
// - Field-level security (/// @vurb.hide)
|
|
12
|
-
// - Tenant isolation
|
|
13
|
-
// - OOM protection
|
|
14
|
-
|
|
15
|
-
generator client {
|
|
16
|
-
provider = "prisma-client-js"
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
generator vurb {
|
|
20
|
-
provider = "@vurb/prisma-gen"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
datasource db {
|
|
24
|
-
provider = "postgresql"
|
|
25
|
-
url = env("DATABASE_URL")
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
model User {
|
|
29
|
-
id String @id @default(cuid())
|
|
30
|
-
email String @unique
|
|
31
|
-
name String
|
|
32
|
-
|
|
33
|
-
/// @vurb.hide — Stripped by the Egress Firewall before reaching the LLM
|
|
34
|
-
password String
|
|
35
|
-
|
|
36
|
-
role String @default("USER")
|
|
37
|
-
createdAt DateTime @default(now())
|
|
38
|
-
updatedAt DateTime @updatedAt
|
|
39
|
-
|
|
40
|
-
posts Post[]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
model Post {
|
|
44
|
-
id String @id @default(cuid())
|
|
45
|
-
title String
|
|
46
|
-
content String?
|
|
47
|
-
published Boolean @default(false)
|
|
48
|
-
createdAt DateTime @default(now())
|
|
49
|
-
|
|
50
|
-
author User @relation(fields: [authorId], references: [id])
|
|
51
|
-
authorId String
|
|
52
|
-
}
|
|
7
|
+
return `// Prisma Schema — Database-Driven MCP Server
|
|
8
|
+
//
|
|
9
|
+
// The @vurb/prisma-gen generator reads annotations
|
|
10
|
+
// and auto-generates Presenters + ToolBuilders with:
|
|
11
|
+
// - Field-level security (/// @vurb.hide)
|
|
12
|
+
// - Tenant isolation
|
|
13
|
+
// - OOM protection
|
|
14
|
+
|
|
15
|
+
generator client {
|
|
16
|
+
provider = "prisma-client-js"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
generator vurb {
|
|
20
|
+
provider = "@vurb/prisma-gen"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
datasource db {
|
|
24
|
+
provider = "postgresql"
|
|
25
|
+
url = env("DATABASE_URL")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
model User {
|
|
29
|
+
id String @id @default(cuid())
|
|
30
|
+
email String @unique
|
|
31
|
+
name String
|
|
32
|
+
|
|
33
|
+
/// @vurb.hide — Stripped by the Egress Firewall before reaching the LLM
|
|
34
|
+
password String
|
|
35
|
+
|
|
36
|
+
role String @default("USER")
|
|
37
|
+
createdAt DateTime @default(now())
|
|
38
|
+
updatedAt DateTime @updatedAt
|
|
39
|
+
|
|
40
|
+
posts Post[]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
model Post {
|
|
44
|
+
id String @id @default(cuid())
|
|
45
|
+
title String
|
|
46
|
+
content String?
|
|
47
|
+
published Boolean @default(false)
|
|
48
|
+
createdAt DateTime @default(now())
|
|
49
|
+
|
|
50
|
+
author User @relation(fields: [authorId], references: [id])
|
|
51
|
+
authorId String
|
|
52
|
+
}
|
|
53
53
|
`;
|
|
54
54
|
}
|
|
55
55
|
/** Generate `src/tools/db/users.ts` */
|
|
56
56
|
export function dbUsersToolTs() {
|
|
57
|
-
return `/**
|
|
58
|
-
* Database Users Tool — Prisma-Driven CRUD (Fluent API)
|
|
59
|
-
*
|
|
60
|
-
* Demonstrates:
|
|
61
|
-
* - f.query() with .withOptionalNumber() typed parameter
|
|
62
|
-
* - .handle(input, ctx) — input.take is typed as number | undefined
|
|
63
|
-
* - Implicit success() wrapping
|
|
64
|
-
*/
|
|
65
|
-
import { f } from '../../vurb.js';
|
|
66
|
-
|
|
67
|
-
export default f.query('db.list_users')
|
|
68
|
-
.describe('List users from the database')
|
|
69
|
-
.withOptionalNumber('take', 'Max results (1-50)')
|
|
70
|
-
.handle(async (input, ctx) => {
|
|
71
|
-
// TODO: Replace with your Prisma client
|
|
72
|
-
// const users = await ctx.db.user.findMany({ take: input.take ?? 10 });
|
|
73
|
-
// return users;
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
hint: 'Connect your Prisma client in src/context.ts to enable database queries.',
|
|
77
|
-
example: 'const users = await ctx.db.user.findMany({ take: 10 })',
|
|
78
|
-
};
|
|
79
|
-
});
|
|
57
|
+
return `/**
|
|
58
|
+
* Database Users Tool — Prisma-Driven CRUD (Fluent API)
|
|
59
|
+
*
|
|
60
|
+
* Demonstrates:
|
|
61
|
+
* - f.query() with .withOptionalNumber() typed parameter
|
|
62
|
+
* - .handle(input, ctx) — input.take is typed as number | undefined
|
|
63
|
+
* - Implicit success() wrapping
|
|
64
|
+
*/
|
|
65
|
+
import { f } from '../../vurb.js';
|
|
66
|
+
|
|
67
|
+
export default f.query('db.list_users')
|
|
68
|
+
.describe('List users from the database')
|
|
69
|
+
.withOptionalNumber('take', 'Max results (1-50)')
|
|
70
|
+
.handle(async (input, ctx) => {
|
|
71
|
+
// TODO: Replace with your Prisma client
|
|
72
|
+
// const users = await ctx.db.user.findMany({ take: input.take ?? 10 });
|
|
73
|
+
// return users;
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
hint: 'Connect your Prisma client in src/context.ts to enable database queries.',
|
|
77
|
+
example: 'const users = await ctx.db.user.findMany({ take: 10 })',
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
80
|
`;
|
|
81
81
|
}
|
|
82
82
|
//# sourceMappingURL=database.js.map
|
|
@@ -4,73 +4,73 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Generate `src/auth.ts` — OAuth Device Flow setup */
|
|
6
6
|
export function oauthSetupTs(config) {
|
|
7
|
-
return `/**
|
|
8
|
-
* OAuth Setup — Device Flow Authentication (RFC 8628)
|
|
9
|
-
*
|
|
10
|
-
* Pre-configured \`createAuthTool()\` with login, complete, status, logout actions.
|
|
11
|
-
* The \`requireAuth()\` middleware protects any tool with one line.
|
|
12
|
-
*
|
|
13
|
-
* 1. Set CLIENT_ID and AUTH endpoints in .env
|
|
14
|
-
* 2. Register the auth tool in server.ts
|
|
15
|
-
* 3. Use \`requireAuth()\` on protected tools
|
|
16
|
-
*/
|
|
17
|
-
import { createAuthTool, TokenManager } from '@vurb/oauth';
|
|
18
|
-
import type { ToolRegistry } from '@vurb/core';
|
|
19
|
-
|
|
20
|
-
export function registerAuth<TContext>(registry: ToolRegistry<TContext>): void {
|
|
21
|
-
const clientId = process.env['OAUTH_CLIENT_ID'];
|
|
22
|
-
const authEndpoint = process.env['OAUTH_AUTH_ENDPOINT'];
|
|
23
|
-
const tokenEndpoint = process.env['OAUTH_TOKEN_ENDPOINT'];
|
|
24
|
-
|
|
25
|
-
if (!clientId || !authEndpoint || !tokenEndpoint) {
|
|
26
|
-
console.error('⚠️ OAUTH_CLIENT_ID, OAUTH_AUTH_ENDPOINT, OAUTH_TOKEN_ENDPOINT are required in .env');
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const auth = createAuthTool({
|
|
31
|
-
clientId,
|
|
32
|
-
authorizationEndpoint: authEndpoint,
|
|
33
|
-
tokenEndpoint,
|
|
34
|
-
tokenManager: {
|
|
35
|
-
configDir: '.${config.name}',
|
|
36
|
-
envVar: '${config.name.toUpperCase().replace(/-/g, '_')}_TOKEN',
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
registry.register(auth);
|
|
41
|
-
console.error('🔐 OAuth Device Flow registered (auth.login → auth.complete → auth.status)');
|
|
42
|
-
}
|
|
7
|
+
return `/**
|
|
8
|
+
* OAuth Setup — Device Flow Authentication (RFC 8628)
|
|
9
|
+
*
|
|
10
|
+
* Pre-configured \`createAuthTool()\` with login, complete, status, logout actions.
|
|
11
|
+
* The \`requireAuth()\` middleware protects any tool with one line.
|
|
12
|
+
*
|
|
13
|
+
* 1. Set CLIENT_ID and AUTH endpoints in .env
|
|
14
|
+
* 2. Register the auth tool in server.ts
|
|
15
|
+
* 3. Use \`requireAuth()\` on protected tools
|
|
16
|
+
*/
|
|
17
|
+
import { createAuthTool, TokenManager } from '@vurb/oauth';
|
|
18
|
+
import type { ToolRegistry } from '@vurb/core';
|
|
19
|
+
|
|
20
|
+
export function registerAuth<TContext>(registry: ToolRegistry<TContext>): void {
|
|
21
|
+
const clientId = process.env['OAUTH_CLIENT_ID'];
|
|
22
|
+
const authEndpoint = process.env['OAUTH_AUTH_ENDPOINT'];
|
|
23
|
+
const tokenEndpoint = process.env['OAUTH_TOKEN_ENDPOINT'];
|
|
24
|
+
|
|
25
|
+
if (!clientId || !authEndpoint || !tokenEndpoint) {
|
|
26
|
+
console.error('⚠️ OAUTH_CLIENT_ID, OAUTH_AUTH_ENDPOINT, OAUTH_TOKEN_ENDPOINT are required in .env');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const auth = createAuthTool({
|
|
31
|
+
clientId,
|
|
32
|
+
authorizationEndpoint: authEndpoint,
|
|
33
|
+
tokenEndpoint,
|
|
34
|
+
tokenManager: {
|
|
35
|
+
configDir: '.${config.name}',
|
|
36
|
+
envVar: '${config.name.toUpperCase().replace(/-/g, '_')}_TOKEN',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
registry.register(auth);
|
|
41
|
+
console.error('🔐 OAuth Device Flow registered (auth.login → auth.complete → auth.status)');
|
|
42
|
+
}
|
|
43
43
|
`;
|
|
44
44
|
}
|
|
45
45
|
/** Generate `src/middleware/auth.ts` — requireAuth middleware */
|
|
46
46
|
export function oauthMiddlewareTs() {
|
|
47
|
-
return `/**
|
|
48
|
-
* Auth Middleware — Protect tools with requireAuth()
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* \`\`\`ts
|
|
52
|
-
* import { withAuth } from '../middleware/auth.js';
|
|
53
|
-
*
|
|
54
|
-
* export default f.query('projects.list')
|
|
55
|
-
* .describe('List all projects')
|
|
56
|
-
* .use(withAuth)
|
|
57
|
-
* .handle(async (input, ctx) => { /* authenticated */ });
|
|
58
|
-
* \`\`\`
|
|
59
|
-
*/
|
|
60
|
-
import { requireAuth } from '@vurb/oauth';
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Pre-configured auth middleware.
|
|
64
|
-
* Rejects unauthenticated requests with \`AUTH_REQUIRED\` + self-healing hints.
|
|
65
|
-
*/
|
|
66
|
-
export const withAuth = requireAuth({
|
|
67
|
-
extractToken: (ctx: unknown) => {
|
|
68
|
-
const obj = ctx as Record<string, unknown>;
|
|
69
|
-
return typeof obj['token'] === 'string' ? obj['token'] : null;
|
|
70
|
-
},
|
|
71
|
-
recoveryHint: 'Call auth action=login to authenticate via browser',
|
|
72
|
-
recoveryAction: 'auth',
|
|
73
|
-
});
|
|
47
|
+
return `/**
|
|
48
|
+
* Auth Middleware — Protect tools with requireAuth()
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* \`\`\`ts
|
|
52
|
+
* import { withAuth } from '../middleware/auth.js';
|
|
53
|
+
*
|
|
54
|
+
* export default f.query('projects.list')
|
|
55
|
+
* .describe('List all projects')
|
|
56
|
+
* .use(withAuth)
|
|
57
|
+
* .handle(async (input, ctx) => { /* authenticated */ });
|
|
58
|
+
* \`\`\`
|
|
59
|
+
*/
|
|
60
|
+
import { requireAuth } from '@vurb/oauth';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Pre-configured auth middleware.
|
|
64
|
+
* Rejects unauthenticated requests with \`AUTH_REQUIRED\` + self-healing hints.
|
|
65
|
+
*/
|
|
66
|
+
export const withAuth = requireAuth({
|
|
67
|
+
extractToken: (ctx: unknown) => {
|
|
68
|
+
const obj = ctx as Record<string, unknown>;
|
|
69
|
+
return typeof obj['token'] === 'string' ? obj['token'] : null;
|
|
70
|
+
},
|
|
71
|
+
recoveryHint: 'Call auth action=login to authenticate via browser',
|
|
72
|
+
recoveryAction: 'auth',
|
|
73
|
+
});
|
|
74
74
|
`;
|
|
75
75
|
}
|
|
76
76
|
//# sourceMappingURL=oauth.js.map
|