@tankpkg/mcp-server 0.4.2

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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +30 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/api-client.d.ts +44 -0
  8. package/dist/lib/api-client.d.ts.map +1 -0
  9. package/dist/lib/api-client.js +78 -0
  10. package/dist/lib/api-client.js.map +1 -0
  11. package/dist/lib/config.d.ts +25 -0
  12. package/dist/lib/config.d.ts.map +1 -0
  13. package/dist/lib/config.js +59 -0
  14. package/dist/lib/config.js.map +1 -0
  15. package/dist/lib/packer.d.ts +14 -0
  16. package/dist/lib/packer.d.ts.map +1 -0
  17. package/dist/lib/packer.js +191 -0
  18. package/dist/lib/packer.js.map +1 -0
  19. package/dist/tools/login.d.ts +3 -0
  20. package/dist/tools/login.d.ts.map +1 -0
  21. package/dist/tools/login.js +104 -0
  22. package/dist/tools/login.js.map +1 -0
  23. package/dist/tools/publish-skill.d.ts +3 -0
  24. package/dist/tools/publish-skill.d.ts.map +1 -0
  25. package/dist/tools/publish-skill.js +166 -0
  26. package/dist/tools/publish-skill.js.map +1 -0
  27. package/dist/tools/scan-skill.d.ts +3 -0
  28. package/dist/tools/scan-skill.d.ts.map +1 -0
  29. package/dist/tools/scan-skill.js +148 -0
  30. package/dist/tools/scan-skill.js.map +1 -0
  31. package/dist/tools/search-skills.d.ts +3 -0
  32. package/dist/tools/search-skills.d.ts.map +1 -0
  33. package/dist/tools/search-skills.js +54 -0
  34. package/dist/tools/search-skills.js.map +1 -0
  35. package/dist/tools/skill-info.d.ts +3 -0
  36. package/dist/tools/skill-info.d.ts.map +1 -0
  37. package/dist/tools/skill-info.js +88 -0
  38. package/dist/tools/skill-info.js.map +1 -0
  39. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tank Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @tankpkg/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for Tank - scan and publish AI agent skills directly from your editor.
