@tankpkg/mcp-server 0.7.0 → 0.8.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.
Files changed (87) hide show
  1. package/README.md +11 -4
  2. package/dist/index.d.ts +1 -3
  3. package/dist/index.js +2215 -29
  4. package/dist/index.js.map +1 -1
  5. package/package.json +20 -14
  6. package/LICENSE +0 -21
  7. package/dist/index.d.ts.map +0 -1
  8. package/dist/lib/api-client.d.ts +0 -45
  9. package/dist/lib/api-client.d.ts.map +0 -1
  10. package/dist/lib/api-client.js +0 -78
  11. package/dist/lib/api-client.js.map +0 -1
  12. package/dist/lib/config.d.ts +0 -25
  13. package/dist/lib/config.d.ts.map +0 -1
  14. package/dist/lib/config.js +0 -59
  15. package/dist/lib/config.js.map +0 -1
  16. package/dist/lib/packer.d.ts +0 -34
  17. package/dist/lib/packer.d.ts.map +0 -1
  18. package/dist/lib/packer.js +0 -276
  19. package/dist/lib/packer.js.map +0 -1
  20. package/dist/tools/audit-skill.d.ts +0 -3
  21. package/dist/tools/audit-skill.d.ts.map +0 -1
  22. package/dist/tools/audit-skill.js +0 -213
  23. package/dist/tools/audit-skill.js.map +0 -1
  24. package/dist/tools/doctor.d.ts +0 -3
  25. package/dist/tools/doctor.d.ts.map +0 -1
  26. package/dist/tools/doctor.js +0 -158
  27. package/dist/tools/doctor.js.map +0 -1
  28. package/dist/tools/init-skill.d.ts +0 -3
  29. package/dist/tools/init-skill.d.ts.map +0 -1
  30. package/dist/tools/init-skill.js +0 -72
  31. package/dist/tools/init-skill.js.map +0 -1
  32. package/dist/tools/install-skill.d.ts +0 -3
  33. package/dist/tools/install-skill.d.ts.map +0 -1
  34. package/dist/tools/install-skill.js +0 -206
  35. package/dist/tools/install-skill.js.map +0 -1
  36. package/dist/tools/link-skill.d.ts +0 -3
  37. package/dist/tools/link-skill.d.ts.map +0 -1
  38. package/dist/tools/link-skill.js +0 -81
  39. package/dist/tools/link-skill.js.map +0 -1
  40. package/dist/tools/login.d.ts +0 -3
  41. package/dist/tools/login.d.ts.map +0 -1
  42. package/dist/tools/login.js +0 -104
  43. package/dist/tools/login.js.map +0 -1
  44. package/dist/tools/logout.d.ts +0 -3
  45. package/dist/tools/logout.d.ts.map +0 -1
  46. package/dist/tools/logout.js +0 -19
  47. package/dist/tools/logout.js.map +0 -1
  48. package/dist/tools/publish-skill.d.ts +0 -3
  49. package/dist/tools/publish-skill.d.ts.map +0 -1
  50. package/dist/tools/publish-skill.js +0 -166
  51. package/dist/tools/publish-skill.js.map +0 -1
  52. package/dist/tools/remove-skill.d.ts +0 -3
  53. package/dist/tools/remove-skill.d.ts.map +0 -1
  54. package/dist/tools/remove-skill.js +0 -110
  55. package/dist/tools/remove-skill.js.map +0 -1
  56. package/dist/tools/scan-skill.d.ts +0 -3
  57. package/dist/tools/scan-skill.d.ts.map +0 -1
  58. package/dist/tools/scan-skill.js +0 -200
  59. package/dist/tools/scan-skill.js.map +0 -1
  60. package/dist/tools/search-skills.d.ts +0 -3
  61. package/dist/tools/search-skills.d.ts.map +0 -1
  62. package/dist/tools/search-skills.js +0 -54
  63. package/dist/tools/search-skills.js.map +0 -1
  64. package/dist/tools/skill-info.d.ts +0 -3
  65. package/dist/tools/skill-info.d.ts.map +0 -1
  66. package/dist/tools/skill-info.js +0 -88
  67. package/dist/tools/skill-info.js.map +0 -1
  68. package/dist/tools/skill-permissions.d.ts +0 -3
  69. package/dist/tools/skill-permissions.d.ts.map +0 -1
  70. package/dist/tools/skill-permissions.js +0 -311
  71. package/dist/tools/skill-permissions.js.map +0 -1
  72. package/dist/tools/unlink-skill.d.ts +0 -3
  73. package/dist/tools/unlink-skill.d.ts.map +0 -1
  74. package/dist/tools/unlink-skill.js +0 -72
  75. package/dist/tools/unlink-skill.js.map +0 -1
  76. package/dist/tools/update-skill.d.ts +0 -3
  77. package/dist/tools/update-skill.d.ts.map +0 -1
  78. package/dist/tools/update-skill.js +0 -317
  79. package/dist/tools/update-skill.js.map +0 -1
  80. package/dist/tools/verify-skills.d.ts +0 -3
  81. package/dist/tools/verify-skills.d.ts.map +0 -1
  82. package/dist/tools/verify-skills.js +0 -121
  83. package/dist/tools/verify-skills.js.map +0 -1
  84. package/dist/tools/whoami.d.ts +0 -3
  85. package/dist/tools/whoami.d.ts.map +0 -1
  86. package/dist/tools/whoami.js +0 -29
  87. package/dist/tools/whoami.js.map +0 -1
