@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,77 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
import { appendFileSync } from 'node:fs';
|
|
3
|
+
const lokiUrl = process.env.TANK_LOKI_URL || 'http://localhost:3100';
|
|
4
|
+
const debugEnabled = process.env.TANK_DEBUG === '1' || process.env.TANK_DEBUG === 'true';
|
|
5
|
+
// Buffer logs and push to Loki via HTTP (avoids pino.transport() worker thread serialization issues)
|
|
6
|
+
const logBuffer = [];
|
|
7
|
+
let flushTimer = null;
|
|
8
|
+
async function flushToLoki() {
|
|
9
|
+
if (logBuffer.length === 0)
|
|
10
|
+
return;
|
|
11
|
+
const logs = logBuffer.splice(0);
|
|
12
|
+
const values = logs.map((line) => {
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(line);
|
|
15
|
+
const ts = parsed.time
|
|
16
|
+
? String(new Date(parsed.time).getTime() * 1_000_000)
|
|
17
|
+
: String(Date.now() * 1_000_000);
|
|
18
|
+
return [ts, line];
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return [String(Date.now() * 1_000_000), line];
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
await fetch(`${lokiUrl}/loki/api/v1/push`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
streams: [{ stream: { app: 'tank-cli' }, values }],
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
logBuffer.unshift(...logs);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const lokiStream = {
|
|
38
|
+
write(line) {
|
|
39
|
+
const trimmed = line.trimEnd();
|
|
40
|
+
logBuffer.push(trimmed);
|
|
41
|
+
if (debugEnabled) {
|
|
42
|
+
try {
|
|
43
|
+
appendFileSync('/tmp/tank-cli-debug.log', trimmed + '\n');
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
}
|
|
47
|
+
if (!flushTimer) {
|
|
48
|
+
flushTimer = setInterval(flushToLoki, 2000);
|
|
49
|
+
}
|
|
50
|
+
if (logBuffer.length >= 50) {
|
|
51
|
+
void flushToLoki();
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
export const debugLog = pino({
|
|
56
|
+
level: 'debug',
|
|
57
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
58
|
+
formatters: {
|
|
59
|
+
level(label) {
|
|
60
|
+
return { level: label };
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
}, lokiStream);
|
|
64
|
+
export const httpLog = debugLog.child({ module: 'http' });
|
|
65
|
+
export const authFlowLog = debugLog.child({ module: 'auth-flow' });
|
|
66
|
+
/**
|
|
67
|
+
* Flush pending logs before process exit.
|
|
68
|
+
* Call this at the end of CLI commands to ensure logs are delivered.
|
|
69
|
+
*/
|
|
70
|
+
export async function flushLogs() {
|
|
71
|
+
if (flushTimer) {
|
|
72
|
+
clearInterval(flushTimer);
|
|
73
|
+
flushTimer = null;
|
|
74
|
+
}
|
|
75
|
+
await flushToLoki();
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=debug-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-logger.js","sourceRoot":"","sources":["../../src/lib/debug-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PrepareOptions {
|
|
2
|
+
skillName: string;
|
|
3
|
+
extractDir: string;
|
|
4
|
+
agentSkillsBaseDir: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function hasFrontmatter(content: string): boolean;
|
|
8
|
+
export declare function stripScope(skillName: string): string;
|
|
9
|
+
export declare function extractDescriptionFromMarkdown(content: string): string;
|
|
10
|
+
export declare function generateFrontmatter(name: string, description: string): string;
|
|
11
|
+
export declare function prepareAgentSkillDir(options: PrepareOptions): string;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getSymlinkName } from './agents.js';
|
|
4
|
+
export function hasFrontmatter(content) {
|
|
5
|
+
return /^---\s*\n/.test(content);
|
|
6
|
+
}
|
|
7
|
+
export function stripScope(skillName) {
|
|
8
|
+
const match = skillName.match(/^@[^/]+\/(.+)$/);
|
|
9
|
+
if (!match) {
|
|
10
|
+
return skillName;
|
|
11
|
+
}
|
|
12
|
+
return match[1] ?? skillName;
|
|
13
|
+
}
|
|
14
|
+
export function extractDescriptionFromMarkdown(content) {
|
|
15
|
+
const lines = content.split(/\r?\n/);
|
|
16
|
+
const firstLine = lines.find((line) => line.trim().length > 0);
|
|
17
|
+
if (firstLine && /^#\s+/.test(firstLine)) {
|
|
18
|
+
return firstLine.replace(/^#\s+/, '').trim();
|
|
19
|
+
}
|
|
20
|
+
let seenHeading = false;
|
|
21
|
+
let paragraphLines = [];
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (/^#{1,6}\s+/.test(trimmed)) {
|
|
25
|
+
seenHeading = true;
|
|
26
|
+
paragraphLines = [];
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!seenHeading) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (trimmed.length === 0) {
|
|
33
|
+
if (paragraphLines.length > 0) {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
paragraphLines.push(trimmed);
|
|
39
|
+
}
|
|
40
|
+
if (paragraphLines.length > 0) {
|
|
41
|
+
const paragraph = paragraphLines.join(' ').trim();
|
|
42
|
+
const match = paragraph.match(/^(.+?[.!?])(\s|$)/);
|
|
43
|
+
return (match ? match[1] : paragraph).trim();
|
|
44
|
+
}
|
|
45
|
+
return 'An AI agent skill';
|
|
46
|
+
}
|
|
47
|
+
export function generateFrontmatter(name, description) {
|
|
48
|
+
const indented = description
|
|
49
|
+
.split(/\r?\n/)
|
|
50
|
+
.map((line) => ` ${line}`)
|
|
51
|
+
.join('\n');
|
|
52
|
+
return `---\nname: ${name}\ndescription: |\n${indented}\n---\n\n`;
|
|
53
|
+
}
|
|
54
|
+
export function prepareAgentSkillDir(options) {
|
|
55
|
+
const { skillName, extractDir, agentSkillsBaseDir, description } = options;
|
|
56
|
+
const symlinkName = getSymlinkName(skillName);
|
|
57
|
+
const targetDir = path.resolve(agentSkillsBaseDir, symlinkName);
|
|
58
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
59
|
+
const sourceSkillPath = path.join(extractDir, 'SKILL.md');
|
|
60
|
+
const targetSkillPath = path.join(targetDir, 'SKILL.md');
|
|
61
|
+
const baseName = stripScope(skillName);
|
|
62
|
+
if (!fs.existsSync(sourceSkillPath)) {
|
|
63
|
+
const fallbackDescription = description ?? 'An AI agent skill';
|
|
64
|
+
const minimal = generateFrontmatter(baseName, fallbackDescription);
|
|
65
|
+
fs.writeFileSync(targetSkillPath, minimal, 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const content = fs.readFileSync(sourceSkillPath, 'utf-8');
|
|
69
|
+
if (hasFrontmatter(content)) {
|
|
70
|
+
fs.writeFileSync(targetSkillPath, content, 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const resolvedDescription = description ?? extractDescriptionFromMarkdown(content);
|
|
74
|
+
const frontmatter = generateFrontmatter(baseName, resolvedDescription);
|
|
75
|
+
fs.writeFileSync(targetSkillPath, `${frontmatter}${content}`, 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const entries = fs.readdirSync(extractDir, { withFileTypes: true });
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (entry.name === 'SKILL.md') {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const sourcePath = path.join(extractDir, entry.name);
|
|
84
|
+
const targetPath = path.join(targetDir, entry.name);
|
|
85
|
+
fs.cpSync(sourcePath, targetPath, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
return targetDir;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/lib/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface LinkResult {
|
|
2
|
+
linked: string[];
|
|
3
|
+
skipped: string[];
|
|
4
|
+
failed: Array<{
|
|
5
|
+
agentId: string;
|
|
6
|
+
error: string;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export interface UnlinkResult {
|
|
10
|
+
unlinked: string[];
|
|
11
|
+
notFound: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface AgentLinkStatus {
|
|
14
|
+
agentId: string;
|
|
15
|
+
agentName: string;
|
|
16
|
+
linked: boolean;
|
|
17
|
+
symlinkPath: string;
|
|
18
|
+
targetValid: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface LinkOptions {
|
|
21
|
+
skillName: string;
|
|
22
|
+
sourceDir: string;
|
|
23
|
+
linksDir: string;
|
|
24
|
+
source: 'local' | 'global' | 'dev';
|
|
25
|
+
homedir?: string;
|
|
26
|
+
}
|
|
27
|
+
interface UnlinkOptions {
|
|
28
|
+
skillName: string;
|
|
29
|
+
linksDir: string;
|
|
30
|
+
homedir?: string;
|
|
31
|
+
}
|
|
32
|
+
interface StatusOptions {
|
|
33
|
+
skillName: string;
|
|
34
|
+
linksDir: string;
|
|
35
|
+
homedir?: string;
|
|
36
|
+
}
|
|
37
|
+
interface AllStatusOptions {
|
|
38
|
+
linksDir: string;
|
|
39
|
+
homedir?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function linkSkillToAgents(options: LinkOptions): LinkResult;
|
|
42
|
+
export declare function unlinkSkillFromAgents(options: UnlinkOptions): UnlinkResult;
|
|
43
|
+
export declare function getSkillLinkStatus(options: StatusOptions): AgentLinkStatus[];
|
|
44
|
+
export declare function getAllLinkStatuses(options: AllStatusOptions): Record<string, AgentLinkStatus[]>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { detectInstalledAgents, getSymlinkName } from './agents.js';
|
|
4
|
+
import { readLinks, writeLinks } from './links.js';
|
|
5
|
+
const resolveSymlinkTarget = (symlinkPath, target) => {
|
|
6
|
+
if (path.isAbsolute(target)) {
|
|
7
|
+
return target;
|
|
8
|
+
}
|
|
9
|
+
return path.resolve(path.dirname(symlinkPath), target);
|
|
10
|
+
};
|
|
11
|
+
const checkSymlink = (symlinkPath) => {
|
|
12
|
+
try {
|
|
13
|
+
const stats = fs.lstatSync(symlinkPath);
|
|
14
|
+
if (!stats.isSymbolicLink()) {
|
|
15
|
+
return { exists: true, isSymlink: false, targetPath: null, targetValid: false };
|
|
16
|
+
}
|
|
17
|
+
const rawTarget = fs.readlinkSync(symlinkPath);
|
|
18
|
+
const targetPath = resolveSymlinkTarget(symlinkPath, rawTarget);
|
|
19
|
+
const targetValid = fs.existsSync(targetPath);
|
|
20
|
+
return { exists: true, isSymlink: true, targetPath, targetValid };
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return { exists: false, isSymlink: false, targetPath: null, targetValid: false };
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const createEntry = (manifest, skillName, entry) => ({
|
|
27
|
+
version: manifest.version,
|
|
28
|
+
links: {
|
|
29
|
+
...manifest.links,
|
|
30
|
+
[skillName]: entry,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
export function linkSkillToAgents(options) {
|
|
34
|
+
const result = { linked: [], skipped: [], failed: [] };
|
|
35
|
+
const agents = detectInstalledAgents(options.homedir);
|
|
36
|
+
const symlinkName = getSymlinkName(options.skillName);
|
|
37
|
+
const resolvedSource = path.resolve(options.sourceDir);
|
|
38
|
+
const agentLinks = {};
|
|
39
|
+
for (const agent of agents) {
|
|
40
|
+
const symlinkPath = path.join(agent.skillsDir, symlinkName);
|
|
41
|
+
try {
|
|
42
|
+
fs.mkdirSync(agent.skillsDir, { recursive: true });
|
|
43
|
+
const check = checkSymlink(symlinkPath);
|
|
44
|
+
if (check.exists && !check.isSymlink) {
|
|
45
|
+
result.failed.push({
|
|
46
|
+
agentId: agent.id,
|
|
47
|
+
error: `Path exists and is not a symlink: ${symlinkPath}`,
|
|
48
|
+
});
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (check.exists && check.isSymlink && check.targetPath) {
|
|
52
|
+
const targetMatches = path.resolve(check.targetPath) === resolvedSource;
|
|
53
|
+
if (targetMatches && check.targetValid) {
|
|
54
|
+
result.skipped.push(agent.id);
|
|
55
|
+
agentLinks[agent.id] = symlinkPath;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
fs.unlinkSync(symlinkPath);
|
|
59
|
+
}
|
|
60
|
+
fs.symlinkSync(options.sourceDir, symlinkPath, 'dir');
|
|
61
|
+
result.linked.push(agent.id);
|
|
62
|
+
agentLinks[agent.id] = symlinkPath;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
result.failed.push({ agentId: agent.id, error: message });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const manifest = readLinks(options.linksDir);
|
|
70
|
+
const entry = {
|
|
71
|
+
source: options.source,
|
|
72
|
+
sourceDir: options.sourceDir,
|
|
73
|
+
installedAt: new Date().toISOString(),
|
|
74
|
+
agentLinks,
|
|
75
|
+
};
|
|
76
|
+
const updated = createEntry(manifest, options.skillName, entry);
|
|
77
|
+
writeLinks(options.linksDir, updated);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
export function unlinkSkillFromAgents(options) {
|
|
81
|
+
const manifest = readLinks(options.linksDir);
|
|
82
|
+
const entry = manifest.links[options.skillName];
|
|
83
|
+
if (!entry) {
|
|
84
|
+
return { unlinked: [], notFound: [] };
|
|
85
|
+
}
|
|
86
|
+
const result = { unlinked: [], notFound: [] };
|
|
87
|
+
for (const [agentId, symlinkPath] of Object.entries(entry.agentLinks)) {
|
|
88
|
+
try {
|
|
89
|
+
const stats = fs.lstatSync(symlinkPath);
|
|
90
|
+
if (!stats.isSymbolicLink()) {
|
|
91
|
+
result.notFound.push(agentId);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
fs.unlinkSync(symlinkPath);
|
|
95
|
+
result.unlinked.push(agentId);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
result.notFound.push(agentId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const updated = {
|
|
102
|
+
version: manifest.version,
|
|
103
|
+
links: { ...manifest.links },
|
|
104
|
+
};
|
|
105
|
+
if (options.skillName in updated.links) {
|
|
106
|
+
delete updated.links[options.skillName];
|
|
107
|
+
}
|
|
108
|
+
writeLinks(options.linksDir, updated);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
const getStatusForAgent = (agent, skillName) => {
|
|
112
|
+
const symlinkName = getSymlinkName(skillName);
|
|
113
|
+
const symlinkPath = path.join(agent.skillsDir, symlinkName);
|
|
114
|
+
const check = checkSymlink(symlinkPath);
|
|
115
|
+
return {
|
|
116
|
+
agentId: agent.id,
|
|
117
|
+
agentName: agent.name,
|
|
118
|
+
linked: check.exists && check.isSymlink,
|
|
119
|
+
symlinkPath,
|
|
120
|
+
targetValid: check.exists && check.isSymlink && check.targetValid,
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
export function getSkillLinkStatus(options) {
|
|
124
|
+
const agents = detectInstalledAgents(options.homedir);
|
|
125
|
+
readLinks(options.linksDir);
|
|
126
|
+
return agents.map((agent) => getStatusForAgent(agent, options.skillName));
|
|
127
|
+
}
|
|
128
|
+
export function getAllLinkStatuses(options) {
|
|
129
|
+
const agents = detectInstalledAgents(options.homedir);
|
|
130
|
+
const manifest = readLinks(options.linksDir);
|
|
131
|
+
const result = {};
|
|
132
|
+
for (const skillName of Object.keys(manifest.links)) {
|
|
133
|
+
result[skillName] = agents.map((agent) => getStatusForAgent(agent, skillName));
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=linker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linker.js","sourceRoot":"","sources":["../../src/lib/linker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAkB,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAsC,MAAM,YAAY,CAAC;AAqDvF,MAAM,oBAAoB,GAAG,CAAC,WAAmB,EAAE,MAAc,EAAU,EAAE;IAC3E,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,WAAmB,EAAgB,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;QAClF,CAAC;QAED,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,oBAAoB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IACnF,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAClB,QAAuB,EACvB,SAAiB,EACjB,KAAgB,EACD,EAAE,CAAC,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,KAAK,EAAE;QACL,GAAG,QAAQ,CAAC,KAAK;QACjB,CAAC,SAAS,CAAC,EAAE,KAAK;KACnB;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,OAAoB;IACpD,MAAM,MAAM,GAAe,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACvD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAE5D,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;YAExC,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACrC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACjB,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,KAAK,EAAE,qCAAqC,WAAW,EAAE;iBAC1D,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACxD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,cAAc,CAAC;gBACxE,IAAI,aAAa,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;oBACvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC9B,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC;oBACnC,SAAS;gBACX,CAAC;gBAED,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC7B,CAAC;YAED,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;YACtD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC7B,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAc;QACvB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,UAAU;KACX,CAAC;IACF,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAChE,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEtC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAsB;IAC1D,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACxC,CAAC;IAED,MAAM,MAAM,GAAiB,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAC5D,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC9B,SAAS;YACX,CAAC;YAED,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC3B,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAkB;QAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE;KAC7B,CAAC;IACF,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IACD,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEtC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,iBAAiB,GAAG,CACxB,KAAgB,EAChB,SAAiB,EACA,EAAE;IACnB,MAAM,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAExC,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,EAAE;QACjB,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS;QACvC,WAAW;QACX,WAAW,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW;KAClE,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,OAAsB;IACvD,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAyB;IAC1D,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAsC,EAAE,CAAC;IAErD,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface LinkEntry {
|
|
2
|
+
source: 'local' | 'global' | 'dev';
|
|
3
|
+
sourceDir: string;
|
|
4
|
+
installedAt: string;
|
|
5
|
+
agentLinks: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export interface LinksManifest {
|
|
8
|
+
version: 1;
|
|
9
|
+
links: Record<string, LinkEntry>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createEmptyManifest(): LinksManifest;
|
|
12
|
+
export declare function readLinks(linksDir: string): LinksManifest;
|
|
13
|
+
export declare function writeLinks(linksDir: string, manifest: LinksManifest): void;
|
|
14
|
+
export declare function readLocalLinks(projectDir?: string): LinksManifest;
|
|
15
|
+
export declare function readGlobalLinks(homedir?: string): LinksManifest;
|
|
16
|
+
export declare function addLink(manifest: LinksManifest, skillName: string, entry: LinkEntry): LinksManifest;
|
|
17
|
+
export declare function removeLink(manifest: LinksManifest, skillName: string): LinksManifest;
|
|
18
|
+
export declare function getLinksForSkill(manifest: LinksManifest, skillName: string): LinkEntry | undefined;
|
|
19
|
+
export declare function addAgentLink(manifest: LinksManifest, skillName: string, agentId: string, symlinkPath: string): LinksManifest;
|
|
20
|
+
export declare function removeAgentLink(manifest: LinksManifest, skillName: string, agentId: string): LinksManifest;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
export function createEmptyManifest() {
|
|
5
|
+
return { version: 1, links: {} };
|
|
6
|
+
}
|
|
7
|
+
export function readLinks(linksDir) {
|
|
8
|
+
if (!fs.existsSync(linksDir)) {
|
|
9
|
+
return createEmptyManifest();
|
|
10
|
+
}
|
|
11
|
+
const linksPath = path.join(linksDir, 'links.json');
|
|
12
|
+
if (!fs.existsSync(linksPath)) {
|
|
13
|
+
return createEmptyManifest();
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(linksPath, 'utf-8');
|
|
17
|
+
return JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return createEmptyManifest();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function writeLinks(linksDir, manifest) {
|
|
24
|
+
if (!fs.existsSync(linksDir)) {
|
|
25
|
+
fs.mkdirSync(linksDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const sortedLinks = {};
|
|
28
|
+
for (const skillName of Object.keys(manifest.links).sort()) {
|
|
29
|
+
const entry = manifest.links[skillName];
|
|
30
|
+
const sortedAgentLinks = {};
|
|
31
|
+
for (const agentId of Object.keys(entry.agentLinks).sort()) {
|
|
32
|
+
sortedAgentLinks[agentId] = entry.agentLinks[agentId];
|
|
33
|
+
}
|
|
34
|
+
sortedLinks[skillName] = {
|
|
35
|
+
source: entry.source,
|
|
36
|
+
sourceDir: entry.sourceDir,
|
|
37
|
+
installedAt: entry.installedAt,
|
|
38
|
+
agentLinks: sortedAgentLinks,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const output = {
|
|
42
|
+
version: manifest.version,
|
|
43
|
+
links: sortedLinks,
|
|
44
|
+
};
|
|
45
|
+
fs.writeFileSync(path.join(linksDir, 'links.json'), JSON.stringify(output, null, 2) + '\n');
|
|
46
|
+
}
|
|
47
|
+
export function readLocalLinks(projectDir) {
|
|
48
|
+
const dir = projectDir ?? process.cwd();
|
|
49
|
+
return readLinks(path.join(dir, '.tank'));
|
|
50
|
+
}
|
|
51
|
+
export function readGlobalLinks(homedir) {
|
|
52
|
+
const home = homedir ?? os.homedir();
|
|
53
|
+
return readLinks(path.join(home, '.tank'));
|
|
54
|
+
}
|
|
55
|
+
export function addLink(manifest, skillName, entry) {
|
|
56
|
+
return {
|
|
57
|
+
version: manifest.version,
|
|
58
|
+
links: {
|
|
59
|
+
...manifest.links,
|
|
60
|
+
[skillName]: entry,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function removeLink(manifest, skillName) {
|
|
65
|
+
const links = { ...manifest.links };
|
|
66
|
+
if (skillName in links) {
|
|
67
|
+
delete links[skillName];
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
version: manifest.version,
|
|
71
|
+
links,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function getLinksForSkill(manifest, skillName) {
|
|
75
|
+
return manifest.links[skillName];
|
|
76
|
+
}
|
|
77
|
+
export function addAgentLink(manifest, skillName, agentId, symlinkPath) {
|
|
78
|
+
const existing = manifest.links[skillName];
|
|
79
|
+
const baseEntry = existing ?? {
|
|
80
|
+
source: 'local',
|
|
81
|
+
sourceDir: process.cwd(),
|
|
82
|
+
installedAt: new Date().toISOString(),
|
|
83
|
+
agentLinks: {},
|
|
84
|
+
};
|
|
85
|
+
const agentLinks = {
|
|
86
|
+
...baseEntry.agentLinks,
|
|
87
|
+
[agentId]: symlinkPath,
|
|
88
|
+
};
|
|
89
|
+
return addLink(manifest, skillName, { ...baseEntry, agentLinks });
|
|
90
|
+
}
|
|
91
|
+
export function removeAgentLink(manifest, skillName, agentId) {
|
|
92
|
+
const existing = manifest.links[skillName];
|
|
93
|
+
if (!existing) {
|
|
94
|
+
return { version: manifest.version, links: { ...manifest.links } };
|
|
95
|
+
}
|
|
96
|
+
const agentLinks = { ...existing.agentLinks };
|
|
97
|
+
if (agentId in agentLinks) {
|
|
98
|
+
delete agentLinks[agentId];
|
|
99
|
+
}
|
|
100
|
+
if (Object.keys(agentLinks).length === 0) {
|
|
101
|
+
return removeLink(manifest, skillName);
|
|
102
|
+
}
|
|
103
|
+
return addLink(manifest, skillName, { ...existing, agentLinks });
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/lib/links.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SkillsLock, Permissions } from '@tank/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Read and parse the lockfile from the given directory.
|
|
4
|
+
* Returns null if the file doesn't exist or is corrupt.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readLockfile(directory?: string): SkillsLock | null;
|
|
7
|
+
/**
|
|
8
|
+
* Write a lockfile deterministically: sorted keys, consistent formatting, trailing newline.
|
|
9
|
+
*/
|
|
10
|
+
export declare function writeLockfile(lock: SkillsLock, directory?: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Compute the union of all skill permissions from a lockfile.
|
|
13
|
+
* Merges network outbound, filesystem read/write (deduped), and subprocess (OR).
|
|
14
|
+
*/
|
|
15
|
+
export declare function computeResolvedPermissions(lock: SkillsLock): Permissions;
|
|
16
|
+
/**
|
|
17
|
+
* Check if resolved permissions fit within the project's permission budget.
|
|
18
|
+
*
|
|
19
|
+
* Returns:
|
|
20
|
+
* - 'pass' if all resolved permissions are within budget
|
|
21
|
+
* - 'fail' if any resolved permission exceeds budget
|
|
22
|
+
* - 'no_budget' if no project permissions are defined
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeBudgetCheck(resolvedPermissions: Permissions, projectPermissions: Permissions | undefined): 'pass' | 'fail' | 'no_budget';
|