ai-cred 1.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/dist/cli/commands/add.d.ts +8 -0
- package/dist/cli/commands/add.js +145 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/get.d.ts +7 -0
- package/dist/cli/commands/get.js +53 -0
- package/dist/cli/commands/get.js.map +1 -0
- package/dist/cli/commands/list.d.ts +7 -0
- package/dist/cli/commands/list.js +118 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/remove.d.ts +7 -0
- package/dist/cli/commands/remove.js +53 -0
- package/dist/cli/commands/remove.js.map +1 -0
- package/dist/cli/commands/update.d.ts +8 -0
- package/dist/cli/commands/update.js +140 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +25 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/display.d.ts +14 -0
- package/dist/cli/utils/display.js +31 -0
- package/dist/cli/utils/display.js.map +1 -0
- package/dist/cli/utils/prompt.d.ts +6 -0
- package/dist/cli/utils/prompt.js +66 -0
- package/dist/cli/utils/prompt.js.map +1 -0
- package/dist/core/audit-logger.d.ts +25 -0
- package/dist/core/audit-logger.js +41 -0
- package/dist/core/audit-logger.js.map +1 -0
- package/dist/core/audit-logger.test.d.ts +1 -0
- package/dist/core/audit-logger.test.js +89 -0
- package/dist/core/audit-logger.test.js.map +1 -0
- package/dist/core/errors.d.ts +23 -0
- package/dist/core/errors.js +78 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/keychain-adapter.d.ts +65 -0
- package/dist/core/keychain-adapter.js +221 -0
- package/dist/core/keychain-adapter.js.map +1 -0
- package/dist/core/keychain-adapter.test.d.ts +1 -0
- package/dist/core/keychain-adapter.test.js +331 -0
- package/dist/core/keychain-adapter.test.js.map +1 -0
- package/dist/e2e/environment-isolation.test.d.ts +1 -0
- package/dist/e2e/environment-isolation.test.js +112 -0
- package/dist/e2e/environment-isolation.test.js.map +1 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +26 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/tools.d.ts +10 -0
- package/dist/server/tools.js +230 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/server/tools.test.d.ts +7 -0
- package/dist/server/tools.test.js +606 -0
- package/dist/server/tools.test.js.map +1 -0
- package/dist/types/credential.d.ts +77 -0
- package/dist/types/credential.js +46 -0
- package/dist/types/credential.js.map +1 -0
- package/dist/types/credential.test.d.ts +1 -0
- package/dist/types/credential.test.js +316 -0
- package/dist/types/credential.test.js.map +1 -0
- package/dist/utils/validation.d.ts +15 -0
- package/dist/utils/validation.js +34 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +139 -0
- package/dist/utils/validation.test.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdtempSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { KeychainAdapter, KeychainError } from '../core/keychain-adapter.js';
|
|
7
|
+
const SECURITY_BINARY = '/usr/bin/security';
|
|
8
|
+
const hasSecurityBinary = existsSync(SECURITY_BINARY);
|
|
9
|
+
describe.skipIf(!hasSecurityBinary)('E2E: Environment Isolation', () => {
|
|
10
|
+
let adapter;
|
|
11
|
+
let keychainPath;
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'ai-cred-e2e-'));
|
|
14
|
+
keychainPath = join(tempDir, 'ai-cred-e2e-test.keychain-db');
|
|
15
|
+
adapter = new KeychainAdapter(keychainPath);
|
|
16
|
+
await adapter.initialize();
|
|
17
|
+
});
|
|
18
|
+
afterAll(async () => {
|
|
19
|
+
try {
|
|
20
|
+
await adapter.destroy();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Keychain may already be cleaned up
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
it('prod and staging credentials are isolated', async () => {
|
|
27
|
+
await adapter.store('prod', 'jenkins', {
|
|
28
|
+
type: 'jenkins',
|
|
29
|
+
url: 'https://prod.ci.com',
|
|
30
|
+
username: 'admin',
|
|
31
|
+
apiToken: 'prod-token-123',
|
|
32
|
+
});
|
|
33
|
+
await adapter.store('staging', 'jenkins', {
|
|
34
|
+
type: 'jenkins',
|
|
35
|
+
url: 'https://staging.ci.com',
|
|
36
|
+
username: 'deploy',
|
|
37
|
+
apiToken: 'staging-token-456',
|
|
38
|
+
});
|
|
39
|
+
const prodCred = await adapter.get('prod', 'jenkins');
|
|
40
|
+
expect(prodCred.type).toBe('jenkins');
|
|
41
|
+
if (prodCred.type === 'jenkins') {
|
|
42
|
+
expect(prodCred.url).toBe('https://prod.ci.com');
|
|
43
|
+
expect(prodCred.apiToken).toBe('prod-token-123');
|
|
44
|
+
// Prod NEVER returns staging data
|
|
45
|
+
expect(prodCred.url).not.toBe('https://staging.ci.com');
|
|
46
|
+
expect(prodCred.apiToken).not.toBe('staging-token-456');
|
|
47
|
+
}
|
|
48
|
+
const stagingCred = await adapter.get('staging', 'jenkins');
|
|
49
|
+
expect(stagingCred.type).toBe('jenkins');
|
|
50
|
+
if (stagingCred.type === 'jenkins') {
|
|
51
|
+
expect(stagingCred.url).toBe('https://staging.ci.com');
|
|
52
|
+
expect(stagingCred.apiToken).toBe('staging-token-456');
|
|
53
|
+
// Staging NEVER returns prod data
|
|
54
|
+
expect(stagingCred.url).not.toBe('https://prod.ci.com');
|
|
55
|
+
expect(stagingCred.apiToken).not.toBe('prod-token-123');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
it('deleting one environment does not affect the other', async () => {
|
|
59
|
+
// Ensure both exist
|
|
60
|
+
await adapter.store('prod', 'jenkins', {
|
|
61
|
+
type: 'jenkins',
|
|
62
|
+
url: 'https://prod.ci.com',
|
|
63
|
+
username: 'admin',
|
|
64
|
+
apiToken: 'prod-token-123',
|
|
65
|
+
});
|
|
66
|
+
await adapter.store('staging', 'jenkins', {
|
|
67
|
+
type: 'jenkins',
|
|
68
|
+
url: 'https://staging.ci.com',
|
|
69
|
+
username: 'deploy',
|
|
70
|
+
apiToken: 'staging-token-456',
|
|
71
|
+
});
|
|
72
|
+
// Delete staging
|
|
73
|
+
await adapter.delete('staging', 'jenkins');
|
|
74
|
+
// Prod still accessible
|
|
75
|
+
const prodCred = await adapter.get('prod', 'jenkins');
|
|
76
|
+
expect(prodCred.type).toBe('jenkins');
|
|
77
|
+
if (prodCred.type === 'jenkins') {
|
|
78
|
+
expect(prodCred.url).toBe('https://prod.ci.com');
|
|
79
|
+
}
|
|
80
|
+
// Staging is gone
|
|
81
|
+
await expect(adapter.get('staging', 'jenkins')).rejects.toThrow(KeychainError);
|
|
82
|
+
try {
|
|
83
|
+
await adapter.get('staging', 'jenkins');
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
expect(err).toBeInstanceOf(KeychainError);
|
|
87
|
+
expect(err.exitCode).toBe(44);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
it('list shows both environments as separate entries', async () => {
|
|
91
|
+
// Store both
|
|
92
|
+
await adapter.store('prod', 'jenkins', {
|
|
93
|
+
type: 'jenkins',
|
|
94
|
+
url: 'https://prod.ci.com',
|
|
95
|
+
username: 'admin',
|
|
96
|
+
apiToken: 'prod-token-123',
|
|
97
|
+
});
|
|
98
|
+
await adapter.store('staging', 'jenkins', {
|
|
99
|
+
type: 'jenkins',
|
|
100
|
+
url: 'https://staging.ci.com',
|
|
101
|
+
username: 'deploy',
|
|
102
|
+
apiToken: 'staging-token-456',
|
|
103
|
+
});
|
|
104
|
+
const entries = await adapter.list();
|
|
105
|
+
const jenkinsEntries = entries.filter((e) => e.service === 'jenkins');
|
|
106
|
+
expect(jenkinsEntries.length).toBeGreaterThanOrEqual(2);
|
|
107
|
+
const environments = jenkinsEntries.map((e) => e.environment).sort();
|
|
108
|
+
expect(environments).toContain('prod');
|
|
109
|
+
expect(environments).toContain('staging');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=environment-isolation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-isolation.test.js","sourceRoot":"","sources":["../../src/e2e/environment-isolation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE7E,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAC5C,MAAM,iBAAiB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;AAEtD,QAAQ,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,4BAA4B,EAAE,GAAG,EAAE;IACrE,IAAI,OAAwB,CAAC;IAC7B,IAAI,YAAoB,CAAC;IAEzB,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;QAC5D,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;QAC7D,OAAO,GAAG,IAAI,eAAe,CAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE;YACrC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,qBAAqB;YAC1B,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,gBAAgB;SAC3B,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE;YACxC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,wBAAwB;YAC7B,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,mBAAmB;SAC9B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACtD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACjD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACjD,kCAAkC;YAClC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACxD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,WAAW,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACvD,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACvD,kCAAkC;YAClC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;YACxD,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,oBAAoB;QACpB,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE;YACrC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,qBAAqB;YAC1B,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,gBAAgB;SAC3B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE;YACxC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,wBAAwB;YAC7B,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,mBAAmB;SAC9B,CAAC,CAAC;QAEH,iBAAiB;QACjB,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAE3C,wBAAwB;QACxB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACtD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QACnD,CAAC;QAED,kBAAkB;QAClB,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC/E,IAAI,CAAC;YACH,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YAC1C,MAAM,CAAE,GAAqB,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,aAAa;QACb,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE;YACrC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,qBAAqB;YAC1B,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,gBAAgB;SAC3B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE;YACxC,IAAI,EAAE,SAAS;YACf,GAAG,EAAE,wBAAwB;YAC7B,QAAQ,EAAE,QAAQ;YAClB,QAAQ,EAAE,mBAAmB;SAC9B,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;QAEtE,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,YAAY,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server entry point for ai-cred.
|
|
4
|
+
*
|
|
5
|
+
* Starts an MCP server over stdio transport with all credential management tools.
|
|
6
|
+
* CRITICAL: No console.log() -- stdio is the JSON-RPC channel.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
10
|
+
import { registerTools } from './tools.js';
|
|
11
|
+
(async function main() {
|
|
12
|
+
try {
|
|
13
|
+
const server = new McpServer({
|
|
14
|
+
name: 'ai-cred',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
});
|
|
17
|
+
registerTools(server);
|
|
18
|
+
const transport = new StdioServerTransport();
|
|
19
|
+
await server.connect(transport);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error('ai-cred MCP server fatal error:', err);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,CAAC,KAAK,UAAU,IAAI;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;YAC3B,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,aAAa,CAAC,MAAM,CAAC,CAAC;QAEtB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool registrations for ai-cred credential management.
|
|
3
|
+
*
|
|
4
|
+
* All five tools: store_credential, get_credential, list_credentials, delete_credential, find_credential.
|
|
5
|
+
*
|
|
6
|
+
* CRITICAL: No console.log() anywhere -- stdio is the JSON-RPC channel.
|
|
7
|
+
* All errors are returned as text content, never thrown.
|
|
8
|
+
*/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
export declare function registerTools(server: McpServer): void;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool registrations for ai-cred credential management.
|
|
3
|
+
*
|
|
4
|
+
* All five tools: store_credential, get_credential, list_credentials, delete_credential, find_credential.
|
|
5
|
+
*
|
|
6
|
+
* CRITICAL: No console.log() anywhere -- stdio is the JSON-RPC channel.
|
|
7
|
+
* All errors are returned as text content, never thrown.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { KeychainAdapter, KeychainError } from '../core/keychain-adapter.js';
|
|
11
|
+
import { CredentialSchema } from '../types/credential.js';
|
|
12
|
+
import { ServiceNameSchema } from '../utils/validation.js';
|
|
13
|
+
import { auditLog } from '../core/audit-logger.js';
|
|
14
|
+
const adapter = new KeychainAdapter();
|
|
15
|
+
let initialized = false;
|
|
16
|
+
async function ensureInitialized() {
|
|
17
|
+
if (!initialized) {
|
|
18
|
+
await adapter.initialize();
|
|
19
|
+
initialized = true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function errorResult(message) {
|
|
23
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
24
|
+
}
|
|
25
|
+
function textResult(message) {
|
|
26
|
+
return { content: [{ type: 'text', text: message }] };
|
|
27
|
+
}
|
|
28
|
+
export function registerTools(server) {
|
|
29
|
+
// -------------------------------------------------------------------------
|
|
30
|
+
// Tool 1: store_credential (MCP-02)
|
|
31
|
+
// -------------------------------------------------------------------------
|
|
32
|
+
server.registerTool('store_credential', {
|
|
33
|
+
description: 'Store a credential in the macOS Keychain',
|
|
34
|
+
inputSchema: {
|
|
35
|
+
service: z.string().describe('Service name (letters, numbers, hyphens, underscores)'),
|
|
36
|
+
environment: z.enum(['prod', 'dev', 'staging', 'global']).describe('Target environment'),
|
|
37
|
+
type: z.enum(['ssh', 'jenkins', 'portainer', 'aws', 'api-key']).describe('Credential type'),
|
|
38
|
+
// SSH fields
|
|
39
|
+
host: z.string().optional().describe('SSH host'),
|
|
40
|
+
port: z.number().optional().describe('SSH port (default 22)'),
|
|
41
|
+
username: z.string().optional().describe('Username for SSH or Jenkins'),
|
|
42
|
+
password: z.string().optional().describe('Password for SSH'),
|
|
43
|
+
keyPath: z.string().optional().describe('SSH key path'),
|
|
44
|
+
// Jenkins / Portainer fields
|
|
45
|
+
url: z.string().optional().describe('URL for Jenkins, Portainer, or API key'),
|
|
46
|
+
apiToken: z.string().optional().describe('API token for Jenkins or Portainer'),
|
|
47
|
+
// AWS fields
|
|
48
|
+
accessKeyId: z.string().optional().describe('AWS access key ID'),
|
|
49
|
+
secretAccessKey: z.string().optional().describe('AWS secret access key'),
|
|
50
|
+
region: z.string().optional().describe('AWS region'),
|
|
51
|
+
profile: z.string().optional().describe('AWS profile name'),
|
|
52
|
+
// API key fields
|
|
53
|
+
key: z.string().optional().describe('API key value'),
|
|
54
|
+
notes: z.string().optional().describe('Optional notes'),
|
|
55
|
+
},
|
|
56
|
+
}, async ({ service, environment, type, ...fields }) => {
|
|
57
|
+
try {
|
|
58
|
+
await ensureInitialized();
|
|
59
|
+
// Validate service name
|
|
60
|
+
const serviceResult = ServiceNameSchema.safeParse(service);
|
|
61
|
+
if (!serviceResult.success) {
|
|
62
|
+
return errorResult(`Invalid service name: ${serviceResult.error.issues[0]?.message ?? 'validation failed'}`);
|
|
63
|
+
}
|
|
64
|
+
// Build credential object, removing undefined keys
|
|
65
|
+
const credentialObj = { type };
|
|
66
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
67
|
+
if (v !== undefined) {
|
|
68
|
+
credentialObj[k] = v;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Validate with CredentialSchema
|
|
72
|
+
const result = CredentialSchema.safeParse(credentialObj);
|
|
73
|
+
if (!result.success) {
|
|
74
|
+
return errorResult(`Invalid credential: ${result.error.issues.map((i) => i.message).join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
await adapter.store(environment, service, result.data);
|
|
77
|
+
await auditLog({ operation: 'store', service, environment });
|
|
78
|
+
return textResult(`Stored ${type} credential '${service}' in ${environment}`);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err instanceof KeychainError) {
|
|
82
|
+
return errorResult(err.message);
|
|
83
|
+
}
|
|
84
|
+
return errorResult('Unexpected error storing credential');
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
// Tool 2: get_credential (MCP-03, META-01)
|
|
89
|
+
// -------------------------------------------------------------------------
|
|
90
|
+
server.registerTool('get_credential', {
|
|
91
|
+
description: 'Retrieve a credential from the macOS Keychain',
|
|
92
|
+
inputSchema: {
|
|
93
|
+
service: z.string().describe('Service name'),
|
|
94
|
+
environment: z.enum(['prod', 'dev', 'staging', 'global']).describe('Target environment'),
|
|
95
|
+
},
|
|
96
|
+
}, async ({ service, environment }) => {
|
|
97
|
+
try {
|
|
98
|
+
await ensureInitialized();
|
|
99
|
+
const credential = await adapter.get(environment, service);
|
|
100
|
+
// META-01: Re-store to bump macOS mdat timestamp (last-accessed tracking)
|
|
101
|
+
await adapter.store(environment, service, credential);
|
|
102
|
+
await auditLog({ operation: 'get', service, environment });
|
|
103
|
+
return textResult(JSON.stringify(credential, null, 2));
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (err instanceof KeychainError) {
|
|
107
|
+
if (err.exitCode === 44) {
|
|
108
|
+
return errorResult(`No credential found for '${service}' in ${environment}`);
|
|
109
|
+
}
|
|
110
|
+
return errorResult(err.message);
|
|
111
|
+
}
|
|
112
|
+
return errorResult('Unexpected error retrieving credential');
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
// -------------------------------------------------------------------------
|
|
116
|
+
// Tool 3: list_credentials (MCP-04)
|
|
117
|
+
// -------------------------------------------------------------------------
|
|
118
|
+
server.registerTool('list_credentials', {
|
|
119
|
+
description: 'List all stored credentials (metadata only, no secrets)',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
environment: z.enum(['prod', 'dev', 'staging', 'global']).optional().describe('Filter by environment (optional)'),
|
|
122
|
+
},
|
|
123
|
+
}, async ({ environment }) => {
|
|
124
|
+
try {
|
|
125
|
+
await ensureInitialized();
|
|
126
|
+
let items = await adapter.list();
|
|
127
|
+
if (environment) {
|
|
128
|
+
items = items.filter((i) => i.environment === environment);
|
|
129
|
+
}
|
|
130
|
+
if (items.length === 0) {
|
|
131
|
+
return textResult('No credentials found');
|
|
132
|
+
}
|
|
133
|
+
// Fetch notes per item (notes are in the credential blob, not metadata)
|
|
134
|
+
const notesMap = new Map();
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
try {
|
|
137
|
+
const cred = await adapter.get(item.environment, item.service);
|
|
138
|
+
if (cred && 'notes' in cred && cred.notes) {
|
|
139
|
+
notesMap.set(`${item.environment}/${item.service}`, cred.notes);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Skip if can't fetch — notes just won't show
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Format as text table
|
|
147
|
+
const header = 'Service | Environment | Type | Created | Last Accessed | Notes';
|
|
148
|
+
const divider = '---------------- | ----------- | ---------- | -------------------- | -------------------- | -----';
|
|
149
|
+
const rows = items.map((i) => {
|
|
150
|
+
const svc = i.service.padEnd(16);
|
|
151
|
+
const env = i.environment.padEnd(11);
|
|
152
|
+
const typ = i.type.padEnd(10);
|
|
153
|
+
const cre = (i.createdAt || 'N/A').padEnd(20);
|
|
154
|
+
const mod = (i.modifiedAt || 'N/A').padEnd(20);
|
|
155
|
+
const notes = notesMap.get(`${i.environment}/${i.service}`) || '';
|
|
156
|
+
return `${svc} | ${env} | ${typ} | ${cre} | ${mod} | ${notes}`;
|
|
157
|
+
});
|
|
158
|
+
const table = [header, divider, ...rows].join('\n');
|
|
159
|
+
await auditLog({ operation: 'list', service: '*', environment: environment ?? '*' });
|
|
160
|
+
return textResult(table);
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (err instanceof KeychainError) {
|
|
164
|
+
return errorResult(err.message);
|
|
165
|
+
}
|
|
166
|
+
return errorResult('Unexpected error listing credentials');
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
// -------------------------------------------------------------------------
|
|
170
|
+
// Tool 4: delete_credential (MCP-05)
|
|
171
|
+
// -------------------------------------------------------------------------
|
|
172
|
+
server.registerTool('delete_credential', {
|
|
173
|
+
description: 'Delete a credential from the macOS Keychain',
|
|
174
|
+
inputSchema: {
|
|
175
|
+
service: z.string().describe('Service name'),
|
|
176
|
+
environment: z.enum(['prod', 'dev', 'staging', 'global']).describe('Target environment'),
|
|
177
|
+
},
|
|
178
|
+
}, async ({ service, environment }) => {
|
|
179
|
+
try {
|
|
180
|
+
await ensureInitialized();
|
|
181
|
+
await adapter.delete(environment, service);
|
|
182
|
+
await auditLog({ operation: 'delete', service, environment });
|
|
183
|
+
return textResult(`Deleted credential '${service}' from ${environment}`);
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (err instanceof KeychainError) {
|
|
187
|
+
if (err.exitCode === 44) {
|
|
188
|
+
return errorResult(`No credential found for '${service}' in ${environment}`);
|
|
189
|
+
}
|
|
190
|
+
return errorResult(err.message);
|
|
191
|
+
}
|
|
192
|
+
return errorResult('Unexpected error deleting credential');
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
// -------------------------------------------------------------------------
|
|
196
|
+
// Tool 5: find_credential (MCP-06)
|
|
197
|
+
// -------------------------------------------------------------------------
|
|
198
|
+
server.registerTool('find_credential', {
|
|
199
|
+
description: 'Search credentials by partial service name (case-insensitive substring match)',
|
|
200
|
+
inputSchema: {
|
|
201
|
+
query: z.string().min(1).describe('Partial service name to search for'),
|
|
202
|
+
environment: z.enum(['prod', 'dev', 'staging', 'global']).optional()
|
|
203
|
+
.describe('Filter by environment (optional)'),
|
|
204
|
+
},
|
|
205
|
+
}, async ({ query, environment }) => {
|
|
206
|
+
try {
|
|
207
|
+
await ensureInitialized();
|
|
208
|
+
let items = await adapter.list();
|
|
209
|
+
// Case-insensitive substring match on service name
|
|
210
|
+
items = items.filter((item) => item.service.toLowerCase().includes(query.toLowerCase()));
|
|
211
|
+
// Optional environment filter
|
|
212
|
+
if (environment) {
|
|
213
|
+
items = items.filter((item) => item.environment === environment);
|
|
214
|
+
}
|
|
215
|
+
if (items.length === 0) {
|
|
216
|
+
return textResult(`No credentials matching '${query}'`);
|
|
217
|
+
}
|
|
218
|
+
const lines = items.map((item) => `${item.service} (${item.environment}) [${item.type}]`);
|
|
219
|
+
await auditLog({ operation: 'find', service: query, environment: environment ?? '*' });
|
|
220
|
+
return textResult(lines.join('\n'));
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
if (err instanceof KeychainError) {
|
|
224
|
+
return errorResult(err.message);
|
|
225
|
+
}
|
|
226
|
+
return errorResult('Unexpected error searching credentials');
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/server/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAqB,MAAM,wBAAwB,CAAC;AAE7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;AACtC,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,KAAK,UAAU,iBAAiB;IAC9B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3B,WAAW,GAAG,IAAI,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAa,EAAE,CAAC;AACzF,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAiB;IAC7C,4EAA4E;IAC5E,oCAAoC;IACpC,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EAAE,0CAA0C;QACvD,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uDAAuD,CAAC;YACrF,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;YACxF,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAC3F,aAAa;YACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;YAChD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAC7D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;YACvE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC5D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvD,6BAA6B;YAC7B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YAC7E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;YAC9E,aAAa;YACb,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAChE,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACxE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;YACpD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3D,iBAAiB;YACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;YACpD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;SACxD;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE;QAClD,IAAI,CAAC;YACH,MAAM,iBAAiB,EAAE,CAAC;YAE1B,wBAAwB;YACxB,MAAM,aAAa,GAAG,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC3D,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC3B,OAAO,WAAW,CAAC,yBAAyB,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,mBAAmB,EAAE,CAAC,CAAC;YAC/G,CAAC;YAED,mDAAmD;YACnD,MAAM,aAAa,GAA4B,EAAE,IAAI,EAAE,CAAC;YACxD,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;oBACpB,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,iCAAiC;YACjC,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,WAAW,CAAC,uBAAuB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzH,CAAC;YAED,MAAM,OAAO,CAAC,KAAK,CAAC,WAA0B,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACtE,MAAM,QAAQ,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC7D,OAAO,UAAU,CAAC,UAAU,IAAI,gBAAgB,OAAO,QAAQ,WAAW,EAAE,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC,qCAAqC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CACF,CAAC;IAEF,4EAA4E;IAC5E,2CAA2C;IAC3C,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,WAAW,EAAE,+CAA+C;QAC5D,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC5C,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;SACzF;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,iBAAiB,EAAE,CAAC;YAE1B,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAA0B,EAAE,OAAO,CAAC,CAAC;YAE1E,0EAA0E;YAC1E,MAAM,OAAO,CAAC,KAAK,CAAC,WAA0B,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YAErE,MAAM,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC3D,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;oBACxB,OAAO,WAAW,CAAC,4BAA4B,OAAO,QAAQ,WAAW,EAAE,CAAC,CAAC;gBAC/E,CAAC;gBACD,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC,wCAAwC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CACF,CAAC;IAEF,4EAA4E;IAC5E,oCAAoC;IACpC,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EAAE,yDAAyD;QACtE,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;SAClH;KACF,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,iBAAiB,EAAE,CAAC;YAE1B,IAAI,KAAK,GAAyB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YAEvD,IAAI,WAAW,EAAE,CAAC;gBAChB,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC,CAAC;YAC7D,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC5C,CAAC;YAED,wEAAwE;YACxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;YAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;oBAC/D,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;oBAClE,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,8CAA8C;gBAChD,CAAC;YACH,CAAC;YAED,uBAAuB;YACvB,MAAM,MAAM,GAAG,mGAAmG,CAAC;YACnH,MAAM,OAAO,GAAG,mGAAmG,CAAC;YACpH,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACjC,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrC,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC9B,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC9C,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC;gBAClE,OAAO,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,KAAK,EAAE,CAAC;YACjE,CAAC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpD,MAAM,QAAQ,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;YACrF,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC,sCAAsC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CACF,CAAC;IAEF,4EAA4E;IAC5E,qCAAqC;IACrC,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,WAAW,EAAE,6CAA6C;QAC1D,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC5C,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;SACzF;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,iBAAiB,EAAE,CAAC;YAE1B,MAAM,OAAO,CAAC,MAAM,CAAC,WAA0B,EAAE,OAAO,CAAC,CAAC;YAC1D,MAAM,QAAQ,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YAC9D,OAAO,UAAU,CAAC,uBAAuB,OAAO,UAAU,WAAW,EAAE,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;oBACxB,OAAO,WAAW,CAAC,4BAA4B,OAAO,QAAQ,WAAW,EAAE,CAAC,CAAC;gBAC/E,CAAC;gBACD,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC,sCAAsC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CACF,CAAC;IAEF,4EAA4E;IAC5E,mCAAmC;IACnC,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EAAE,+EAA+E;QAC5F,WAAW,EAAE;YACX,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;YACvE,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE;iBACjE,QAAQ,CAAC,kCAAkC,CAAC;SAChD;KACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,iBAAiB,EAAE,CAAC;YAE1B,IAAI,KAAK,GAAyB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YAEvD,mDAAmD;YACnD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5B,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CACzD,CAAC;YAEF,8BAA8B;YAC9B,IAAI,WAAW,EAAE,CAAC;gBAChB,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,KAAK,WAAW,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,UAAU,CAAC,4BAA4B,KAAK,GAAG,CAAC,CAAC;YAC1D,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CACrB,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,WAAW,MAAM,IAAI,CAAC,IAAI,GAAG,CACjE,CAAC;YACF,MAAM,QAAQ,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;YACvF,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,OAAO,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC,wCAAwC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|