@tankpkg/cli 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.
- package/LICENSE +21 -0
- package/dist/bin/tank.d.ts +2 -0
- package/dist/bin/tank.js +279 -0
- package/dist/bin/tank.js.map +1 -0
- package/dist/commands/audit.d.ts +5 -0
- package/dist/commands/audit.js +185 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +164 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/info.d.ts +5 -0
- package/dist/commands/info.js +102 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +92 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +39 -0
- package/dist/commands/install.js +550 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/link.d.ts +5 -0
- package/dist/commands/link.js +79 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.js +87 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +9 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/permissions.d.ts +4 -0
- package/dist/commands/permissions.js +199 -0
- package/dist/commands/permissions.js.map +1 -0
- package/dist/commands/publish.d.ts +25 -0
- package/dist/commands/publish.js +166 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/remove.d.ts +7 -0
- package/dist/commands/remove.js +163 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/search.d.ts +5 -0
- package/dist/commands/search.js +67 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/unlink.d.ts +5 -0
- package/dist/commands/unlink.js +42 -0
- package/dist/commands/unlink.js.map +1 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +337 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/upgrade.d.ts +6 -0
- package/dist/commands/upgrade.js +100 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/verify.d.ts +22 -0
- package/dist/commands/verify.js +63 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/whoami.d.ts +4 -0
- package/dist/commands/whoami.js +57 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agents.d.ts +19 -0
- package/dist/lib/agents.js +84 -0
- package/dist/lib/agents.js.map +1 -0
- package/dist/lib/api-client.d.ts +14 -0
- package/dist/lib/api-client.js +63 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/config.d.ts +29 -0
- package/dist/lib/config.js +66 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/debug-logger.d.ts +9 -0
- package/dist/lib/debug-logger.js +77 -0
- package/dist/lib/debug-logger.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +11 -0
- package/dist/lib/frontmatter.js +89 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/linker.d.ts +45 -0
- package/dist/lib/linker.js +137 -0
- package/dist/lib/linker.js.map +1 -0
- package/dist/lib/links.d.ts +20 -0
- package/dist/lib/links.js +105 -0
- package/dist/lib/links.js.map +1 -0
- package/dist/lib/lockfile.d.ts +24 -0
- package/dist/lib/lockfile.js +135 -0
- package/dist/lib/lockfile.js.map +1 -0
- package/dist/lib/logger.d.ts +6 -0
- package/dist/lib/logger.js +8 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/packer.d.ts +21 -0
- package/dist/lib/packer.js +210 -0
- package/dist/lib/packer.js.map +1 -0
- package/dist/lib/upgrade-check.d.ts +1 -0
- package/dist/lib/upgrade-check.js +52 -0
- package/dist/lib/upgrade-check.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface LoginOptions {
|
|
2
|
+
configDir?: string;
|
|
3
|
+
timeout?: number;
|
|
4
|
+
pollInterval?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Start the CLI login flow:
|
|
8
|
+
* 1. Generate random state
|
|
9
|
+
* 2. POST /api/v1/cli-auth/start → get authUrl + sessionCode
|
|
10
|
+
* 3. Open browser to authUrl
|
|
11
|
+
* 4. Poll POST /api/v1/cli-auth/exchange until authorized or timeout
|
|
12
|
+
* 5. Write token + user to config
|
|
13
|
+
*/
|
|
14
|
+
export declare function loginCommand(options?: LoginOptions): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import open from 'open';
|
|
2
|
+
import { getConfig, setConfig } from '../lib/config.js';
|
|
3
|
+
import { logger } from '../lib/logger.js';
|
|
4
|
+
import { authFlowLog } from '../lib/debug-logger.js';
|
|
5
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
7
|
+
/**
|
|
8
|
+
* Start the CLI login flow:
|
|
9
|
+
* 1. Generate random state
|
|
10
|
+
* 2. POST /api/v1/cli-auth/start → get authUrl + sessionCode
|
|
11
|
+
* 3. Open browser to authUrl
|
|
12
|
+
* 4. Poll POST /api/v1/cli-auth/exchange until authorized or timeout
|
|
13
|
+
* 5. Write token + user to config
|
|
14
|
+
*/
|
|
15
|
+
export async function loginCommand(options = {}) {
|
|
16
|
+
const { configDir, timeout = DEFAULT_TIMEOUT_MS, pollInterval = DEFAULT_POLL_INTERVAL_MS, } = options;
|
|
17
|
+
const config = getConfig(configDir);
|
|
18
|
+
const baseUrl = config.registry;
|
|
19
|
+
// Step 1: Generate random state for CSRF protection
|
|
20
|
+
const state = crypto.randomUUID();
|
|
21
|
+
authFlowLog.info({ state: state.slice(0, 8) + '...' }, 'Login flow started');
|
|
22
|
+
// Step 2: Start auth session
|
|
23
|
+
logger.info('Starting login...');
|
|
24
|
+
const startRes = await fetch(`${baseUrl}/api/v1/cli-auth/start`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({ state }),
|
|
28
|
+
});
|
|
29
|
+
if (!startRes.ok) {
|
|
30
|
+
const body = await startRes.json().catch(() => ({}));
|
|
31
|
+
authFlowLog.error({ status: startRes.status, error: body.error }, 'Start request failed');
|
|
32
|
+
throw new Error(`Failed to start auth session: ${body.error ?? startRes.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
authFlowLog.info({ ok: startRes.ok, status: startRes.status }, 'Start response received');
|
|
35
|
+
const { authUrl, sessionCode } = (await startRes.json());
|
|
36
|
+
authFlowLog.info({ authUrl, sessionCode: sessionCode.slice(0, 8) + '...' }, 'Session created, opening browser');
|
|
37
|
+
// Step 3: Open browser
|
|
38
|
+
try {
|
|
39
|
+
await open(authUrl);
|
|
40
|
+
logger.info('Opened browser for authentication.');
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Browser failed to open — print URL for manual copy
|
|
44
|
+
logger.warn('Could not open browser automatically.');
|
|
45
|
+
logger.info(`Open this URL in your browser:\n ${authUrl}`);
|
|
46
|
+
}
|
|
47
|
+
logger.info('Waiting for authorization...');
|
|
48
|
+
// Step 4: Poll exchange endpoint
|
|
49
|
+
const deadline = Date.now() + timeout;
|
|
50
|
+
while (Date.now() < deadline) {
|
|
51
|
+
try {
|
|
52
|
+
const exchangeRes = await fetch(`${baseUrl}/api/v1/cli-auth/exchange`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({ sessionCode, state }),
|
|
56
|
+
});
|
|
57
|
+
authFlowLog.debug({ status: exchangeRes.status, ok: exchangeRes.ok }, 'Exchange poll response');
|
|
58
|
+
if (exchangeRes.ok) {
|
|
59
|
+
const { token, user } = (await exchangeRes.json());
|
|
60
|
+
authFlowLog.info({ userName: user.name, userEmail: user.email }, 'Login successful, saving config');
|
|
61
|
+
// Step 5: Write to config
|
|
62
|
+
setConfig({ token, user: user }, configDir);
|
|
63
|
+
const displayName = user.name ?? user.email ?? 'unknown';
|
|
64
|
+
logger.success(`Logged in as ${displayName}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// 400 means session not yet authorized — keep polling
|
|
68
|
+
// Any other error is unexpected
|
|
69
|
+
if (exchangeRes.status !== 400) {
|
|
70
|
+
const body = await exchangeRes.json().catch(() => ({}));
|
|
71
|
+
throw new Error(`Exchange failed: ${body.error ?? exchangeRes.statusText}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
authFlowLog.warn({ error: err instanceof Error ? err.message : String(err) }, 'Exchange poll error');
|
|
76
|
+
// If it's our own thrown error, re-throw
|
|
77
|
+
if (err instanceof Error && err.message.startsWith('Exchange failed:')) {
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
// Network errors during polling are transient — keep trying
|
|
81
|
+
}
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
83
|
+
}
|
|
84
|
+
authFlowLog.error({}, 'Login timed out');
|
|
85
|
+
throw new Error('Login timed out. Please try again.');
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface LogoutOptions {
|
|
2
|
+
configDir?: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Logout command: Remove token and user from config.
|
|
6
|
+
* If not logged in, prints "Not logged in" and returns.
|
|
7
|
+
* If logged in, removes token and user, prints success message.
|
|
8
|
+
*/
|
|
9
|
+
export declare function logoutCommand(options?: LogoutOptions): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getConfig, setConfig } from '../lib/config.js';
|
|
2
|
+
import { logger } from '../lib/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Logout command: Remove token and user from config.
|
|
5
|
+
* If not logged in, prints "Not logged in" and returns.
|
|
6
|
+
* If logged in, removes token and user, prints success message.
|
|
7
|
+
*/
|
|
8
|
+
export async function logoutCommand(options = {}) {
|
|
9
|
+
const { configDir } = options;
|
|
10
|
+
const config = getConfig(configDir);
|
|
11
|
+
// Check if logged in
|
|
12
|
+
if (!config.token) {
|
|
13
|
+
logger.warn('Not logged in. Run: tank login');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Remove token and user from config
|
|
17
|
+
setConfig({ token: undefined, user: undefined }, configDir);
|
|
18
|
+
logger.success('Logged out');
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM1C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAyB,EAAE;IAC7D,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9B,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAEpC,qBAAqB;IACrB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,oCAAoC;IACpC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC;IAE5D,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
/**
|
|
5
|
+
* Parse a lockfile key like "@org/skill@1.0.0" into the skill name "@org/skill".
|
|
6
|
+
*/
|
|
7
|
+
function parseSkillName(key) {
|
|
8
|
+
const lastAt = key.lastIndexOf('@');
|
|
9
|
+
// For scoped packages, lastIndexOf('@') finds the version separator
|
|
10
|
+
// e.g. "@org/skill@1.0.0" → lastAt = 10, name = "@org/skill"
|
|
11
|
+
if (lastAt > 0) {
|
|
12
|
+
return key.slice(0, lastAt);
|
|
13
|
+
}
|
|
14
|
+
return key;
|
|
15
|
+
}
|
|
16
|
+
function collectPermissions(lockfile) {
|
|
17
|
+
const networkMap = new Map();
|
|
18
|
+
const fsReadMap = new Map();
|
|
19
|
+
const fsWriteMap = new Map();
|
|
20
|
+
const subprocessSkills = [];
|
|
21
|
+
for (const [key, entry] of Object.entries(lockfile.skills)) {
|
|
22
|
+
const skillName = parseSkillName(key);
|
|
23
|
+
const perms = entry.permissions;
|
|
24
|
+
if (perms.network?.outbound) {
|
|
25
|
+
for (const domain of perms.network.outbound) {
|
|
26
|
+
const existing = networkMap.get(domain) ?? [];
|
|
27
|
+
existing.push(skillName);
|
|
28
|
+
networkMap.set(domain, existing);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (perms.filesystem?.read) {
|
|
32
|
+
for (const p of perms.filesystem.read) {
|
|
33
|
+
const existing = fsReadMap.get(p) ?? [];
|
|
34
|
+
existing.push(skillName);
|
|
35
|
+
fsReadMap.set(p, existing);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (perms.filesystem?.write) {
|
|
39
|
+
for (const p of perms.filesystem.write) {
|
|
40
|
+
const existing = fsWriteMap.get(p) ?? [];
|
|
41
|
+
existing.push(skillName);
|
|
42
|
+
fsWriteMap.set(p, existing);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (perms.subprocess === true) {
|
|
46
|
+
subprocessSkills.push(skillName);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const toEntries = (map) => Array.from(map.entries()).map(([value, skills]) => ({ value, skills }));
|
|
50
|
+
return {
|
|
51
|
+
networkOutbound: toEntries(networkMap),
|
|
52
|
+
filesystemRead: toEntries(fsReadMap),
|
|
53
|
+
filesystemWrite: toEntries(fsWriteMap),
|
|
54
|
+
subprocess: subprocessSkills,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if a domain is allowed by the budget's domain list.
|
|
59
|
+
* Supports wildcard matching: *.example.com matches sub.example.com
|
|
60
|
+
*/
|
|
61
|
+
function isDomainAllowed(domain, allowedDomains) {
|
|
62
|
+
for (const allowed of allowedDomains) {
|
|
63
|
+
if (allowed === domain)
|
|
64
|
+
return true;
|
|
65
|
+
if (allowed.startsWith('*.')) {
|
|
66
|
+
const suffix = allowed.slice(1);
|
|
67
|
+
if (domain.endsWith(suffix) || domain === allowed.slice(2)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (domain === allowed)
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a path is allowed by the budget's path list.
|
|
78
|
+
*/
|
|
79
|
+
function isPathAllowed(requestedPath, allowedPaths) {
|
|
80
|
+
for (const allowed of allowedPaths) {
|
|
81
|
+
if (allowed === requestedPath)
|
|
82
|
+
return true;
|
|
83
|
+
if (allowed.endsWith('/**')) {
|
|
84
|
+
const prefix = allowed.slice(0, -3);
|
|
85
|
+
if (requestedPath.startsWith(prefix))
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function checkBudget(resolved, budget) {
|
|
92
|
+
const violations = [];
|
|
93
|
+
const budgetDomains = budget.network?.outbound ?? [];
|
|
94
|
+
for (const entry of resolved.networkOutbound) {
|
|
95
|
+
if (!isDomainAllowed(entry.value, budgetDomains)) {
|
|
96
|
+
violations.push({
|
|
97
|
+
category: 'network outbound',
|
|
98
|
+
value: entry.value,
|
|
99
|
+
skills: entry.skills,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const budgetReadPaths = budget.filesystem?.read ?? [];
|
|
104
|
+
for (const entry of resolved.filesystemRead) {
|
|
105
|
+
if (!isPathAllowed(entry.value, budgetReadPaths)) {
|
|
106
|
+
violations.push({
|
|
107
|
+
category: 'filesystem read',
|
|
108
|
+
value: entry.value,
|
|
109
|
+
skills: entry.skills,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const budgetWritePaths = budget.filesystem?.write ?? [];
|
|
114
|
+
for (const entry of resolved.filesystemWrite) {
|
|
115
|
+
if (!isPathAllowed(entry.value, budgetWritePaths)) {
|
|
116
|
+
violations.push({
|
|
117
|
+
category: 'filesystem write',
|
|
118
|
+
value: entry.value,
|
|
119
|
+
skills: entry.skills,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (resolved.subprocess.length > 0 && budget.subprocess !== true) {
|
|
124
|
+
violations.push({
|
|
125
|
+
category: 'subprocess',
|
|
126
|
+
value: 'subprocess access',
|
|
127
|
+
skills: resolved.subprocess,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return violations;
|
|
131
|
+
}
|
|
132
|
+
function formatAttribution(skills) {
|
|
133
|
+
return chalk.gray('← ' + skills.join(', '));
|
|
134
|
+
}
|
|
135
|
+
function printPermissionSection(title, entries) {
|
|
136
|
+
console.log(`\n${chalk.bold(title)}:`);
|
|
137
|
+
if (entries.length === 0) {
|
|
138
|
+
console.log(' none');
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
console.log(` ${entry.value} ${formatAttribution(entry.skills)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export async function permissionsCommand(options) {
|
|
147
|
+
const dir = options?.directory ?? process.cwd();
|
|
148
|
+
const lockfilePath = path.join(dir, 'skills.lock');
|
|
149
|
+
// 1. Read lockfile
|
|
150
|
+
if (!fs.existsSync(lockfilePath)) {
|
|
151
|
+
console.log('No skills installed.');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const lockfileContent = fs.readFileSync(lockfilePath, 'utf-8');
|
|
155
|
+
const lockfile = JSON.parse(lockfileContent);
|
|
156
|
+
if (!lockfile.skills || Object.keys(lockfile.skills).length === 0) {
|
|
157
|
+
console.log('No skills installed.');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// 2. Collect permissions
|
|
161
|
+
const resolved = collectPermissions(lockfile);
|
|
162
|
+
// 3. Display
|
|
163
|
+
console.log('\nResolved permissions for this project:\n');
|
|
164
|
+
printPermissionSection('Network (outbound)', resolved.networkOutbound);
|
|
165
|
+
printPermissionSection('Filesystem (read)', resolved.filesystemRead);
|
|
166
|
+
printPermissionSection('Filesystem (write)', resolved.filesystemWrite);
|
|
167
|
+
// Subprocess section
|
|
168
|
+
console.log(`\n${chalk.bold('Subprocess')}:`);
|
|
169
|
+
if (resolved.subprocess.length === 0) {
|
|
170
|
+
console.log(' none');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log(` allowed ${formatAttribution(resolved.subprocess)}`);
|
|
174
|
+
}
|
|
175
|
+
// 4. Budget check
|
|
176
|
+
const skillsJsonPath = path.join(dir, 'skills.json');
|
|
177
|
+
let budget;
|
|
178
|
+
if (fs.existsSync(skillsJsonPath)) {
|
|
179
|
+
const skillsJsonContent = fs.readFileSync(skillsJsonPath, 'utf-8');
|
|
180
|
+
const skillsJson = JSON.parse(skillsJsonContent);
|
|
181
|
+
budget = skillsJson.permissions;
|
|
182
|
+
}
|
|
183
|
+
console.log('');
|
|
184
|
+
if (!budget) {
|
|
185
|
+
console.log(`Budget status: ${chalk.yellow('⚠ No budget defined')}`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const violations = checkBudget(resolved, budget);
|
|
189
|
+
if (violations.length === 0) {
|
|
190
|
+
console.log(`Budget status: ${chalk.green('✓ PASS')} (all within budget)`);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
console.log(`Budget status: ${chalk.red('✗ FAIL')}`);
|
|
194
|
+
for (const v of violations) {
|
|
195
|
+
console.log(chalk.red(` - ${v.category}: "${v.value}" not in budget (requested by ${v.skills.join(', ')})`));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/commands/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface PublishOptions {
|
|
2
|
+
directory?: string;
|
|
3
|
+
configDir?: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
private?: boolean;
|
|
6
|
+
visibility?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Format bytes into a human-readable size string.
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatSize(bytes: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Publish a skill package to the Tank registry.
|
|
14
|
+
*
|
|
15
|
+
* Flow:
|
|
16
|
+
* 1. Check auth (token exists)
|
|
17
|
+
* 2. Read skills.json from directory
|
|
18
|
+
* 3. Pack directory into tarball
|
|
19
|
+
* 4. If --dry-run: print summary and exit
|
|
20
|
+
* 5. POST /api/v1/skills with manifest → get uploadUrl, skillId, versionId
|
|
21
|
+
* 6. PUT tarball to uploadUrl
|
|
22
|
+
* 7. POST /api/v1/skills/confirm with integrity data
|
|
23
|
+
* 8. Print success
|
|
24
|
+
*/
|
|
25
|
+
export declare function publishCommand(options?: PublishOptions): Promise<void>;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { getConfig } from '../lib/config.js';
|
|
5
|
+
import { logger } from '../lib/logger.js';
|
|
6
|
+
import { pack } from '../lib/packer.js';
|
|
7
|
+
import { USER_AGENT } from '../version.js';
|
|
8
|
+
/**
|
|
9
|
+
* Format bytes into a human-readable size string.
|
|
10
|
+
*/
|
|
11
|
+
export function formatSize(bytes) {
|
|
12
|
+
if (bytes < 1024)
|
|
13
|
+
return `${bytes} B`;
|
|
14
|
+
if (bytes < 1024 * 1024)
|
|
15
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
16
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Publish a skill package to the Tank registry.
|
|
20
|
+
*
|
|
21
|
+
* Flow:
|
|
22
|
+
* 1. Check auth (token exists)
|
|
23
|
+
* 2. Read skills.json from directory
|
|
24
|
+
* 3. Pack directory into tarball
|
|
25
|
+
* 4. If --dry-run: print summary and exit
|
|
26
|
+
* 5. POST /api/v1/skills with manifest → get uploadUrl, skillId, versionId
|
|
27
|
+
* 6. PUT tarball to uploadUrl
|
|
28
|
+
* 7. POST /api/v1/skills/confirm with integrity data
|
|
29
|
+
* 8. Print success
|
|
30
|
+
*/
|
|
31
|
+
export async function publishCommand(options = {}) {
|
|
32
|
+
const { directory = process.cwd(), configDir, dryRun = false, private: privateFlag, visibility } = options;
|
|
33
|
+
// 1. Check auth
|
|
34
|
+
const config = getConfig(configDir);
|
|
35
|
+
if (!config.token) {
|
|
36
|
+
throw new Error('Not logged in. Run: tank login');
|
|
37
|
+
}
|
|
38
|
+
// 2. Read skills.json
|
|
39
|
+
const skillsJsonPath = path.join(directory, 'skills.json');
|
|
40
|
+
if (!fs.existsSync(skillsJsonPath)) {
|
|
41
|
+
throw new Error(`No skills.json found in ${directory}. Run: tank init`);
|
|
42
|
+
}
|
|
43
|
+
let manifest;
|
|
44
|
+
try {
|
|
45
|
+
const raw = fs.readFileSync(skillsJsonPath, 'utf-8');
|
|
46
|
+
manifest = JSON.parse(raw);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new Error('Failed to read or parse skills.json');
|
|
50
|
+
}
|
|
51
|
+
if (visibility && visibility !== 'public' && visibility !== 'private') {
|
|
52
|
+
throw new Error("Invalid visibility. Use 'public' or 'private'");
|
|
53
|
+
}
|
|
54
|
+
const effectiveVisibility = visibility ?? (privateFlag ? 'private' : undefined);
|
|
55
|
+
if (effectiveVisibility) {
|
|
56
|
+
manifest.visibility = effectiveVisibility;
|
|
57
|
+
}
|
|
58
|
+
const name = manifest.name;
|
|
59
|
+
const version = manifest.version;
|
|
60
|
+
// 3. Pack
|
|
61
|
+
const spinner = ora('Packing...').start();
|
|
62
|
+
let packResult;
|
|
63
|
+
try {
|
|
64
|
+
packResult = await pack(directory);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
spinner.fail('Packing failed');
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const { tarball, integrity, fileCount, totalSize, readme, files } = packResult;
|
|
71
|
+
// 4. Dry run — print summary, verify auth with server, and exit
|
|
72
|
+
if (dryRun) {
|
|
73
|
+
spinner.stop();
|
|
74
|
+
logger.info(`name: ${name}`);
|
|
75
|
+
logger.info(`version: ${version}`);
|
|
76
|
+
logger.info(`visibility: ${String(manifest.visibility ?? 'default')}`);
|
|
77
|
+
logger.info(`size: ${formatSize(totalSize)} (${fileCount} files)`);
|
|
78
|
+
logger.info(`tarball: ${formatSize(tarball.length)} (compressed)`);
|
|
79
|
+
// Verify token with server to catch stale auth before real publish
|
|
80
|
+
try {
|
|
81
|
+
const verifyRes = await fetch(`${config.registry}/api/v1/auth/whoami`, {
|
|
82
|
+
method: 'GET',
|
|
83
|
+
headers: {
|
|
84
|
+
'Authorization': `Bearer ${config.token}`,
|
|
85
|
+
'User-Agent': USER_AGENT,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
if (verifyRes.status === 401) {
|
|
89
|
+
logger.warn('Token is invalid or expired. Run: tank login');
|
|
90
|
+
}
|
|
91
|
+
else if (!verifyRes.ok) {
|
|
92
|
+
logger.warn('Could not verify token with server. Publish may fail.');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
logger.success('Auth verified with server.');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
logger.warn('Could not reach server to verify token. Publish may fail.');
|
|
100
|
+
}
|
|
101
|
+
logger.success('Dry run complete — no files were uploaded.');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// 5. Step 1: POST /api/v1/skills
|
|
105
|
+
spinner.text = 'Publishing...';
|
|
106
|
+
const headers = {
|
|
107
|
+
'Authorization': `Bearer ${config.token}`,
|
|
108
|
+
'Content-Type': 'application/json',
|
|
109
|
+
'User-Agent': USER_AGENT,
|
|
110
|
+
};
|
|
111
|
+
const step1Res = await fetch(`${config.registry}/api/v1/skills`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers,
|
|
114
|
+
body: JSON.stringify({ manifest, readme, files }),
|
|
115
|
+
});
|
|
116
|
+
if (!step1Res.ok) {
|
|
117
|
+
spinner.fail('Publish failed');
|
|
118
|
+
const body = await step1Res.json().catch(() => ({}));
|
|
119
|
+
const errorMsg = body.error ?? step1Res.statusText;
|
|
120
|
+
if (step1Res.status === 401) {
|
|
121
|
+
throw new Error('Authentication failed. Your token may be expired or invalid. Run: tank login');
|
|
122
|
+
}
|
|
123
|
+
if (step1Res.status === 403) {
|
|
124
|
+
throw new Error(`Publish failed: ${errorMsg}`);
|
|
125
|
+
}
|
|
126
|
+
if (step1Res.status === 404) {
|
|
127
|
+
throw new Error(`Publish failed: ${errorMsg}`);
|
|
128
|
+
}
|
|
129
|
+
if (step1Res.status === 409) {
|
|
130
|
+
throw new Error('Version already exists. Bump the version in skills.json');
|
|
131
|
+
}
|
|
132
|
+
throw new Error(errorMsg);
|
|
133
|
+
}
|
|
134
|
+
const { uploadUrl, versionId } = (await step1Res.json());
|
|
135
|
+
// 6. Step 2: Upload tarball to signed URL
|
|
136
|
+
spinner.text = 'Uploading...';
|
|
137
|
+
const uploadRes = await fetch(uploadUrl, {
|
|
138
|
+
method: 'PUT',
|
|
139
|
+
headers: { 'Content-Type': 'application/octet-stream' },
|
|
140
|
+
body: new Uint8Array(tarball),
|
|
141
|
+
});
|
|
142
|
+
if (!uploadRes.ok) {
|
|
143
|
+
spinner.fail('Upload failed');
|
|
144
|
+
throw new Error(`Failed to upload tarball: ${uploadRes.status} ${uploadRes.statusText}`);
|
|
145
|
+
}
|
|
146
|
+
// 7. Step 3: Confirm publish
|
|
147
|
+
spinner.text = 'Confirming...';
|
|
148
|
+
const confirmRes = await fetch(`${config.registry}/api/v1/skills/confirm`, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
headers,
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
versionId,
|
|
153
|
+
integrity,
|
|
154
|
+
fileCount,
|
|
155
|
+
tarballSize: totalSize,
|
|
156
|
+
readme,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
if (!confirmRes.ok) {
|
|
160
|
+
spinner.fail('Publish confirmation failed');
|
|
161
|
+
const body = await confirmRes.json().catch(() => ({}));
|
|
162
|
+
throw new Error(`Failed to confirm publish: ${body.error ?? confirmRes.statusText}`);
|
|
163
|
+
}
|
|
164
|
+
spinner.succeed(`Published ${name}@${version} (${formatSize(totalSize)}, ${fileCount} files)`);
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
|