@honeyfield/rent2b-mcp 1.2.0 → 1.2.1
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/dist/api-client.js +14 -5
- package/dist/api-client.js.map +1 -1
- package/dist/api-client.test.d.ts +1 -0
- package/dist/api-client.test.js +41 -0
- package/dist/api-client.test.js.map +1 -0
- package/dist/auth/authorize-page.d.ts +18 -0
- package/dist/auth/authorize-page.js +60 -0
- package/dist/auth/authorize-page.js.map +1 -0
- package/dist/auth/authorize-page.test.d.ts +1 -0
- package/dist/auth/authorize-page.test.js +37 -0
- package/dist/auth/authorize-page.test.js.map +1 -0
- package/dist/auth/http-oauth.d.ts +28 -0
- package/dist/auth/http-oauth.js +93 -0
- package/dist/auth/http-oauth.js.map +1 -0
- package/dist/auth/oauth-config.d.ts +6 -0
- package/dist/auth/oauth-config.js +16 -0
- package/dist/auth/oauth-config.js.map +1 -0
- package/dist/auth/oauth-config.test.d.ts +1 -0
- package/dist/auth/oauth-config.test.js +25 -0
- package/dist/auth/oauth-config.test.js.map +1 -0
- package/dist/auth/provider.d.ts +28 -0
- package/dist/auth/provider.js +127 -0
- package/dist/auth/provider.js.map +1 -0
- package/dist/auth/provider.test.d.ts +1 -0
- package/dist/auth/provider.test.js +80 -0
- package/dist/auth/provider.test.js.map +1 -0
- package/dist/auth/stores.d.ts +27 -0
- package/dist/auth/stores.js +39 -0
- package/dist/auth/stores.js.map +1 -0
- package/dist/auth/stores.test.d.ts +1 -0
- package/dist/auth/stores.test.js +41 -0
- package/dist/auth/stores.test.js.map +1 -0
- package/dist/auth/token-crypto.d.ts +14 -0
- package/dist/auth/token-crypto.js +50 -0
- package/dist/auth/token-crypto.js.map +1 -0
- package/dist/auth/token-crypto.test.d.ts +1 -0
- package/dist/auth/token-crypto.test.js +40 -0
- package/dist/auth/token-crypto.test.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js.map +1 -1
- package/dist/generator/openapi-parser.js +21 -0
- package/dist/generator/openapi-parser.js.map +1 -1
- package/dist/generator/tool-generator.js +19 -1
- package/dist/generator/tool-generator.js.map +1 -1
- package/dist/generator/tool-generator.test.d.ts +1 -0
- package/dist/generator/tool-generator.test.js +35 -0
- package/dist/generator/tool-generator.test.js.map +1 -0
- package/dist/index.js +85 -60
- package/dist/index.js.map +1 -1
- package/dist/overrides/tool-overrides.json +60 -0
- package/generated/tools.json +1395 -215
- package/package.json +8 -3
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { createTokenCodec } from './token-crypto.js';
|
|
4
|
+
import { InMemoryClientsStore, AuthCodeStore } from './stores.js';
|
|
5
|
+
import { Rent2bOAuthProvider } from './provider.js';
|
|
6
|
+
function makeProvider(validateKey = vi.fn(async () => 99)) {
|
|
7
|
+
const codec = createTokenCodec(randomBytes(32));
|
|
8
|
+
const provider = new Rent2bOAuthProvider({
|
|
9
|
+
codec,
|
|
10
|
+
clientsStore: new InMemoryClientsStore(),
|
|
11
|
+
codeStore: new AuthCodeStore(),
|
|
12
|
+
validateKey,
|
|
13
|
+
});
|
|
14
|
+
return { provider, codec, validateKey };
|
|
15
|
+
}
|
|
16
|
+
const client = { client_id: 'c1', redirect_uris: ['https://claude.ai/cb'] };
|
|
17
|
+
describe('Rent2bOAuthProvider', () => {
|
|
18
|
+
it('issues a code, exposes its challenge, then exchanges it once', async () => {
|
|
19
|
+
const { provider } = makeProvider();
|
|
20
|
+
const code = provider.issueAuthCode({
|
|
21
|
+
apiKey: 'r2b_live',
|
|
22
|
+
orgId: 99,
|
|
23
|
+
codeChallenge: 'CHAL',
|
|
24
|
+
redirectUri: 'https://claude.ai/cb',
|
|
25
|
+
clientId: 'c1',
|
|
26
|
+
});
|
|
27
|
+
expect(await provider.challengeForAuthorizationCode(client, code)).toBe('CHAL');
|
|
28
|
+
const tokens = await provider.exchangeAuthorizationCode(client, code);
|
|
29
|
+
expect(tokens.access_token).toBeTruthy();
|
|
30
|
+
expect(tokens.token_type).toBe('Bearer');
|
|
31
|
+
expect(tokens.refresh_token).toBeTruthy();
|
|
32
|
+
// one-time: second exchange must fail
|
|
33
|
+
await expect(provider.exchangeAuthorizationCode(client, code)).rejects.toThrow();
|
|
34
|
+
});
|
|
35
|
+
it('verifyAccessToken decrypts an issued access token to the api key', async () => {
|
|
36
|
+
const { provider } = makeProvider();
|
|
37
|
+
const code = provider.issueAuthCode({
|
|
38
|
+
apiKey: 'r2b_live',
|
|
39
|
+
orgId: 99,
|
|
40
|
+
codeChallenge: 'CHAL',
|
|
41
|
+
redirectUri: 'https://claude.ai/cb',
|
|
42
|
+
clientId: 'c1',
|
|
43
|
+
});
|
|
44
|
+
const tokens = await provider.exchangeAuthorizationCode(client, code);
|
|
45
|
+
const info = await provider.verifyAccessToken(tokens.access_token);
|
|
46
|
+
expect(info.extra?.apiKey).toBe('r2b_live');
|
|
47
|
+
expect(info.extra?.orgId).toBe(99);
|
|
48
|
+
expect(info.clientId).toBe('rent2b-oauth');
|
|
49
|
+
});
|
|
50
|
+
it('verifyAccessToken accepts a raw r2b_ key (backward compat)', async () => {
|
|
51
|
+
const validateKey = vi.fn(async () => 7);
|
|
52
|
+
const { provider } = makeProvider(validateKey);
|
|
53
|
+
const info = await provider.verifyAccessToken('r2b_directkey');
|
|
54
|
+
expect(validateKey).toHaveBeenCalledWith('r2b_directkey');
|
|
55
|
+
expect(info.extra?.apiKey).toBe('r2b_directkey');
|
|
56
|
+
expect(info.extra?.orgId).toBe(7);
|
|
57
|
+
// requireBearerAuth rejects tokens without an expiry — both branches must set it.
|
|
58
|
+
expect(typeof info.expiresAt).toBe('number');
|
|
59
|
+
expect(info.expiresAt).toBeGreaterThan(Math.floor(Date.now() / 1000));
|
|
60
|
+
});
|
|
61
|
+
it('verifyAccessToken rejects garbage', async () => {
|
|
62
|
+
const { provider } = makeProvider();
|
|
63
|
+
await expect(provider.verifyAccessToken('not-a-token')).rejects.toThrow();
|
|
64
|
+
});
|
|
65
|
+
it('refresh token yields a new access token bound to the same key', async () => {
|
|
66
|
+
const { provider } = makeProvider();
|
|
67
|
+
const code = provider.issueAuthCode({
|
|
68
|
+
apiKey: 'r2b_live',
|
|
69
|
+
orgId: 99,
|
|
70
|
+
codeChallenge: 'CHAL',
|
|
71
|
+
redirectUri: 'https://claude.ai/cb',
|
|
72
|
+
clientId: 'c1',
|
|
73
|
+
});
|
|
74
|
+
const tokens = await provider.exchangeAuthorizationCode(client, code);
|
|
75
|
+
const refreshed = await provider.exchangeRefreshToken(client, tokens.refresh_token);
|
|
76
|
+
const info = await provider.verifyAccessToken(refreshed.access_token);
|
|
77
|
+
expect(info.extra?.apiKey).toBe('r2b_live');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.test.js","sourceRoot":"","sources":["../../src/auth/provider.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { OAuthClientInformationFull } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory registered-clients store for Dynamic Client Registration.
|
|
4
|
+
*
|
|
5
|
+
* The SDK's /register handler generates `client_id` and `client_id_issued_at`
|
|
6
|
+
* itself before calling `registerClient`, so at runtime the argument always
|
|
7
|
+
* carries a `client_id`. The parameter is typed as the SDK's `Omit<...>` to
|
|
8
|
+
* satisfy `OAuthRegisteredClientsStore`; we cast internally to read the id.
|
|
9
|
+
*/
|
|
10
|
+
export declare class InMemoryClientsStore {
|
|
11
|
+
private clients;
|
|
12
|
+
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
13
|
+
registerClient(client: Omit<OAuthClientInformationFull, 'client_id' | 'client_id_issued_at'>): Promise<OAuthClientInformationFull>;
|
|
14
|
+
}
|
|
15
|
+
export interface AuthCodeRecord {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
orgId: number;
|
|
18
|
+
codeChallenge: string;
|
|
19
|
+
redirectUri: string;
|
|
20
|
+
clientId: string;
|
|
21
|
+
expiresAt: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class AuthCodeStore {
|
|
24
|
+
private codes;
|
|
25
|
+
create(record: Omit<AuthCodeRecord, 'expiresAt'>, ttlMs?: number): string;
|
|
26
|
+
take(code: string): AuthCodeRecord | undefined;
|
|
27
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory registered-clients store for Dynamic Client Registration.
|
|
4
|
+
*
|
|
5
|
+
* The SDK's /register handler generates `client_id` and `client_id_issued_at`
|
|
6
|
+
* itself before calling `registerClient`, so at runtime the argument always
|
|
7
|
+
* carries a `client_id`. The parameter is typed as the SDK's `Omit<...>` to
|
|
8
|
+
* satisfy `OAuthRegisteredClientsStore`; we cast internally to read the id.
|
|
9
|
+
*/
|
|
10
|
+
export class InMemoryClientsStore {
|
|
11
|
+
clients = new Map();
|
|
12
|
+
async getClient(clientId) {
|
|
13
|
+
return this.clients.get(clientId);
|
|
14
|
+
}
|
|
15
|
+
async registerClient(client) {
|
|
16
|
+
const full = client;
|
|
17
|
+
this.clients.set(full.client_id, full);
|
|
18
|
+
return full;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
22
|
+
export class AuthCodeStore {
|
|
23
|
+
codes = new Map();
|
|
24
|
+
create(record, ttlMs = DEFAULT_TTL_MS) {
|
|
25
|
+
const code = randomUUID().replace(/-/g, '') + randomUUID().replace(/-/g, '');
|
|
26
|
+
this.codes.set(code, { ...record, expiresAt: Date.now() + ttlMs });
|
|
27
|
+
return code;
|
|
28
|
+
}
|
|
29
|
+
take(code) {
|
|
30
|
+
const rec = this.codes.get(code);
|
|
31
|
+
if (!rec)
|
|
32
|
+
return undefined;
|
|
33
|
+
this.codes.delete(code);
|
|
34
|
+
if (rec.expiresAt < Date.now())
|
|
35
|
+
return undefined;
|
|
36
|
+
return rec;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../../src/auth/stores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAoB;IACvB,OAAO,GAAG,IAAI,GAAG,EAAsC,CAAC;IAEhE,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAA6E;QAE7E,MAAM,IAAI,GAAG,MAAoC,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAWD,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAErC,MAAM,OAAO,aAAa;IAChB,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,MAAM,CAAC,MAAyC,EAAE,QAAgB,cAAc;QAC9E,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,IAAY;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG;YAAE,OAAO,SAAS,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,SAAS,CAAC;QACjD,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { InMemoryClientsStore, AuthCodeStore } from './stores.js';
|
|
3
|
+
describe('InMemoryClientsStore', () => {
|
|
4
|
+
it('registers and retrieves a client', async () => {
|
|
5
|
+
const store = new InMemoryClientsStore();
|
|
6
|
+
const reg = await store.registerClient({
|
|
7
|
+
client_id: 'c1',
|
|
8
|
+
redirect_uris: ['https://claude.ai/callback'],
|
|
9
|
+
});
|
|
10
|
+
expect(reg.client_id).toBe('c1');
|
|
11
|
+
const got = await store.getClient('c1');
|
|
12
|
+
expect(got?.redirect_uris).toEqual(['https://claude.ai/callback']);
|
|
13
|
+
});
|
|
14
|
+
it('returns undefined for unknown client', async () => {
|
|
15
|
+
const store = new InMemoryClientsStore();
|
|
16
|
+
expect(await store.getClient('nope')).toBeUndefined();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('AuthCodeStore', () => {
|
|
20
|
+
const rec = {
|
|
21
|
+
apiKey: 'r2b_x',
|
|
22
|
+
orgId: 7,
|
|
23
|
+
codeChallenge: 'chal',
|
|
24
|
+
redirectUri: 'https://claude.ai/cb',
|
|
25
|
+
clientId: 'c1',
|
|
26
|
+
};
|
|
27
|
+
it('creates a code and takes it exactly once', () => {
|
|
28
|
+
const store = new AuthCodeStore();
|
|
29
|
+
const code = store.create(rec);
|
|
30
|
+
expect(typeof code).toBe('string');
|
|
31
|
+
const first = store.take(code);
|
|
32
|
+
expect(first?.apiKey).toBe('r2b_x');
|
|
33
|
+
expect(store.take(code)).toBeUndefined(); // one-time
|
|
34
|
+
});
|
|
35
|
+
it('does not return an expired code', () => {
|
|
36
|
+
const store = new AuthCodeStore();
|
|
37
|
+
const code = store.create(rec, -1); // already expired
|
|
38
|
+
expect(store.take(code)).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=stores.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.test.js","sourceRoot":"","sources":["../../src/auth/stores.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAElE,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,KAAK,GAAG,IAAI,oBAAoB,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC;YACrC,SAAS,EAAE,IAAI;YACf,aAAa,EAAE,CAAC,4BAA4B,CAAC;SACvC,CAAC,CAAC;QACV,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,KAAK,GAAG,IAAI,oBAAoB,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,MAAM,GAAG,GAAG;QACV,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,CAAC;QACR,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,sBAAsB;QACnC,QAAQ,EAAE,IAAI;KACf,CAAC;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,WAAW;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB;QACtD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface TokenPayload {
|
|
2
|
+
apiKey: string;
|
|
3
|
+
orgId: number;
|
|
4
|
+
type: 'access' | 'refresh';
|
|
5
|
+
exp: number;
|
|
6
|
+
jti: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class TokenError extends Error {
|
|
9
|
+
}
|
|
10
|
+
export interface TokenCodec {
|
|
11
|
+
encrypt(payload: TokenPayload): string;
|
|
12
|
+
decrypt(token: string): TokenPayload;
|
|
13
|
+
}
|
|
14
|
+
export declare function createTokenCodec(key: Buffer): TokenCodec;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
|
|
2
|
+
export class TokenError extends Error {
|
|
3
|
+
}
|
|
4
|
+
const IV_LEN = 12;
|
|
5
|
+
const TAG_LEN = 16;
|
|
6
|
+
export function createTokenCodec(key) {
|
|
7
|
+
if (key.length !== 32) {
|
|
8
|
+
throw new Error('OAUTH_ENCRYPTION_KEY must decode to exactly 32 bytes');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
encrypt(payload) {
|
|
12
|
+
const iv = randomBytes(IV_LEN);
|
|
13
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
14
|
+
const plaintext = Buffer.from(JSON.stringify(payload), 'utf8');
|
|
15
|
+
const enc = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
16
|
+
const tag = cipher.getAuthTag();
|
|
17
|
+
return Buffer.concat([iv, tag, enc]).toString('base64url');
|
|
18
|
+
},
|
|
19
|
+
decrypt(token) {
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
raw = Buffer.from(token, 'base64url');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new TokenError('Malformed token');
|
|
26
|
+
}
|
|
27
|
+
if (raw.length < IV_LEN + TAG_LEN + 1) {
|
|
28
|
+
throw new TokenError('Malformed token');
|
|
29
|
+
}
|
|
30
|
+
const iv = raw.subarray(0, IV_LEN);
|
|
31
|
+
const tag = raw.subarray(IV_LEN, IV_LEN + TAG_LEN);
|
|
32
|
+
const enc = raw.subarray(IV_LEN + TAG_LEN);
|
|
33
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
34
|
+
decipher.setAuthTag(tag);
|
|
35
|
+
let payload;
|
|
36
|
+
try {
|
|
37
|
+
const dec = Buffer.concat([decipher.update(enc), decipher.final()]);
|
|
38
|
+
payload = JSON.parse(dec.toString('utf8'));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new TokenError('Invalid or tampered token');
|
|
42
|
+
}
|
|
43
|
+
if (typeof payload.exp !== 'number' || payload.exp < Math.floor(Date.now() / 1000)) {
|
|
44
|
+
throw new TokenError('Token expired');
|
|
45
|
+
}
|
|
46
|
+
return payload;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=token-crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-crypto.js","sourceRoot":"","sources":["../../src/auth/token-crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAU5E,MAAM,OAAO,UAAW,SAAQ,KAAK;CAAG;AAOxC,MAAM,MAAM,GAAG,EAAE,CAAC;AAClB,MAAM,OAAO,GAAG,EAAE,CAAC;AAEnB,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO;QACL,OAAO,CAAC,OAAqB;YAC3B,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;YAC/D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YACtE,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,CAAC,KAAa;YACnB,IAAI,GAAW,CAAC;YAChB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAC;YAC1C,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAAC;YAC1C,CAAC;YACD,MAAM,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;YAC3C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YAC1D,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,OAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBACpE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAiB,CAAC;YAC7D,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,UAAU,CAAC,2BAA2B,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;gBACnF,MAAM,IAAI,UAAU,CAAC,eAAe,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { createTokenCodec, TokenError } from './token-crypto.js';
|
|
4
|
+
const key = randomBytes(32);
|
|
5
|
+
const base = () => ({
|
|
6
|
+
apiKey: 'r2b_abc',
|
|
7
|
+
orgId: 42,
|
|
8
|
+
type: 'access',
|
|
9
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
10
|
+
jti: 'jti-1',
|
|
11
|
+
});
|
|
12
|
+
describe('token-crypto', () => {
|
|
13
|
+
it('round-trips an encrypted payload', () => {
|
|
14
|
+
const codec = createTokenCodec(key);
|
|
15
|
+
const token = codec.encrypt(base());
|
|
16
|
+
expect(typeof token).toBe('string');
|
|
17
|
+
expect(token).not.toContain('r2b_'); // key must not be readable
|
|
18
|
+
const out = codec.decrypt(token);
|
|
19
|
+
expect(out.apiKey).toBe('r2b_abc');
|
|
20
|
+
expect(out.orgId).toBe(42);
|
|
21
|
+
expect(out.type).toBe('access');
|
|
22
|
+
});
|
|
23
|
+
it('rejects a tampered token', () => {
|
|
24
|
+
const codec = createTokenCodec(key);
|
|
25
|
+
const token = codec.encrypt(base());
|
|
26
|
+
const tampered = token.slice(0, -2) + (token.endsWith('A') ? 'BB' : 'AA');
|
|
27
|
+
expect(() => codec.decrypt(tampered)).toThrow(TokenError);
|
|
28
|
+
});
|
|
29
|
+
it('rejects a token signed with a different key', () => {
|
|
30
|
+
const token = createTokenCodec(key).encrypt(base());
|
|
31
|
+
const other = createTokenCodec(randomBytes(32));
|
|
32
|
+
expect(() => other.decrypt(token)).toThrow(TokenError);
|
|
33
|
+
});
|
|
34
|
+
it('rejects an expired token', () => {
|
|
35
|
+
const codec = createTokenCodec(key);
|
|
36
|
+
const token = codec.encrypt({ ...base(), exp: Math.floor(Date.now() / 1000) - 1 });
|
|
37
|
+
expect(() => codec.decrypt(token)).toThrow(TokenError);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=token-crypto.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-crypto.test.js","sourceRoot":"","sources":["../../src/auth/token-crypto.test.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export interface Config {
|
|
2
2
|
apiKey: string;
|
|
3
3
|
apiUrl: string;
|
|
4
|
+
/**
|
|
5
|
+
* Pre-resolved organization id for this key. When provided, the client skips
|
|
6
|
+
* the `GET /zapier/me` lookup — used in HTTP mode where the auth layer
|
|
7
|
+
* (verifyAccessToken) already resolved the org and set it on req.auth.
|
|
8
|
+
*/
|
|
9
|
+
organizationId?: number;
|
|
4
10
|
}
|
|
5
11
|
export declare function loadConfig(): Config;
|
|
6
12
|
export declare function loadHttpConfig(): {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,MAAM,eAAe,GAAG,6CAA6C,CAAC;AAEtE,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO;QACL,MAAM;QACN,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,eAAe;KACtD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,eAAe;KACtD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAsD;IAClF,MAAM,UAAU,GAAG,OAAO,CAAC,eAAe,CAAuB,CAAC;IAClE,IAAI,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACtC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,CAAuB,CAAC;IAC3D,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;AACnG,CAAC"}
|
|
@@ -7,6 +7,11 @@ const INCLUDED_PREFIXES = [
|
|
|
7
7
|
'/time-slots',
|
|
8
8
|
'/categories',
|
|
9
9
|
'/organizations/{organizationId}/guests',
|
|
10
|
+
'/organizations/{organizationId}/website-content',
|
|
11
|
+
'/organizations/{organizationId}/branding',
|
|
12
|
+
'/settings',
|
|
13
|
+
'/profile',
|
|
14
|
+
'/campaign-links',
|
|
10
15
|
'/statistics',
|
|
11
16
|
'/organizations/{organizationId}/locations/{locationId}/items/{itemId}/availability',
|
|
12
17
|
];
|
|
@@ -17,6 +22,20 @@ export function shouldIncludeEndpoint(path) {
|
|
|
17
22
|
const stripped = stripApiPrefix(path);
|
|
18
23
|
return INCLUDED_PREFIXES.some((prefix) => stripped.startsWith(prefix));
|
|
19
24
|
}
|
|
25
|
+
// Endpoints inside the included prefixes that don't translate to usable
|
|
26
|
+
// JSON-RPC tools and would only add noise: multipart file uploads, the public
|
|
27
|
+
// click-tracking endpoint, and binary (QR image) responses.
|
|
28
|
+
const EXCLUDED_ENDPOINTS = new Set([
|
|
29
|
+
'POST /rooms/{id}/images',
|
|
30
|
+
'POST /organizations/{organizationId}/branding/logo',
|
|
31
|
+
'POST /organizations/{organizationId}/branding/background',
|
|
32
|
+
'POST /profile/image',
|
|
33
|
+
'POST /campaign-links/track',
|
|
34
|
+
'POST /campaign-links/{id}/generate-qr',
|
|
35
|
+
]);
|
|
36
|
+
function isExcludedEndpoint(method, path) {
|
|
37
|
+
return EXCLUDED_ENDPOINTS.has(`${method.toUpperCase()} ${stripApiPrefix(path)}`);
|
|
38
|
+
}
|
|
20
39
|
function resolveRef(spec, ref) {
|
|
21
40
|
const parts = ref.replace('#/', '').split('/');
|
|
22
41
|
let current = spec;
|
|
@@ -58,6 +77,8 @@ export function parseOpenApiSpec(spec) {
|
|
|
58
77
|
for (const [method, operation] of Object.entries(methods)) {
|
|
59
78
|
if (['get', 'post', 'put', 'patch', 'delete'].indexOf(method) === -1)
|
|
60
79
|
continue;
|
|
80
|
+
if (isExcludedEndpoint(method, path))
|
|
81
|
+
continue;
|
|
61
82
|
const parameters = (operation.parameters || []).map((p) => {
|
|
62
83
|
const resolved = p.$ref ? resolveRef(spec, p.$ref) : p;
|
|
63
84
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-parser.js","sourceRoot":"","sources":["../../src/generator/openapi-parser.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openapi-parser.js","sourceRoot":"","sources":["../../src/generator/openapi-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,15 @@ function generateToolName(endpoint) {
|
|
|
7
7
|
.replace(/^\//, '')
|
|
8
8
|
.replace(/\//g, '_')
|
|
9
9
|
.replace(/-/g, '_');
|
|
10
|
+
// A GET whose path ends in a resource id (…/{id}) fetches a single item →
|
|
11
|
+
// 'get'; otherwise it's a collection → 'list'. This also disambiguates
|
|
12
|
+
// sibling endpoints like GET /…/damages (list) vs GET /…/damages/{id} (get),
|
|
13
|
+
// which previously collapsed to the same auto-generated name.
|
|
14
|
+
const endsWithPathParam = /\/\{[^}]+\}\/?$/.test(path);
|
|
10
15
|
const action = method === 'GET'
|
|
11
|
-
?
|
|
16
|
+
? endsWithPathParam
|
|
17
|
+
? 'get'
|
|
18
|
+
: 'list'
|
|
12
19
|
: method === 'POST'
|
|
13
20
|
? 'create'
|
|
14
21
|
: method === 'PATCH' || method === 'PUT'
|
|
@@ -63,6 +70,17 @@ export function generateTools(endpoints) {
|
|
|
63
70
|
},
|
|
64
71
|
});
|
|
65
72
|
}
|
|
73
|
+
// Safety net: guarantee unique tool names. If any name still collides (e.g.
|
|
74
|
+
// two endpoints that clean to the same path + action), suffix later ones with
|
|
75
|
+
// _2, _3, … so every endpoint stays callable instead of being shadowed.
|
|
76
|
+
const nameCounts = new Map();
|
|
77
|
+
for (const tool of tools) {
|
|
78
|
+
const seen = nameCounts.get(tool.name) ?? 0;
|
|
79
|
+
nameCounts.set(tool.name, seen + 1);
|
|
80
|
+
if (seen > 0) {
|
|
81
|
+
tool.name = `${tool.name}_${seen + 1}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
66
84
|
return tools;
|
|
67
85
|
}
|
|
68
86
|
//# sourceMappingURL=tool-generator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-generator.js","sourceRoot":"","sources":["../../src/generator/tool-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAcpD,SAAS,gBAAgB,CAAC,QAAwB;IAChD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAElC,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,sCAAsC,EAAE,EAAE,CAAC;SACnD,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAEtB,MAAM,MAAM,GACV,MAAM,KAAK,KAAK;QACd,CAAC,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"tool-generator.js","sourceRoot":"","sources":["../../src/generator/tool-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAcpD,SAAS,gBAAgB,CAAC,QAAwB;IAChD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAElC,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,sCAAsC,EAAE,EAAE,CAAC;SACnD,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAEtB,0EAA0E;IAC1E,uEAAuE;IACvE,6EAA6E;IAC7E,8DAA8D;IAC9D,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,MAAM,GACV,MAAM,KAAK,KAAK;QACd,CAAC,CAAC,iBAAiB;YACjB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,MAAM;QACV,CAAC,CAAC,MAAM,KAAK,MAAM;YACjB,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK;gBACtC,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,MAAM,KAAK,QAAQ;oBACnB,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;IAEjC,OAAO,UAAU,OAAO,IAAI,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAA2B;IACvD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAElC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE7D,MAAM,UAAU,GAAwB,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;YACxC,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB;gBAAE,SAAS;YAC9C,IAAI,KAAK,CAAC,EAAE,KAAK,QAAQ;gBAAE,SAAS;YAEpC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;gBACvB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,GAAG,KAAK,CAAC,IAAI,YAAY;aAC5D,CAAC;YACF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CACvC,QAAQ,CAAC,WAAW,CAAC,UAAU,CAChC,EAAE,CAAC;gBACF,UAAU,CAAC,IAAI,CAAC,GAAG;oBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;iBACtC,CAAC;gBACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;oBACd,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;gBACpC,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtB,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,gBAAgB,CAAC,QAAQ,CAAC;YAClD,WAAW,EACT,QAAQ,EAAE,WAAW,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,OAAO;YACnE,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU;gBACV,QAAQ;aACT;SACF,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,8EAA8E;IAC9E,wEAAwE;IACxE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC;QACpC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACzC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateTools } from './tool-generator.js';
|
|
3
|
+
function ep(method, path) {
|
|
4
|
+
return {
|
|
5
|
+
operationId: `${method} ${path}`,
|
|
6
|
+
method,
|
|
7
|
+
path,
|
|
8
|
+
summary: '',
|
|
9
|
+
description: '',
|
|
10
|
+
parameters: [],
|
|
11
|
+
tags: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe('generateTools naming', () => {
|
|
15
|
+
it("uses 'get' for a GET on a single resource (path ends in /{id}) and 'list' for a collection", () => {
|
|
16
|
+
const tools = generateTools([
|
|
17
|
+
ep('GET', '/bookings/{bookingId}/damages'),
|
|
18
|
+
ep('GET', '/bookings/{bookingId}/damages/{id}'),
|
|
19
|
+
]);
|
|
20
|
+
const names = tools.map((t) => t.name);
|
|
21
|
+
expect(names).toContain('rent2b_bookings_damages_list');
|
|
22
|
+
expect(names).toContain('rent2b_bookings_damages_get');
|
|
23
|
+
});
|
|
24
|
+
it('never emits duplicate tool names (dedup safety net)', () => {
|
|
25
|
+
// Two endpoints that clean to the same name + action would collide without dedup.
|
|
26
|
+
const tools = generateTools([
|
|
27
|
+
ep('GET', '/bookings/{bookingId}/damages'),
|
|
28
|
+
ep('GET', '/bookings/{otherId}/damages'),
|
|
29
|
+
]);
|
|
30
|
+
const names = tools.map((t) => t.name);
|
|
31
|
+
expect(new Set(names).size).toBe(names.length);
|
|
32
|
+
expect(names).toEqual(['rent2b_bookings_damages_list', 'rent2b_bookings_damages_list_2']);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=tool-generator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-generator.test.js","sourceRoot":"","sources":["../../src/generator/tool-generator.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,SAAS,EAAE,CAAC,MAAc,EAAE,IAAY;IACtC,OAAO;QACL,WAAW,EAAE,GAAG,MAAM,IAAI,IAAI,EAAE;QAChC,MAAM;QACN,IAAI;QACJ,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,EAAE;QACf,UAAU,EAAE,EAAE;QACd,IAAI,EAAE,EAAE;KACT,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,4FAA4F,EAAE,GAAG,EAAE;QACpG,MAAM,KAAK,GAAG,aAAa,CAAC;YAC1B,EAAE,CAAC,KAAK,EAAE,+BAA+B,CAAC;YAC1C,EAAE,CAAC,KAAK,EAAE,oCAAoC,CAAC;SAChD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,kFAAkF;QAClF,MAAM,KAAK,GAAG,aAAa,CAAC;YAC1B,EAAE,CAAC,KAAK,EAAE,+BAA+B,CAAC;YAC1C,EAAE,CAAC,KAAK,EAAE,6BAA6B,CAAC;SACzC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,8BAA8B,EAAE,gCAAgC,CAAC,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|