@@ -1,206 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import crypto from 'node:crypto';
4
- import { resolve, LOCKFILE_VERSION } from '@tank/shared';
5
- import { extract } from 'tar';
6
- import { z } from 'zod';
7
- import { TankApiClient } from '../lib/api-client.js';
8
- const SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\/[a-z0-9][a-z0-9-]*$/;
9
- function textResult(text, isError) {
10
- return {
11
- content: [{ type: 'text', text }],
12
- ...(isError ? { isError: true } : {}),
13
- };
14
- }
15
- function getSkillDir(projectDir, skillName) {
16
- if (skillName.startsWith('@')) {
17
- const [scope, name] = skillName.split('/');
18
- return path.join(projectDir, '.tank', 'skills', scope, name);
19
- }
20
- return path.join(projectDir, '.tank', 'skills', skillName);
21
- }
22
- export function registerInstallSkillTool(server) {
23
- server.tool('install-skill', 'Install a skill from the Tank registry. Resolves version, downloads tarball, verifies SHA-512 integrity, extracts files, and updates skills.json + skills.lock.', {
24
- name: z.string().describe('Skill name in @org/name format'),
25
- version: z.string().optional().describe('Specific version or semver range (default: latest)'),
26
- directory: z.string().optional().describe('Project directory (defaults to current working directory)'),
27
- }, async ({ name, version: versionRange, directory }) => {
28
- // 1. Validate scoped name
29
- if (!SCOPED_NAME_PATTERN.test(name)) {
30
- return textResult(`Validation error: Skill name "${name}" must use the @org/name format (e.g. @acme/my-skill).`, true);
31
- }
32
- const client = new TankApiClient();
33
- // 2. Check authentication
34
- if (!client.isAuthenticated) {
35
- return textResult('Not authenticated. Use the "login" tool first to authenticate with Tank.', true);
36
- }
37
- const dir = directory ? path.resolve(directory) : process.cwd();
38
- const range = versionRange ?? '*';
39
- // 3. Read or create skills.json
40
- const skillsJsonPath = path.join(dir, 'skills.json');
41
- let skillsJson = { skills: {} };
42
- if (fs.existsSync(skillsJsonPath)) {
43
- try {
44
- const raw = fs.readFileSync(skillsJsonPath, 'utf-8');
45
- skillsJson = JSON.parse(raw);
46
- }
47
- catch {
48
- return textResult('Failed to read or parse skills.json.', true);
49
- }
50
- }
51
- else {
52
- skillsJson = { skills: {} };
53
- fs.mkdirSync(dir, { recursive: true });
54
- fs.writeFileSync(skillsJsonPath, JSON.stringify(skillsJson, null, 2) + '\n');
55
- }
56
- // 4. Read existing lockfile
57
- const lockPath = path.join(dir, 'skills.lock');
58
- let lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };
59
- if (fs.existsSync(lockPath)) {
60
- try {
61
- const raw = fs.readFileSync(lockPath, 'utf-8');
62
- lock = JSON.parse(raw);
63
- }
64
- catch {
65
- lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };
66
- }
67
- }
68
- // 5. Fetch available versions
69
- const encodedName = encodeURIComponent(name);
70
- const versionsResult = await client.fetch(`/api/v1/skills/${encodedName}/versions`);
71
- if (!versionsResult.ok) {
72
- if (versionsResult.status === 401 || versionsResult.status === 403) {
73
- return textResult('Authentication failed. Use the "login" tool to authenticate with Tank.', true);
74
- }
75
- if (versionsResult.status === 404) {
76
- return textResult(`Skill not found: "${name}" does not exist in the Tank registry.`, true);
77
- }
78
- if (versionsResult.status === 0) {
79
- return textResult(`Cannot reach the Tank registry. Check your network connection and try again.\nError: ${versionsResult.error}`, true);
80
- }
81
- return textResult(`Failed to fetch versions for ${name}: ${versionsResult.error}`, true);
82
- }
83
- const availableVersions = versionsResult.data.versions.map((v) => v.version);
84
- // 6. Resolve best version
85
- const resolved = resolve(range, availableVersions);
86
- if (!resolved) {
87
- return textResult(`No version of ${name} satisfies range "${range}". Available versions: ${availableVersions.join(', ')}`, true);
88
- }
89
- // 7. Check if already installed
90
- const lockKey = `${name}@${resolved}`;
91
- if (lock.skills[lockKey]) {
92
- return textResult(`${name}@${resolved} is already installed. No changes needed.`);
93
- }
94
- // 8. Fetch version metadata
95
- const metaResult = await client.fetch(`/api/v1/skills/${encodedName}/${resolved}`);
96
- if (!metaResult.ok) {
97
- if (metaResult.status === 404) {
98
- return textResult(`Version ${resolved} of ${name} not found in the registry.`, true);
99
- }
100
- return textResult(`Failed to fetch metadata for ${name}@${resolved}: ${metaResult.error}`, true);
101
- }
102
- const metadata = metaResult.data;
103
- // 9. Download tarball
104
- let tarballBuffer;
105
- try {
106
- const downloadRes = await fetch(metadata.downloadUrl);
107
- if (!downloadRes.ok) {
108
- return textResult(`Failed to download tarball for ${name}@${resolved}: ${downloadRes.status} ${downloadRes.statusText}`, true);
109
- }
110
- tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());
111
- }
112
- catch (err) {
113
- return textResult(`Network error downloading tarball for ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`, true);
114
- }
115
- // 10. Verify SHA-512 integrity
116
- const hash = crypto.createHash('sha512').update(tarballBuffer).digest('base64');
117
- const computedIntegrity = `sha512-${hash}`;
118
- if (computedIntegrity !== metadata.integrity) {
119
- return textResult(`Integrity verification failed for ${name}@${resolved}.\n` +
120
- `Expected: ${metadata.integrity}\n` +
121
- `Got: ${computedIntegrity}\n\n` +
122
- 'The tarball may have been tampered with. No files were extracted.', true);
123
- }
124
- // 11. Extract tarball safely
125
- const extractDir = getSkillDir(dir, name);
126
- fs.mkdirSync(extractDir, { recursive: true });
127
- try {
128
- await extractSafely(tarballBuffer, extractDir);
129
- }
130
- catch (err) {
131
- // Clean up on extraction failure
132
- fs.rmSync(extractDir, { recursive: true, force: true });
133
- return textResult(`Failed to extract tarball for ${name}@${resolved}: ${err instanceof Error ? err.message : String(err)}`, true);
134
- }
135
- // 12. Update skills.json
136
- const skills = (skillsJson.skills ?? {});
137
- skills[name] = range === '*' ? `^${resolved}` : range;
138
- skillsJson.skills = skills;
139
- fs.writeFileSync(skillsJsonPath, JSON.stringify(skillsJson, null, 2) + '\n');
140
- // 13. Update skills.lock
141
- lock.skills[lockKey] = {
142
- resolved: metadata.downloadUrl,
143
- integrity: computedIntegrity,
144
- permissions: metadata.permissions ?? {},
145
- audit_score: metadata.auditScore ?? null,
146
- };
147
- // Sort keys alphabetically for determinism
148
- const sortedSkills = {};
149
- for (const key of Object.keys(lock.skills).sort()) {
150
- sortedSkills[key] = lock.skills[key];
151
- }
152
- lock.skills = sortedSkills;
153
- fs.mkdirSync(path.dirname(lockPath), { recursive: true });
154
- fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n');
155
- // Build response
156
- const score = metadata.auditScore !== null && metadata.auditScore !== undefined
157
- ? `${metadata.auditScore.toFixed(1)}/10`
158
- : 'pending';
159
- const lines = [
160
- `## Installed ${name}@${resolved}`,
161
- '',
162
- `**Integrity:** SHA-512 verified`,
163
- `**Audit Score:** ${score}`,
164
- `**Extracted to:** ${extractDir}`,
165
- '',
166
- '### Updated files',
167
- `- skills.json: added "${name}": "${skills[name]}"`,
168
- `- skills.lock: added ${lockKey}`,
169
- ];
170
- return textResult(lines.join('\n'));
171
- });
172
- }
173
- /**
174
- * Extract a tarball safely with security checks.
175
- * Rejects: absolute paths, path traversal (..), symlinks/hardlinks.
176
- */
177
- async function extractSafely(tarball, destDir) {
178
- const tmpTarball = path.join(destDir, '.tmp-tarball.tgz');
179
- fs.writeFileSync(tmpTarball, tarball);
180
- try {
181
- await extract({
182
- file: tmpTarball,
183
- cwd: destDir,
184
- filter: (entryPath) => {
185
- if (path.isAbsolute(entryPath)) {
186
- throw new Error(`Absolute path in tarball: ${entryPath}`);
187
- }
188
- if (entryPath.split('/').includes('..') || entryPath.split(path.sep).includes('..')) {
189
- throw new Error(`Path traversal in tarball: ${entryPath}`);
190
- }
191
- return true;
192
- },
193
- onReadEntry: (entry) => {
194
- if (entry.type === 'SymbolicLink' || entry.type === 'Link') {
195
- throw new Error(`Symlink/hardlink in tarball: ${entry.path}`);
196
- }
197
- },
198
- });
199
- }
200
- finally {
201
- if (fs.existsSync(tmpTarball)) {
202
- fs.unlinkSync(tmpTarball);
203
- }
204
- }
205
- }
206
- //# sourceMappingURL=install-skill.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../../src/tools/install-skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,OAAO,EAAqC,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAC9B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,mBAAmB,GAAG,mCAAmC,CAAC;AAsBhE,SAAS,UAAU,CAAC,IAAY,EAAE,OAAiB;IACjD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;QAC1C,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,UAAkB,EAAE,SAAiB;IACxD,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,IAAI,CACT,eAAe,EACf,iKAAiK,EACjK;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC3D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;QAC7F,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;KACvG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,EAAE;QACnD,0BAA0B;QAC1B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,OAAO,UAAU,CACf,iCAAiC,IAAI,wDAAwD,EAC7F,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QAEnC,0BAA0B;QAC1B,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5B,OAAO,UAAU,CACf,0EAA0E,EAC1E,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAChE,MAAM,KAAK,GAAG,YAAY,IAAI,GAAG,CAAC;QAElC,gCAAgC;QAChC,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACrD,IAAI,UAAU,GAA4B,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACzD,IAAI,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;gBACrD,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,UAAU,CAAC,sCAAsC,EAAE,IAAI,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC5B,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,EAAE,CAAC,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/E,CAAC;QAED,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC/C,IAAI,IAAI,GAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACzE,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAe,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,8BAA8B;QAC9B,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,KAAK,CACvC,kBAAkB,WAAW,WAAW,CACzC,CAAC;QAEF,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;YACvB,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnE,OAAO,UAAU,CACf,wEAAwE,EACxE,IAAI,CACL,CAAC;YACJ,CAAC;YACD,IAAI,cAAc,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAClC,OAAO,UAAU,CACf,qBAAqB,IAAI,wCAAwC,EACjE,IAAI,CACL,CAAC;YACJ,CAAC;YACD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,OAAO,UAAU,CACf,wFAAwF,cAAc,CAAC,KAAK,EAAE,EAC9G,IAAI,CACL,CAAC;YACJ,CAAC;YACD,OAAO,UAAU,CACf,gCAAgC,IAAI,KAAK,cAAc,CAAC,KAAK,EAAE,EAC/D,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,iBAAiB,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAE7E,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,UAAU,CACf,iBAAiB,IAAI,qBAAqB,KAAK,0BAA0B,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACvG,IAAI,CACL,CAAC;QACJ,CAAC;QAED,gCAAgC;QAChC,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,UAAU,CACf,GAAG,IAAI,IAAI,QAAQ,2CAA2C,CAC/D,CAAC;QACJ,CAAC;QAED,4BAA4B;QAC5B,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,KAAK,CACnC,kBAAkB,WAAW,IAAI,QAAQ,EAAE,CAC5C,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;YACnB,IAAI,UAAU,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC9B,OAAO,UAAU,CACf,WAAW,QAAQ,OAAO,IAAI,6BAA6B,EAC3D,IAAI,CACL,CAAC;YACJ,CAAC;YACD,OAAO,UAAU,CACf,gCAAgC,IAAI,IAAI,QAAQ,KAAK,UAAU,CAAC,KAAK,EAAE,EACvE,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;QAEjC,sBAAsB;QACtB,IAAI,aAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;gBACpB,OAAO,UAAU,CACf,kCAAkC,IAAI,IAAI,QAAQ,KAAK,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,EACrG,IAAI,CACL,CAAC;YACJ,CAAC;YACD,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,UAAU,CACf,yCAAyC,IAAI,IAAI,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAChH,IAAI,CACL,CAAC;QACJ,CAAC;QAED,+BAA+B;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,iBAAiB,GAAG,UAAU,IAAI,EAAE,CAAC;QAE3C,IAAI,iBAAiB,KAAK,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC7C,OAAO,UAAU,CACf,qCAAqC,IAAI,IAAI,QAAQ,KAAK;gBAC1D,aAAa,QAAQ,CAAC,SAAS,IAAI;gBACnC,QAAQ,iBAAiB,MAAM;gBAC/B,mEAAmE,EACnE,IAAI,CACL,CAAC;QACJ,CAAC;QAED,6BAA6B;QAC7B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1C,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,IAAI,CAAC;YACH,MAAM,aAAa,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iCAAiC;YACjC,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,OAAO,UAAU,CACf,iCAAiC,IAAI,IAAI,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACxG,IAAI,CACL,CAAC;QACJ,CAAC;QAED,yBAAyB;QACzB,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,CAA2B,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QACtD,UAAU,CAAC,MAAM,GAAG,MAAM,CAAC;QAC3B,EAAE,CAAC,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAE7E,yBAAyB;QACzB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG;YACrB,QAAQ,EAAE,QAAQ,CAAC,WAAW;YAC9B,SAAS,EAAE,iBAAiB;YAC5B,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE;YACvC,WAAW,EAAE,QAAQ,CAAC,UAAU,IAAI,IAAI;SACzC,CAAC;QAEF,2CAA2C;QAC3C,MAAM,YAAY,GAA4B,EAAE,CAAC;QACjD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAClD,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,YAAoC,CAAC;QAEnD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAEjE,iBAAiB;QACjB,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS;YAC7E,CAAC,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;YACxC,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,KAAK,GAAa;YACtB,gBAAgB,IAAI,IAAI,QAAQ,EAAE;YAClC,EAAE;YACF,iCAAiC;YACjC,oBAAoB,KAAK,EAAE;YAC3B,qBAAqB,UAAU,EAAE;YACjC,EAAE;YACF,mBAAmB;YACnB,yBAAyB,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG;YACnD,wBAAwB,OAAO,EAAE;SAClC,CAAC;QAEF,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,aAAa,CAAC,OAAe,EAAE,OAAe;IAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC;YACZ,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,OAAO;YACZ,MAAM,EAAE,CAAC,SAAiB,EAAE,EAAE;gBAC5B,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC/B,MAAM,IAAI,KAAK,CAAC,6BAA6B,SAAS,EAAE,CAAC,CAAC;gBAC5D,CAAC;gBACD,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpF,MAAM,IAAI,KAAK,CAAC,8BAA8B,SAAS,EAAE,CAAC,CAAC;gBAC7D,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;gBACrB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC3D,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBAChE,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerLinkSkillTool(server: McpServer): void;
3
- //# sourceMappingURL=link-skill.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"link-skill.d.ts","sourceRoot":"","sources":["../../src/tools/link-skill.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiF7D"}
@@ -1,81 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { z } from 'zod';
4
- const SCOPED_NAME_PATTERN = /^@[a-z0-9-]+\/[a-z0-9][a-z0-9-]*$/;
5
- export function registerLinkSkillTool(server) {
6
- server.tool('link-skill', 'Link an installed skill into an agent workspace. Creates a symlink from the workspace .skills directory to the installed skill.', {
7
- name: z.string().describe('Skill name in @org/name format'),
8
- workspace: z.string().describe('Agent workspace directory path'),
9
- directory: z.string().optional().describe('Project directory where skills are installed (defaults to current working directory)'),
10
- }, async ({ name, workspace, directory }) => {
11
- if (!SCOPED_NAME_PATTERN.test(name)) {
12
- return {
13
- content: [{
14
- type: 'text',
15
- text: `Validation error: Skill name "${name}" must use the @org/name format (e.g. @acme/my-skill).`,
16
- }],
17
- isError: true,
18
- };
19
- }
20
- const projectDir = directory ? path.resolve(directory) : process.cwd();
21
- const workspaceDir = path.resolve(workspace);
22
- if (!fs.existsSync(workspaceDir)) {
23
- return {
24
- content: [{
25
- type: 'text',
26
- text: `Error: Workspace directory does not exist: ${workspaceDir}`,
27
- }],
28
- isError: true,
29
- };
30
- }
31
- const skillDir = getSkillDir(projectDir, name);
32
- if (!fs.existsSync(skillDir)) {
33
- return {
34
- content: [{
35
- type: 'text',
36
- text: `Skill "${name}" is not installed. Install it first with "install-skill" before linking.`,
37
- }],
38
- isError: true,
39
- };
40
- }
41
- const [scope, skillName] = name.split('/');
42
- const skillsLinkDir = path.join(workspaceDir, '.skills', scope);
43
- const symlinkPath = path.join(skillsLinkDir, skillName);
44
- try {
45
- const stats = fs.lstatSync(symlinkPath);
46
- if (stats.isSymbolicLink()) {
47
- const currentTarget = fs.readlinkSync(symlinkPath);
48
- const resolvedTarget = path.isAbsolute(currentTarget)
49
- ? currentTarget
50
- : path.resolve(path.dirname(symlinkPath), currentTarget);
51
- if (path.resolve(resolvedTarget) === path.resolve(skillDir)) {
52
- return {
53
- content: [{
54
- type: 'text',
55
- text: `Skill "${name}" is already linked in ${workspaceDir}.`,
56
- }],
57
- };
58
- }
59
- fs.unlinkSync(symlinkPath);
60
- }
61
- }
62
- catch {
63
- }
64
- fs.mkdirSync(skillsLinkDir, { recursive: true });
65
- fs.symlinkSync(skillDir, symlinkPath, 'dir');
66
- return {
67
- content: [{
68
- type: 'text',
69
- text: `Successfully linked "${name}" into ${workspaceDir}.\nSymlink: ${symlinkPath} → ${skillDir}`,
70
- }],
71
- };
72
- });
73
- }
74
- function getSkillDir(projectDir, skillName) {
75
- if (skillName.startsWith('@')) {
76
- const [scope, name] = skillName.split('/');
77
- return path.join(projectDir, '.tank', 'skills', scope, name);
78
- }
79
- return path.join(projectDir, '.tank', 'skills', skillName);
80
- }
81
- //# sourceMappingURL=link-skill.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"link-skill.js","sourceRoot":"","sources":["../../src/tools/link-skill.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerLoginTool(server: McpServer): void;
3
- //# sourceMappingURL=login.d.ts.map
@@ -1 +0,0 @@
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"}
@@ -1,104 +0,0 @@
1
- import { z } from 'zod';
2
- import { getConfig, setConfig } from '../lib/config.js';
3
- import { TankApiClient } from '../lib/api-client.js';
4
- const DEFAULT_POLL_INTERVAL_MS = 2000;
5
- const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
6
- export function registerLoginTool(server) {
7
- server.tool('login', 'Authenticate with Tank using GitHub OAuth device flow. Opens browser for authorization.', {
8
- timeout: z.number().optional().describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
9
- }, async ({ timeout = DEFAULT_TIMEOUT_MS }) => {
10
- const client = new TankApiClient();
11
- const config = getConfig();
12
- // Check if already logged in with valid token
13
- if (config.token) {
14
- const authCheck = await client.verifyAuth();
15
- if (authCheck.valid) {
16
- const displayName = authCheck.user?.name ?? authCheck.user?.email ?? 'unknown user';
17
- return {
18
- content: [
19
- {
20
- type: 'text',
21
- text: `Already logged in as ${displayName}.\n\nTo log out, delete ~/.tank/config.json or use the CLI: tank logout`,
22
- },
23
- ],
24
- };
25
- }
26
- }
27
- // Start device flow
28
- const state = crypto.randomUUID();
29
- const startRes = await fetch(`${config.registry}/api/v1/cli-auth/start`, {
30
- method: 'POST',
31
- headers: { 'Content-Type': 'application/json' },
32
- body: JSON.stringify({ state }),
33
- });
34
- if (!startRes.ok) {
35
- const body = await startRes.json().catch(() => ({}));
36
- return {
37
- content: [
38
- {
39
- type: 'text',
40
- text: `Failed to start login flow: ${body.error ?? startRes.statusText}`,
41
- },
42
- ],
43
- };
44
- }
45
- const { authUrl, sessionCode } = (await startRes.json());
46
- // Return auth URL and poll for completion
47
- const deadline = Date.now() + timeout;
48
- let authorized = false;
49
- let lastStatus = '';
50
- while (Date.now() < deadline) {
51
- try {
52
- const exchangeRes = await fetch(`${config.registry}/api/v1/cli-auth/exchange`, {
53
- method: 'POST',
54
- headers: { 'Content-Type': 'application/json' },
55
- body: JSON.stringify({ sessionCode, state }),
56
- });
57
- if (exchangeRes.ok) {
58
- const { token, user } = (await exchangeRes.json());
59
- // Save token to config
60
- setConfig({ token, user: user });
61
- const displayName = user.name ?? user.email ?? 'unknown user';
62
- return {
63
- content: [
64
- {
65
- type: 'text',
66
- text: `Successfully logged in as ${displayName}!\n\nYou can now use all Tank MCP tools: scan-skill, publish-skill, etc.`,
67
- },
68
- ],
69
- };
70
- }
71
- // 400 means not yet authorized - keep polling
72
- if (exchangeRes.status !== 400) {
73
- const body = await exchangeRes.json().catch(() => ({}));
74
- return {
75
- content: [
76
- {
77
- type: 'text',
78
- text: `Login failed: ${body.error ?? exchangeRes.statusText}`,
79
- },
80
- ],
81
- };
82
- }
83
- // Check status and provide updates
84
- const newStatus = 'Waiting for authorization...';
85
- if (newStatus !== lastStatus) {
86
- lastStatus = newStatus;
87
- }
88
- }
89
- catch {
90
- // Network errors during polling are transient
91
- }
92
- await new Promise((resolve) => setTimeout(resolve, DEFAULT_POLL_INTERVAL_MS));
93
- }
94
- return {
95
- content: [
96
- {
97
- type: 'text',
98
- text: `Login timed out. The authorization link may have expired.\n\nTry again: tank login`,
99
- },
100
- ],
101
- };
102
- });
103
- }
104
- //# sourceMappingURL=login.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/tools/login.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerLogoutTool(server: McpServer): void;
3
- //# sourceMappingURL=logout.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/tools/logout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0B1D"}
@@ -1,19 +0,0 @@
1
- import { getConfig, setConfig } from '../lib/config.js';
2
- export function registerLogoutTool(server) {
3
- server.tool('logout', 'Log out of Tank by clearing local credentials.', {}, async () => {
4
- const config = getConfig();
5
- if (!config.token) {
6
- return {
7
- content: [{ type: 'text', text: 'Not logged in. No credentials to clear.' }],
8
- };
9
- }
10
- setConfig({ token: undefined, user: undefined });
11
- // Also clear env-based token so subsequent tool calls in this
12
- // process don't re-read it via getConfig().
13
- delete process.env.TANK_TOKEN;
14
- return {
15
- content: [{ type: 'text', text: 'Successfully logged out.' }],
16
- };
17
- });
18
- }
19
- //# sourceMappingURL=logout.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/tools/logout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,IAAI,CACT,QAAQ,EACR,gDAAgD,EAChD,EAAE,EACF,KAAK,IAAI,EAAE;QAET,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAE3B,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,yCAAyC,EAAE,CAAC;aACtF,CAAC;QACJ,CAAC;QAED,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAEjD,8DAA8D;QAC9D,4CAA4C;QAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;QAE9B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC;SACvE,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- export declare function registerPublishSkillTool(server: McpServer): void;
3
- //# sourceMappingURL=publish-skill.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"publish-skill.d.ts","sourceRoot":"","sources":["../../src/tools/publish-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBzE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmLhE"}
@@ -1,166 +0,0 @@
1
- import { z } from 'zod';
2
- import path from 'node:path';
3
- import { TankApiClient } from '../lib/api-client.js';
4
- import { pack } from '../lib/packer.js';
5
- import { getConfig } from '../lib/config.js';
6
- export function registerPublishSkillTool(server) {
7
- server.tool('publish-skill', 'Publish a skill to the Tank registry. Requires authentication.', {
8
- directory: z.string().optional().describe('Directory to publish (default: current directory)'),
9
- visibility: z.enum(['public', 'private']).optional().default('public').describe('Package visibility'),
10
- dryRun: z.boolean().optional().default(false).describe('Validate without publishing'),
11
- }, async ({ directory = '.', visibility = 'public', dryRun = false }) => {
12
- const absDir = path.resolve(directory);
13
- const client = new TankApiClient();
14
- const config = getConfig();
15
- // Check auth (skip for dry run)
16
- if (!dryRun && !client.isAuthenticated) {
17
- return {
18
- content: [
19
- {
20
- type: 'text',
21
- text: 'You need to log in first. Use the login tool to authenticate with Tank.\n\nExample: "Log in to Tank"',
22
- },
23
- ],
24
- };
25
- }
26
- if (!dryRun) {
27
- const authCheck = await client.verifyAuth();
28
- if (!authCheck.valid) {
29
- return {
30
- content: [
31
- {
32
- type: 'text',
33
- text: 'Your session has expired. Use the login tool to authenticate again.',
34
- },
35
- ],
36
- };
37
- }
38
- }
39
- // Pack the skill
40
- let packResult;
41
- try {
42
- packResult = await pack(absDir);
43
- }
44
- catch (err) {
45
- return {
46
- content: [
47
- {
48
- type: 'text',
49
- text: `Failed to pack skill: ${err instanceof Error ? err.message : String(err)}`,
50
- },
51
- ],
52
- };
53
- }
54
- const manifest = packResult.manifest;
55
- const skillName = manifest.name ?? 'unknown';
56
- const skillVersion = manifest.version ?? '0.0.0';
57
- // Dry run: just validate and return summary
58
- if (dryRun) {
59
- const lines = [
60
- `## Dry Run for ${skillName}@${skillVersion}`,
61
- '',
62
- '**Validation:** ✅ PASSED',
63
- '',
64
- '### Package Summary',
65
- `- **Name:** ${skillName}`,
66
- `- **Version:** ${skillVersion}`,
67
- `- **Visibility:** ${visibility}`,
68
- `- **Files:** ${packResult.fileCount}`,
69
- `- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB compressed`,
70
- `- **Integrity:** ${packResult.integrity.slice(0, 20)}...`,
71
- '',
72
- '### Manifest',
73
- `- **Description:** ${manifest.description ?? 'No description'}`,
74
- `- **Permissions:** ${JSON.stringify(manifest.permissions ?? {})}`,
75
- '',
76
- '### Files',
77
- ...packResult.files.slice(0, 10).map((f) => ` - ${f}`),
78
- packResult.files.length > 10 ? ` ... and ${packResult.files.length - 10} more` : '',
79
- '',
80
- 'Ready to publish. Say "publish my skill" when you\'re ready.',
81
- ];
82
- return {
83
- content: [{ type: 'text', text: lines.join('\n') }],
84
- };
85
- }
86
- // Step 1: Start publish flow
87
- const startResult = await client.fetch('/api/v1/skills', {
88
- method: 'POST',
89
- body: JSON.stringify({
90
- manifest: { ...manifest, visibility },
91
- readme: packResult.readme,
92
- files: packResult.files,
93
- }),
94
- });
95
- if (!startResult.ok) {
96
- return {
97
- content: [
98
- {
99
- type: 'text',
100
- text: `Failed to start publish: ${startResult.error}`,
101
- },
102
- ],
103
- };
104
- }
105
- const { uploadUrl, versionId } = startResult.data;
106
- // Step 2: Upload tarball
107
- const uploadRes = await fetch(uploadUrl, {
108
- method: 'PUT',
109
- headers: {
110
- 'Content-Type': 'application/gzip',
111
- },
112
- body: packResult.tarball,
113
- });
114
- if (!uploadRes.ok) {
115
- return {
116
- content: [
117
- {
118
- type: 'text',
119
- text: `Failed to upload tarball: ${uploadRes.statusText}`,
120
- },
121
- ],
122
- };
123
- }
124
- // Step 3: Confirm upload
125
- const confirmResult = await client.fetch('/api/v1/skills/confirm', {
126
- method: 'POST',
127
- body: JSON.stringify({
128
- versionId,
129
- integrity: packResult.integrity,
130
- fileCount: packResult.fileCount,
131
- tarballSize: packResult.tarball.length,
132
- readme: packResult.readme,
133
- }),
134
- });
135
- if (!confirmResult.ok) {
136
- return {
137
- content: [
138
- {
139
- type: 'text',
140
- text: `Failed to confirm publish: ${confirmResult.error}`,
141
- },
142
- ],
143
- };
144
- }
145
- const confirm = confirmResult.data;
146
- const score = confirm.auditScore !== null ? `${confirm.auditScore.toFixed(1)}/10` : 'pending';
147
- const lines = [
148
- `## Published ${confirm.name}@${confirm.version}`,
149
- '',
150
- `**Status:** ✅ Successfully published`,
151
- `**Visibility:** ${visibility}`,
152
- `**Audit Score:** ${score}`,
153
- `**Scan Verdict:** ${confirm.scanVerdict ?? 'pending'}`,
154
- '',
155
- '### Package Details',
156
- `- **Files:** ${packResult.fileCount}`,
157
- `- **Size:** ${(packResult.totalSize / 1024).toFixed(1)}KB`,
158
- '',
159
- `View your skill: https://tankpkg.dev/skills/${confirm.name}`,
160
- ];
161
- return {
162
- content: [{ type: 'text', text: lines.join('\n') }],
163
- };
164
- });
165
- }
166
- //# sourceMappingURL=publish-skill.js.map