4
+
5
+ ## Features
6
+
7
+ - **scan-skill** - Scan a skill directory for security issues
8
+ - **publish-skill** - Publish a skill to the Tank registry (with dry-run support)
9
+ - **search-skills** - Search the Tank registry for skills
10
+ - **skill-info** - Get detailed information about a specific skill
11
+ - **login** - Authenticate with Tank via GitHub OAuth
12
+
13
+ ## Installation
14
+
15
+ ### Claude Code
16
+
17
+ Add to `.claude/settings.json` or your project's `.mcp.json`:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "tank": {
23
+ "command": "npx",
24
+ "args": ["-y", "@tankpkg/mcp-server"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### Cursor
31
+
32
+ Add to `~/.cursor/mcp.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "tank": {
38
+ "command": "npx",
39
+ "args": ["-y", "@tankpkg/mcp-server"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### VS Code (Copilot)
46
+
47
+ Add to `.vscode/mcp.json`:
48
+
49
+ ```json
50
+ {
51
+ "servers": {
52
+ "tank": {
53
+ "command": "npx",
54
+ "args": ["-y", "@tankpkg/mcp-server"]
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Authentication
61
+
62
+ The MCP server shares authentication with the Tank CLI. If you've already run `tank login`, you're authenticated!
63
+
64
+ Alternatively, set the `TANK_TOKEN` environment variable:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "tank": {
70
+ "command": "npx",
71
+ "args": ["-y", "@tankpkg/mcp-server"],
72
+ "env": {
73
+ "TANK_TOKEN": "tank_your_token_here"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Usage Examples
81
+
82
+ Once configured, talk to your AI agent naturally:
83
+
84
+ ### Scan a skill for security issues
85
+
86
+ ```
87
+ "Scan my skill in the ./my-skill folder for security issues"
88
+ ```
89
+
90
+ ### Publish a skill
91
+
92
+ ```
93
+ "Publish my-skill as a public package"
94
+ ```
95
+
96
+ ### Dry run before publishing
97
+
98
+ ```
99
+ "Do a dry run publish of my skill to check if everything looks good"
100
+ ```
101
+
102
+ ### Search for skills
103
+
104
+ ```
105
+ "Search Tank for code review skills"
106
+ ```
107
+
108
+ ### Get skill info
109
+
110
+ ```
111
+ "Get info about @tank/code-review skill"
112
+ ```
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Install dependencies
118
+ pnpm install
119
+
120
+ # Build
121
+ pnpm build
122
+
123
+ # Run tests
124
+ pnpm test
125
+
126
+ # Start the server (stdio mode)
127
+ pnpm start
128
+ ```
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ // Import tools
5
+ import { registerLoginTool } from './tools/login.js';
6
+ import { registerSearchSkillsTool } from './tools/search-skills.js';
7
+ import { registerSkillInfoTool } from './tools/skill-info.js';
8
+ import { registerScanSkillTool } from './tools/scan-skill.js';
9
+ import { registerPublishSkillTool } from './tools/publish-skill.js';
10
+ // Create MCP server instance
11
+ const server = new McpServer({
12
+ name: 'tank',
13
+ version: '0.1.0',
14
+ });
15
+ // Register all tools
16
+ registerLoginTool(server);
17
+ registerSearchSkillsTool(server);
18
+ registerSkillInfoTool(server);
19
+ registerScanSkillTool(server);
20
+ registerPublishSkillTool(server);
21
+ // Start stdio transport
22
+ async function main() {
23
+ const transport = new StdioServerTransport();
24
+ await server.connect(transport);
25
+ }
26
+ main().catch((error) => {
27
+ console.error('MCP server error:', error);
28
+ process.exit(1);
29
+ });
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,6BAA6B;AAC7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,qBAAqB;AACrB,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC9B,wBAAwB,CAAC,MAAM,CAAC,CAAC;AAEjC,wBAAwB;AACxB,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,44 @@
1
+ export interface ApiClientOptions {
2
+ configDir?: string;
3
+ }
4
+ /**
5
+ * Tank API client for MCP server.
6
+ */
7
+ export declare class TankApiClient {
8
+ private config;
9
+ constructor(options?: ApiClientOptions);
10
+ /**
11
+ * Get the base URL for the Tank API.
12
+ */
13
+ get baseUrl(): string;
14
+ /**
15
+ * Get the auth token (if available).
16
+ */
17
+ get token(): string | undefined;
18
+ /**
19
+ * Check if authenticated.
20
+ */
21
+ get isAuthenticated(): boolean;
22
+ /**
23
+ * Make an authenticated API request.
24
+ */
25
+ fetch<T>(path: string, options?: RequestInit): Promise<{
26
+ data: T;
27
+ ok: true;
28
+ } | {
29
+ error: string;
30
+ status: number;
31
+ ok: false;
32
+ }>;
33
+ /**
34
+ * Verify current auth token is valid.
35
+ */
36
+ verifyAuth(): Promise<{
37
+ valid: boolean;
38
+ user?: {
39
+ name: string | null;
40
+ email: string | null;
41
+ };
42
+ }>;
43
+ }
44
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAa;gBAEf,OAAO,GAAE,gBAAqB;IAI1C;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED;;OAEG;IACG,KAAK,CAAC,CAAC,EACX,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,KAAK,CAAA;KAAE,CAAC;IAqChF;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KAAE,CAAC;CAetG"}
@@ -0,0 +1,78 @@
1
+ import { getConfig } from './config.js';
2
+ /**
3
+ * Tank API client for MCP server.
4
+ */
5
+ export class TankApiClient {
6
+ config;
7
+ constructor(options = {}) {
8
+ this.config = getConfig(options.configDir);
9
+ }
10
+ /**
11
+ * Get the base URL for the Tank API.
12
+ */
13
+ get baseUrl() {
14
+ return this.config.registry;
15
+ }
16
+ /**
17
+ * Get the auth token (if available).
18
+ */
19
+ get token() {
20
+ return this.config.token;
21
+ }
22
+ /**
23
+ * Check if authenticated.
24
+ */
25
+ get isAuthenticated() {
26
+ return !!this.config.token;
27
+ }
28
+ /**
29
+ * Make an authenticated API request.
30
+ */
31
+ async fetch(path, options = {}) {
32
+ const url = `${this.baseUrl}${path}`;
33
+ const headers = {
34
+ 'Content-Type': 'application/json',
35
+ ...options.headers,
36
+ };
37
+ if (this.config.token) {
38
+ headers['Authorization'] = `Bearer ${this.config.token}`;
39
+ }
40
+ try {
41
+ const response = await fetch(url, {
42
+ ...options,
43
+ headers,
44
+ });
45
+ if (!response.ok) {
46
+ const body = await response.json().catch(() => ({}));
47
+ return {
48
+ error: body.error ?? response.statusText,
49
+ status: response.status,
50
+ ok: false,
51
+ };
52
+ }
53
+ const data = await response.json();
54
+ return { data, ok: true };
55
+ }
56
+ catch (err) {
57
+ return {
58
+ error: err instanceof Error ? err.message : 'Network error',
59
+ status: 0,
60
+ ok: false,
61
+ };
62
+ }
63
+ }
64
+ /**
65
+ * Verify current auth token is valid.
66
+ */
67
+ async verifyAuth() {
68
+ if (!this.config.token) {
69
+ return { valid: false };
70
+ }
71
+ const result = await this.fetch('/api/v1/auth/whoami');
72
+ if (result.ok) {
73
+ return { valid: true, user: result.data.user };
74
+ }
75
+ return { valid: false };
76
+ }
77
+ }
78
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAmB,MAAM,aAAa,CAAC;AAMzD;;GAEG;AACH,MAAM,OAAO,aAAa;IAChB,MAAM,CAAa;IAE3B,YAAY,UAA4B,EAAE;QACxC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,IAAI,eAAe;QACjB,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CACT,IAAY,EACZ,UAAuB,EAAE;QAEzB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,GAAI,OAAO,CAAC,OAAkC;SAC/C,CAAC;QAEF,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,GAAG,OAAO;gBACV,OAAO;aACR,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACrD,OAAO;oBACL,KAAK,EAAG,IAA2B,CAAC,KAAK,IAAI,QAAQ,CAAC,UAAU;oBAChE,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,EAAE,EAAE,KAAK;iBACV,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAO,CAAC;YACxC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC3D,MAAM,EAAE,CAAC;gBACT,EAAE,EAAE,KAAK;aACV,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC1B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,qBAAqB,CACtB,CAAC;QAEF,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACjD,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,25 @@
1
+ export interface TankConfig {
2
+ token?: string;
3
+ user?: {
4
+ name: string;
5
+ email: string;
6
+ };
7
+ registry: string;
8
+ }
9
+ /**
10
+ * Get the path to the tank config directory.
11
+ */
12
+ export declare function getConfigDir(configDir?: string): string;
13
+ /**
14
+ * Get the path to the tank config file.
15
+ */
16
+ export declare function getConfigPath(configDir?: string): string;
17
+ /**
18
+ * Read the tank config file. Returns defaults if file doesn't exist.
19
+ */
20
+ export declare function getConfig(configDir?: string): TankConfig;
21
+ /**
22
+ * Write config to disk. Merges with existing config.
23
+ */
24
+ export declare function setConfig(partial: Partial<TankConfig>, configDir?: string): void;
25
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,CAoBxD;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,EAC5B,SAAS,CAAC,EAAE,MAAM,GACjB,IAAI,CAeN"}
@@ -0,0 +1,59 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const DEFAULT_CONFIG = {
5
+ registry: 'https://tankpkg.dev',
6
+ };
7
+ /**
8
+ * Get the path to the tank config directory.
9
+ */
10
+ export function getConfigDir(configDir) {
11
+ return configDir ?? path.join(os.homedir(), '.tank');
12
+ }
13
+ /**
14
+ * Get the path to the tank config file.
15
+ */
16
+ export function getConfigPath(configDir) {
17
+ return path.join(getConfigDir(configDir), 'config.json');
18
+ }
19
+ /**
20
+ * Read the tank config file. Returns defaults if file doesn't exist.
21
+ */
22
+ export function getConfig(configDir) {
23
+ const configPath = getConfigPath(configDir);
24
+ try {
25
+ const raw = fs.readFileSync(configPath, 'utf-8');
26
+ const parsed = JSON.parse(raw);
27
+ const merged = { ...DEFAULT_CONFIG, ...parsed };
28
+ // TANK_TOKEN env var takes priority
29
+ const envToken = process.env.TANK_TOKEN?.trim();
30
+ if (envToken) {
31
+ merged.token = envToken;
32
+ }
33
+ return merged;
34
+ }
35
+ catch {
36
+ const envToken = process.env.TANK_TOKEN?.trim();
37
+ return {
38
+ ...DEFAULT_CONFIG,
39
+ ...(envToken ? { token: envToken } : {}),
40
+ };
41
+ }
42
+ }
43
+ /**
44
+ * Write config to disk. Merges with existing config.
45
+ */
46
+ export function setConfig(partial, configDir) {
47
+ const dir = getConfigDir(configDir);
48
+ const configPath = getConfigPath(configDir);
49
+ if (!fs.existsSync(dir)) {
50
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
51
+ }
52
+ const existing = getConfig(configDir);
53
+ const merged = { ...existing, ...partial };
54
+ fs.writeFileSync(configPath, JSON.stringify(merged, null, 2) + '\n', {
55
+ encoding: 'utf-8',
56
+ mode: 0o600,
57
+ });
58
+ }
59
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAQzB,MAAM,cAAc,GAAe;IACjC,QAAQ,EAAE,qBAAqB;CAChC,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,SAAkB;IAC7C,OAAO,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,SAAkB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,SAAkB;IAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACtD,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAChD,oCAAoC;QACpC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;QAChD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC;QAC1B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;QAChD,OAAO;YACL,GAAG,cAAc;YACjB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzC,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,OAA4B,EAC5B,SAAkB;IAElB,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAE5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3C,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE;QACnE,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface PackResult {
2
+ tarball: Buffer;
3
+ integrity: string;
4
+ fileCount: number;
5
+ totalSize: number;
6
+ readme: string;
7
+ files: string[];
8
+ manifest: Record<string, unknown>;
9
+ }
10
+ /**
11
+ * Pack a skill directory into a .tgz tarball with integrity hashing.
12
+ */
13
+ export declare function pack(directory: string): Promise<PackResult>;
14
+ //# sourceMappingURL=packer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../../src/lib/packer.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkGjE"}
@@ -0,0 +1,191 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { create } from 'tar';
5
+ import ignore from 'ignore';
6
+ import { skillsJsonSchema } from '@tank/shared';
7
+ // Limits
8
+ const MAX_PACKAGE_SIZE = 50 * 1024 * 1024; // 50MB
9
+ const MAX_FILE_COUNT = 1000;
10
+ // Default ignore patterns
11
+ const DEFAULT_IGNORES = [
12
+ 'node_modules',
13
+ '.git',
14
+ '.env*',
15
+ '*.log',
16
+ '.tank',
17
+ '.DS_Store',
18
+ ];
19
+ // Always ignored regardless of ignore file contents
20
+ const ALWAYS_IGNORED = ['node_modules', '.git'];
21
+ // Ignore file names (not packed into tarball)
22
+ const IGNORE_FILES = ['.tankignore', '.gitignore'];
23
+ /**
24
+ * Pack a skill directory into a .tgz tarball with integrity hashing.
25
+ */
26
+ export async function pack(directory) {
27
+ const absDir = path.resolve(directory);
28
+ // 1. Verify directory exists
29
+ if (!fs.existsSync(absDir)) {
30
+ throw new Error(`Directory does not exist: ${absDir}`);
31
+ }
32
+ const stat = fs.statSync(absDir);
33
+ if (!stat.isDirectory()) {
34
+ throw new Error(`Not a directory: ${absDir}`);
35
+ }
36
+ // 2. Verify skills.json exists and is valid
37
+ const skillsJsonPath = path.join(absDir, 'skills.json');
38
+ if (!fs.existsSync(skillsJsonPath)) {
39
+ throw new Error('Missing required file: skills.json');
40
+ }
41
+ let skillsJsonContent;
42
+ try {
43
+ skillsJsonContent = fs.readFileSync(skillsJsonPath, 'utf-8');
44
+ }
45
+ catch {
46
+ throw new Error('Failed to read skills.json');
47
+ }
48
+ let parsed;
49
+ try {
50
+ parsed = JSON.parse(skillsJsonContent);
51
+ }
52
+ catch {
53
+ throw new Error('Invalid skills.json: not valid JSON');
54
+ }
55
+ const validation = skillsJsonSchema.safeParse(parsed);
56
+ if (!validation.success) {
57
+ const issues = validation.error.issues
58
+ .map((i) => ` - ${i.path.join('.')}: ${i.message}`)
59
+ .join('\n');
60
+ throw new Error(`Invalid skills.json:\n${issues}`);
61
+ }
62
+ // 3. Verify SKILL.md exists and read its content
63
+ const skillMdPath = path.join(absDir, 'SKILL.md');
64
+ if (!fs.existsSync(skillMdPath)) {
65
+ throw new Error('Missing required file: SKILL.md');
66
+ }
67
+ let readmeContent;
68
+ try {
69
+ readmeContent = fs.readFileSync(skillMdPath, 'utf-8');
70
+ }
71
+ catch {
72
+ throw new Error('Failed to read SKILL.md');
73
+ }
74
+ // 4. Build ignore filter
75
+ const ig = buildIgnoreFilter(absDir);
76
+ // 5. Collect files with validation
77
+ const files = collectFiles(absDir, absDir, ig);
78
+ // 6. Enforce file count limit
79
+ if (files.length > MAX_FILE_COUNT) {
80
+ throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);
81
+ }
82
+ // 7. Calculate total size of source files
83
+ let totalSize = 0;
84
+ for (const file of files) {
85
+ const filePath = path.join(absDir, file);
86
+ const fileStat = fs.statSync(filePath);
87
+ totalSize += fileStat.size;
88
+ }
89
+ // 8. Create tarball
90
+ const tarball = await createTarball(absDir, files);
91
+ // 9. Enforce tarball size limit
92
+ if (tarball.length > MAX_PACKAGE_SIZE) {
93
+ throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);
94
+ }
95
+ // 10. Compute integrity hash
96
+ const hash = crypto.createHash('sha512').update(tarball).digest('base64');
97
+ const integrity = `sha512-${hash}`;
98
+ return {
99
+ tarball,
100
+ integrity,
101
+ fileCount: files.length,
102
+ totalSize,
103
+ readme: readmeContent,
104
+ files,
105
+ manifest: validation.data,
106
+ };
107
+ }
108
+ /**
109
+ * Build an ignore filter from .tankignore, .gitignore, or defaults.
110
+ */
111
+ function buildIgnoreFilter(dir) {
112
+ const ig = ignore();
113
+ ig.add(ALWAYS_IGNORED);
114
+ const tankIgnorePath = path.join(dir, '.tankignore');
115
+ const gitIgnorePath = path.join(dir, '.gitignore');
116
+ if (fs.existsSync(tankIgnorePath)) {
117
+ const content = fs.readFileSync(tankIgnorePath, 'utf-8');
118
+ ig.add(content);
119
+ ig.add(IGNORE_FILES);
120
+ }
121
+ else if (fs.existsSync(gitIgnorePath)) {
122
+ const content = fs.readFileSync(gitIgnorePath, 'utf-8');
123
+ ig.add(content);
124
+ ig.add(IGNORE_FILES);
125
+ }
126
+ else {
127
+ ig.add(DEFAULT_IGNORES);
128
+ }
129
+ return ig;
130
+ }
131
+ /**
132
+ * Recursively collect files from a directory.
133
+ */
134
+ function collectFiles(baseDir, currentDir, ig) {
135
+ const files = [];
136
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
137
+ for (const entry of entries) {
138
+ const fullPath = path.join(currentDir, entry.name);
139
+ const relativePath = path.relative(baseDir, fullPath);
140
+ // Security: check for path traversal
141
+ if (relativePath.split(path.sep).includes('..')) {
142
+ throw new Error(`Path traversal detected: "${relativePath}" contains ".." component`);
143
+ }
144
+ // Security: check for absolute paths
145
+ if (path.isAbsolute(relativePath)) {
146
+ throw new Error(`Absolute path detected: "${relativePath}"`);
147
+ }
148
+ // Security: check for symlinks
149
+ const lstatResult = fs.lstatSync(fullPath);
150
+ if (lstatResult.isSymbolicLink()) {
151
+ throw new Error(`Symlink detected: "${relativePath}" — symlinks are not allowed`);
152
+ }
153
+ const pathForIgnore = lstatResult.isDirectory()
154
+ ? relativePath + '/'
155
+ : relativePath;
156
+ if (ig.ignores(pathForIgnore)) {
157
+ continue;
158
+ }
159
+ if (lstatResult.isDirectory()) {
160
+ const subFiles = collectFiles(baseDir, fullPath, ig);
161
+ files.push(...subFiles);
162
+ }
163
+ else if (lstatResult.isFile()) {
164
+ files.push(relativePath);
165
+ }
166
+ }
167
+ return files;
168
+ }
169
+ /**
170
+ * Create a gzipped tarball from the given files.
171
+ */
172
+ async function createTarball(cwd, files) {
173
+ return new Promise((resolve, reject) => {
174
+ const chunks = [];
175
+ const stream = create({
176
+ gzip: true,
177
+ cwd,
178
+ portable: true,
179
+ }, files);
180
+ stream.on('data', (chunk) => {
181
+ chunks.push(chunk);
182
+ });
183
+ stream.on('end', () => {
184
+ resolve(Buffer.concat(chunks));
185
+ });
186
+ stream.on('error', (err) => {
187
+ reject(err);
188
+ });
189
+ });
190
+ }
191
+ //# sourceMappingURL=packer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packer.js","sourceRoot":"","sources":["../../src/lib/packer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,SAAS;AACT,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAClD,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,0BAA0B;AAC1B,MAAM,eAAe,GAAG;IACtB,cAAc;IACd,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,WAAW;CACZ,CAAC;AAEF,oDAAoD;AACpD,MAAM,cAAc,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAEhD,8CAA8C;AAC9C,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAYnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,SAAiB;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,4CAA4C;IAC5C,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACxD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,iBAAyB,CAAC;IAC9B,IAAI,CAAC;QACH,iBAAiB,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM;aACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACnD,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,iDAAiD;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,aAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,yBAAyB;IACzB,MAAM,EAAE,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAErC,mCAAmC;IACnC,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IAE/C,8BAA8B;IAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,mBAAmB,KAAK,CAAC,MAAM,uBAAuB,cAAc,EAAE,CACvE,CAAC;IACJ,CAAC;IAED,0CAA0C;IAC1C,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvC,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED,oBAAoB;IACpB,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAEnD,gCAAgC;IAChC,IAAI,OAAO,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,sBAAsB,OAAO,CAAC,MAAM,6BAA6B,gBAAgB,eAAe,CACjG,CAAC;IACJ,CAAC;IAED,6BAA6B;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,UAAU,IAAI,EAAE,CAAC;IAEnC,OAAO;QACL,OAAO;QACP,SAAS;QACT,SAAS,EAAE,KAAK,CAAC,MAAM;QACvB,SAAS;QACT,MAAM,EAAE,aAAa;QACrB,KAAK;QACL,QAAQ,EAAE,UAAU,CAAC,IAA+B;KACrD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,GAAW;IACpC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAEpB,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEvB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACrD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEnD,IAAI,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QACzD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChB,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC;SAAM,IAAI,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACxD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAChB,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACvB,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CACnB,OAAe,EACf,UAAkB,EAClB,EAA6B;IAE7B,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEtD,qCAAqC;QACrC,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,6BAA6B,YAAY,2BAA2B,CACrE,CAAC;QACJ,CAAC;QAED,qCAAqC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,4BAA4B,YAAY,GAAG,CAAC,CAAC;QAC/D,CAAC;QAED,+BAA+B;QAC/B,MAAM,WAAW,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,WAAW,CAAC,cAAc,EAAE,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,sBAAsB,YAAY,8BAA8B,CACjE,CAAC;QACJ,CAAC;QAED,MAAM,aAAa,GAAG,WAAW,CAAC,WAAW,EAAE;YAC7C,CAAC,CAAC,YAAY,GAAG,GAAG;YACpB,CAAC,CAAC,YAAY,CAAC;QAEjB,IAAI,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9B,SAAS;QACX,CAAC;QAED,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YACrD,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC1B,CAAC;aAAM,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,GAAW,EAAE,KAAe;IACvD,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,MAAM,MAAM,GAAG,MAAM,CACnB;YACE,IAAI,EAAE,IAAI;YACV,GAAG;YACH,QAAQ,EAAE,IAAI;SACf,EACD,KAAK,CACiB,CAAC;QAEzB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAClC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACpB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAChC,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerLoginTool(server: McpServer): void;
3
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/tools/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwHzD"}