@michael-joseph-miller/ant-bot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/browser-OHRD7YI3.js +524 -0
- package/dist/browser-OHRD7YI3.js.map +7 -0
- package/dist/bundled-46DUK5PG.js +133 -0
- package/dist/bundled-46DUK5PG.js.map +7 -0
- package/dist/chunk-7BOHBPB2.js +287 -0
- package/dist/chunk-7BOHBPB2.js.map +7 -0
- package/dist/chunk-AHAON6J7.js +24 -0
- package/dist/chunk-AHAON6J7.js.map +7 -0
- package/dist/chunk-DYIJTUMY.js +321 -0
- package/dist/chunk-DYIJTUMY.js.map +7 -0
- package/dist/chunk-KSQOVWP5.js +310 -0
- package/dist/chunk-KSQOVWP5.js.map +7 -0
- package/dist/chunk-Z2YT2PZN.js +64 -0
- package/dist/chunk-Z2YT2PZN.js.map +7 -0
- package/dist/index.js +1562 -0
- package/dist/index.js.map +7 -0
- package/dist/install-IY2M3OUQ.js +36 -0
- package/dist/install-IY2M3OUQ.js.map +7 -0
- package/dist/plugin-POMHVGD4.js +53 -0
- package/dist/plugin-POMHVGD4.js.map +7 -0
- package/dist/scheduler-Q7OHNGP6.js +274 -0
- package/dist/scheduler-Q7OHNGP6.js.map +7 -0
- package/dist/server.js +3016 -0
- package/dist/server.js.map +7 -0
- package/dist/skills-66WRX64H.js +17 -0
- package/dist/skills-66WRX64H.js.map +7 -0
- package/dist/skills-spec.js +172 -0
- package/dist/skills-spec.js.map +7 -0
- package/dist/tools-P5537ASX.js +148 -0
- package/dist/tools-P5537ASX.js.map +7 -0
- package/package.json +51 -0
- package/skills/README.md +69 -0
- package/skills/SPEC.md +274 -0
- package/skills/bug-repro/SKILL.md +53 -0
- package/skills/deep-research/SKILL.md +451 -0
- package/skills/deep-research/references/V6_1_improvements.md +112 -0
- package/skills/deep-research/references/completeness_review_checklist.md +25 -0
- package/skills/deep-research/references/counter_review_team_guide.md +181 -0
- package/skills/deep-research/references/enterprise_analysis_frameworks.md +135 -0
- package/skills/deep-research/references/enterprise_mode.md +99 -0
- package/skills/deep-research/references/enterprise_quality_checklist.md +160 -0
- package/skills/deep-research/references/enterprise_research_methodology.md +164 -0
- package/skills/deep-research/references/formatting_rules.md +31 -0
- package/skills/deep-research/references/quality_gates.md +77 -0
- package/skills/deep-research/references/report_template_v6.md +82 -0
- package/skills/deep-research/references/research_notes_format.md +147 -0
- package/skills/deep-research/references/research_plan_checklist.md +26 -0
- package/skills/deep-research/references/research_report_template.md +49 -0
- package/skills/deep-research/references/source_accessibility_policy.md +179 -0
- package/skills/deep-research/references/source_quality_rubric.md +28 -0
- package/skills/deep-research/references/subagent_prompt.md +116 -0
- package/skills/inbox-digest/SKILL.md +51 -0
- package/skills/skill-author/SKILL.md +92 -0
- package/skills/weekly-report/SKILL.md +49 -0
- package/web/dist/assets/index-BLQ8rPiN.js +130 -0
- package/web/dist/assets/index-IEIkG_jd.css +2 -0
- package/web/dist/index.html +13 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createRequire as __antbotCreateRequire } from 'node:module';
|
|
2
|
+
const require = __antbotCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
findBundledSkillsDir,
|
|
5
|
+
nodeLocateDeps
|
|
6
|
+
} from "./chunk-Z2YT2PZN.js";
|
|
7
|
+
import {
|
|
8
|
+
copyTree
|
|
9
|
+
} from "./chunk-7BOHBPB2.js";
|
|
10
|
+
import "./chunk-KSQOVWP5.js";
|
|
11
|
+
|
|
12
|
+
// packages/server/src/skills/bundled.ts
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import crypto from "node:crypto";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
var LEDGER_FILE = ".managed.json";
|
|
18
|
+
var LEDGER_VERSION = 1;
|
|
19
|
+
var KNOWN_PRIOR_HASHES = {
|
|
20
|
+
// 0.1.0, seeded from skills-examples/ — before the skills were renamed to spec-conformant
|
|
21
|
+
// frontmatter names and deep-research got its references/ directory.
|
|
22
|
+
"bug-repro": ["e8faa77eb98c3ba4d7618731d946c233354d158b3ecae7b1bed5908dce3e9cac"],
|
|
23
|
+
"deep-research": ["33f7482fa2d136dad53c556b5a25527136838f8bd72d35a9657f45e6c21f391b"],
|
|
24
|
+
"inbox-digest": ["45e137d9d6ce26ad6e230ef1b0711ad7d6b4be06d744f42accea2700f0039361"],
|
|
25
|
+
"weekly-report": ["fb05e984b96e8771b236602b47a82f1ec600bd307b44878cd92694cdb658a852"]
|
|
26
|
+
};
|
|
27
|
+
function planSkillSync(states) {
|
|
28
|
+
return states.map(({ slug, shippedHash, installedHash, ledgerHash, priorHashes }) => {
|
|
29
|
+
if (installedHash === null) {
|
|
30
|
+
return { slug, action: ledgerHash === void 0 ? "install" : "skip-deleted" };
|
|
31
|
+
}
|
|
32
|
+
if (ledgerHash === void 0) {
|
|
33
|
+
if (installedHash === shippedHash) return { slug, action: "adopt" };
|
|
34
|
+
if (priorHashes?.includes(installedHash)) return { slug, action: "update" };
|
|
35
|
+
return { slug, action: "skip-foreign" };
|
|
36
|
+
}
|
|
37
|
+
if (installedHash !== ledgerHash) return { slug, action: "skip-modified" };
|
|
38
|
+
return { slug, action: installedHash === shippedHash ? "unchanged" : "update" };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function hashSkillDir(dir) {
|
|
42
|
+
const h = crypto.createHash("sha256");
|
|
43
|
+
for (const rel of listFilesRecursive(dir).sort()) {
|
|
44
|
+
h.update(rel.split(path.sep).join("/"));
|
|
45
|
+
h.update("\0");
|
|
46
|
+
h.update(fs.readFileSync(path.join(dir, rel)));
|
|
47
|
+
h.update("\0");
|
|
48
|
+
}
|
|
49
|
+
return h.digest("hex");
|
|
50
|
+
}
|
|
51
|
+
function listFilesRecursive(dir, prefix = "") {
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
54
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
55
|
+
if (entry.isSymbolicLink()) continue;
|
|
56
|
+
const rel = path.join(prefix, entry.name);
|
|
57
|
+
if (entry.isDirectory()) out.push(...listFilesRecursive(path.join(dir, entry.name), rel));
|
|
58
|
+
else if (entry.isFile()) out.push(rel);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
function readLedger(targetDir) {
|
|
63
|
+
try {
|
|
64
|
+
const raw = fs.readFileSync(path.join(targetDir, LEDGER_FILE), "utf8");
|
|
65
|
+
const parsed = JSON.parse(raw);
|
|
66
|
+
if (parsed.version !== LEDGER_VERSION || typeof parsed.skills !== "object" || !parsed.skills) {
|
|
67
|
+
return { version: LEDGER_VERSION, skills: {} };
|
|
68
|
+
}
|
|
69
|
+
return { version: LEDGER_VERSION, skills: parsed.skills };
|
|
70
|
+
} catch {
|
|
71
|
+
return { version: LEDGER_VERSION, skills: {} };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function writeLedger(targetDir, ledger) {
|
|
75
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
76
|
+
fs.writeFileSync(path.join(targetDir, LEDGER_FILE), `${JSON.stringify(ledger, null, 2)}
|
|
77
|
+
`, "utf8");
|
|
78
|
+
}
|
|
79
|
+
function defaultBundledSkillsDir() {
|
|
80
|
+
return findBundledSkillsDir(
|
|
81
|
+
nodeLocateDeps(path.dirname(fileURLToPath(import.meta.url)), () => null)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function syncBundledSkills(targetDir, bundledDir = defaultBundledSkillsDir()) {
|
|
85
|
+
if (!fs.existsSync(bundledDir)) return [];
|
|
86
|
+
const ledger = readLedger(targetDir);
|
|
87
|
+
const states = [];
|
|
88
|
+
const shippedDirs = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const entry of fs.readdirSync(bundledDir, { withFileTypes: true })) {
|
|
90
|
+
if (!entry.isDirectory()) continue;
|
|
91
|
+
const src = path.join(bundledDir, entry.name);
|
|
92
|
+
if (!fs.existsSync(path.join(src, "SKILL.md"))) continue;
|
|
93
|
+
const dest = path.join(targetDir, entry.name);
|
|
94
|
+
shippedDirs.set(entry.name, src);
|
|
95
|
+
states.push({
|
|
96
|
+
slug: entry.name,
|
|
97
|
+
shippedHash: hashSkillDir(src),
|
|
98
|
+
installedHash: fs.existsSync(path.join(dest, "SKILL.md")) ? hashSkillDir(dest) : null,
|
|
99
|
+
ledgerHash: ledger.skills[entry.name]?.hash,
|
|
100
|
+
priorHashes: KNOWN_PRIOR_HASHES[entry.name]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (states.length === 0) return [];
|
|
104
|
+
const decisions = planSkillSync(states);
|
|
105
|
+
const byslug = new Map(states.map((s) => [s.slug, s]));
|
|
106
|
+
const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
107
|
+
let ledgerChanged = false;
|
|
108
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
109
|
+
for (const decision of decisions) {
|
|
110
|
+
const state = byslug.get(decision.slug);
|
|
111
|
+
if (decision.action === "install" || decision.action === "update") {
|
|
112
|
+
const dest = path.join(targetDir, decision.slug);
|
|
113
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
114
|
+
copyTree(shippedDirs.get(decision.slug), dest);
|
|
115
|
+
} else if (decision.action !== "adopt") {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
ledger.skills[decision.slug] = { hash: state.shippedHash, syncedAt };
|
|
119
|
+
ledgerChanged = true;
|
|
120
|
+
}
|
|
121
|
+
if (ledgerChanged) writeLedger(targetDir, ledger);
|
|
122
|
+
return decisions;
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
KNOWN_PRIOR_HASHES,
|
|
126
|
+
defaultBundledSkillsDir,
|
|
127
|
+
hashSkillDir,
|
|
128
|
+
planSkillSync,
|
|
129
|
+
readLedger,
|
|
130
|
+
syncBundledSkills,
|
|
131
|
+
writeLedger
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=bundled-46DUK5PG.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../packages/server/src/skills/bundled.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { fileURLToPath } from 'node:url';\nimport { copyTree } from './install.js';\nimport { findBundledSkillsDir, nodeLocateDeps } from '../util/locate.js';\n\n/**\n * Skills that ship with ant-bot itself, in the repo's (and the published package's)\n * `skills/` directory, installed into the user's skills directory on boot.\n *\n * The hard part is not copying files, it is deciding what to copy *again*. A bundled skill\n * has two owners: ant-bot ships updates to it, and the user is free to edit or delete it.\n * Refreshing unconditionally throws away the user's edits and resurrects what they deleted;\n * seeding once means a shipped fix never reaches anyone who already has the old copy. So the\n * hash of what we wrote is recorded in a ledger, and on the next boot the on-disk copy is\n * compared against it: untouched copies track upstream, touched ones are left alone. This is\n * the same bargain a package manager makes with an edited config file.\n */\n\nconst LEDGER_FILE = '.managed.json';\nconst LEDGER_VERSION = 1;\n\nexport interface LedgerEntry {\n /** Hash of the skill directory as ant-bot last wrote it. */\n hash: string;\n syncedAt: string;\n}\n\nexport interface Ledger {\n version: number;\n skills: Record<string, LedgerEntry>;\n}\n\nexport type SkillSyncAction =\n /** Not on disk and never seeded \u2014 a first install. */\n | 'install'\n /** Ours, untouched by the user, and the shipped copy changed. */\n | 'update'\n /** Not in the ledger but byte-identical to what ships \u2014 adopt it so it tracks upstream. */\n | 'adopt'\n /** Ours, untouched, already current. */\n | 'unchanged'\n /** Ours, but the user edited it. */\n | 'skip-modified'\n /** Same slug, different content, never seeded by us \u2014 someone else's skill. */\n | 'skip-foreign'\n /** Seeded once and since deleted by the user. */\n | 'skip-deleted';\n\nexport interface SkillSyncState {\n slug: string;\n /** Hash of the skill as it ships with this build. */\n shippedHash: string;\n /** Hash of the copy in the user's skills directory, or null if it is not there. */\n installedHash: string | null;\n /** Hash recorded the last time ant-bot wrote this skill, or undefined if it never did. */\n ledgerHash: string | undefined;\n /** Hashes this skill shipped with in earlier ant-bot versions. See KNOWN_PRIOR_HASHES. */\n priorHashes?: string[];\n}\n\n/**\n * Hashes of skills as earlier ant-bot versions shipped them, before the ledger existed.\n *\n * Without this an upgrade that changes a shipped skill can never reach anyone who already has\n * it: their copy is not in the ledger and no longer matches what ships, so it reads as someone\n * else's skill and is left alone forever. Matching a known prior hash proves the copy is an\n * untouched older version of ours, which is safe to replace. This is the same record dpkg keeps\n * for conffiles, and it is append-only \u2014 add the outgoing hash here whenever a shipped skill\n * changes, never remove one.\n */\nexport const KNOWN_PRIOR_HASHES: Record<string, string[]> = {\n // 0.1.0, seeded from skills-examples/ \u2014 before the skills were renamed to spec-conformant\n // frontmatter names and deep-research got its references/ directory.\n 'bug-repro': ['e8faa77eb98c3ba4d7618731d946c233354d158b3ecae7b1bed5908dce3e9cac'],\n 'deep-research': ['33f7482fa2d136dad53c556b5a25527136838f8bd72d35a9657f45e6c21f391b'],\n 'inbox-digest': ['45e137d9d6ce26ad6e230ef1b0711ad7d6b4be06d744f42accea2700f0039361'],\n 'weekly-report': ['fb05e984b96e8771b236602b47a82f1ec600bd307b44878cd92694cdb658a852'],\n};\n\nexport interface SkillSyncDecision {\n slug: string;\n action: SkillSyncAction;\n}\n\n/**\n * Decide what to do with each bundled skill. Pure, so every branch below is testable without\n * touching a filesystem \u2014 and there are more branches here than the copying deserves.\n */\nexport function planSkillSync(states: SkillSyncState[]): SkillSyncDecision[] {\n return states.map(({ slug, shippedHash, installedHash, ledgerHash, priorHashes }): SkillSyncDecision => {\n if (installedHash === null) {\n // A ledger entry with nothing on disk means the user deleted it. Putting it back on\n // every boot would make deletion impossible.\n return { slug, action: ledgerHash === undefined ? 'install' : 'skip-deleted' };\n }\n if (ledgerHash === undefined) {\n // Never seeded by us. Byte-identical to what ships means a pre-ledger seed from an older\n // ant-bot: adopt it as-is. Matching a hash we are known to have shipped before means an\n // untouched *older* copy of ours, safe to bring up to date. Anything else belongs to\n // whoever put it there (`antbot skill add`, or dropped in by hand) and we keep our hands off.\n if (installedHash === shippedHash) return { slug, action: 'adopt' };\n if (priorHashes?.includes(installedHash)) return { slug, action: 'update' };\n return { slug, action: 'skip-foreign' };\n }\n if (installedHash !== ledgerHash) return { slug, action: 'skip-modified' };\n return { slug, action: installedHash === shippedHash ? 'unchanged' : 'update' };\n });\n}\n\n/** Hash a skill directory: every file's path and contents, in a stable order. */\nexport function hashSkillDir(dir: string): string {\n const h = crypto.createHash('sha256');\n for (const rel of listFilesRecursive(dir).sort()) {\n h.update(rel.split(path.sep).join('/'));\n h.update('\\0');\n h.update(fs.readFileSync(path.join(dir, rel)));\n h.update('\\0');\n }\n return h.digest('hex');\n}\n\nfunction listFilesRecursive(dir: string, prefix = ''): string[] {\n const out: string[] = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n // Mirrors copyTree: what is not copied must not be hashed, or a skill would look\n // modified forever.\n if (entry.name === '.git' || entry.name === 'node_modules') continue;\n if (entry.isSymbolicLink()) continue;\n const rel = path.join(prefix, entry.name);\n if (entry.isDirectory()) out.push(...listFilesRecursive(path.join(dir, entry.name), rel));\n else if (entry.isFile()) out.push(rel);\n }\n return out;\n}\n\nexport function readLedger(targetDir: string): Ledger {\n try {\n const raw = fs.readFileSync(path.join(targetDir, LEDGER_FILE), 'utf8');\n const parsed = JSON.parse(raw) as Partial<Ledger>;\n if (parsed.version !== LEDGER_VERSION || typeof parsed.skills !== 'object' || !parsed.skills) {\n return { version: LEDGER_VERSION, skills: {} };\n }\n return { version: LEDGER_VERSION, skills: parsed.skills };\n } catch {\n // No ledger, or an unreadable one. Treating it as empty is safe: every on-disk skill then\n // looks foreign or adoptable, and nothing gets overwritten.\n return { version: LEDGER_VERSION, skills: {} };\n }\n}\n\nexport function writeLedger(targetDir: string, ledger: Ledger): void {\n fs.mkdirSync(targetDir, { recursive: true });\n fs.writeFileSync(path.join(targetDir, LEDGER_FILE), `${JSON.stringify(ledger, null, 2)}\\n`, 'utf8');\n}\n\n/** Location of the skills that ship with ant-bot; see `util/locate.ts` for the candidate order. */\nexport function defaultBundledSkillsDir(): string {\n return findBundledSkillsDir(\n nodeLocateDeps(path.dirname(fileURLToPath(import.meta.url)), () => null),\n );\n}\n\n/**\n * Install or refresh ant-bot's own skills in `targetDir`. Safe to call on every boot; see the\n * module comment for why it is not a plain copy. Returns what it did, for the boot log.\n */\nexport function syncBundledSkills(\n targetDir: string,\n bundledDir: string = defaultBundledSkillsDir(),\n): SkillSyncDecision[] {\n if (!fs.existsSync(bundledDir)) return [];\n\n const ledger = readLedger(targetDir);\n const states: SkillSyncState[] = [];\n const shippedDirs = new Map<string, string>();\n\n for (const entry of fs.readdirSync(bundledDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const src = path.join(bundledDir, entry.name);\n if (!fs.existsSync(path.join(src, 'SKILL.md'))) continue;\n const dest = path.join(targetDir, entry.name);\n shippedDirs.set(entry.name, src);\n states.push({\n slug: entry.name,\n shippedHash: hashSkillDir(src),\n installedHash: fs.existsSync(path.join(dest, 'SKILL.md')) ? hashSkillDir(dest) : null,\n ledgerHash: ledger.skills[entry.name]?.hash,\n priorHashes: KNOWN_PRIOR_HASHES[entry.name],\n });\n }\n if (states.length === 0) return [];\n\n const decisions = planSkillSync(states);\n const byslug = new Map(states.map((s) => [s.slug, s]));\n const syncedAt = new Date().toISOString();\n let ledgerChanged = false;\n\n fs.mkdirSync(targetDir, { recursive: true });\n for (const decision of decisions) {\n const state = byslug.get(decision.slug)!;\n if (decision.action === 'install' || decision.action === 'update') {\n const dest = path.join(targetDir, decision.slug);\n fs.rmSync(dest, { recursive: true, force: true });\n copyTree(shippedDirs.get(decision.slug)!, dest);\n } else if (decision.action !== 'adopt') {\n continue;\n }\n ledger.skills[decision.slug] = { hash: state.shippedHash, syncedAt };\n ledgerChanged = true;\n }\n if (ledgerChanged) writeLedger(targetDir, ledger);\n\n return decisions;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { createRequire as __antbotCreateRequire } from 'node:module';
|
|
2
|
+
const require = __antbotCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
parseFrontmatter
|
|
5
|
+
} from "./chunk-KSQOVWP5.js";
|
|
6
|
+
|
|
7
|
+
// packages/server/src/skills/install.ts
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
13
|
+
".sh",
|
|
14
|
+
".bash",
|
|
15
|
+
".zsh",
|
|
16
|
+
".fish",
|
|
17
|
+
".py",
|
|
18
|
+
".rb",
|
|
19
|
+
".pl",
|
|
20
|
+
".js",
|
|
21
|
+
".mjs",
|
|
22
|
+
".cjs",
|
|
23
|
+
".ts",
|
|
24
|
+
".ps1",
|
|
25
|
+
".bat",
|
|
26
|
+
".cmd",
|
|
27
|
+
".exe",
|
|
28
|
+
".bin"
|
|
29
|
+
]);
|
|
30
|
+
function isExecutableFile(relPath, mode) {
|
|
31
|
+
if (SCRIPT_EXTENSIONS.has(path.extname(relPath).toLowerCase())) return true;
|
|
32
|
+
return (mode & 73) !== 0;
|
|
33
|
+
}
|
|
34
|
+
function classifyFiles(files) {
|
|
35
|
+
return {
|
|
36
|
+
files,
|
|
37
|
+
executables: files.filter((f) => isExecutableFile(f.relPath, f.mode)).map((f) => f.relPath),
|
|
38
|
+
totalBytes: files.reduce((n, f) => n + f.bytes, 0)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
var GITHUB_TREE = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/([^/#]+?)(?:\.git)?\/(?:tree|blob)\/([^/]+)(?:\/(.*))?$/;
|
|
42
|
+
var GITHUB_REPO = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^/]+)\/([^/#]+?)(?:\.git)?$/;
|
|
43
|
+
var OWNER_REPO = /^([A-Za-z0-9][\w.-]*)\/([\w.-]+)$/;
|
|
44
|
+
function parseSkillSource(rawSpec) {
|
|
45
|
+
const spec = rawSpec.trim();
|
|
46
|
+
if (!spec) throw new Error("A skill source is required.");
|
|
47
|
+
if (spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/") || spec.startsWith("~")) {
|
|
48
|
+
const expanded = spec.startsWith("~") ? path.join(os.homedir(), spec.slice(1)) : spec;
|
|
49
|
+
return { kind: "path", path: expanded };
|
|
50
|
+
}
|
|
51
|
+
if (spec.startsWith("git@") || spec.startsWith("ssh://") || spec.endsWith(".git")) {
|
|
52
|
+
const [url, ref2] = splitRef(spec);
|
|
53
|
+
return { kind: "git", url, ref: ref2, subdir: void 0 };
|
|
54
|
+
}
|
|
55
|
+
const tree = GITHUB_TREE.exec(spec);
|
|
56
|
+
if (tree) {
|
|
57
|
+
const [, owner, repo, ref2, subdir] = tree;
|
|
58
|
+
const dir = subdir ? subdir.replace(/(^|\/)SKILL\.md$/i, "") : "";
|
|
59
|
+
return { kind: "git", url: `https://github.com/${owner}/${repo}`, ref: ref2, subdir: dir || void 0 };
|
|
60
|
+
}
|
|
61
|
+
const [base, ref] = splitRef(spec);
|
|
62
|
+
const repoMatch = GITHUB_REPO.exec(base);
|
|
63
|
+
if (repoMatch) {
|
|
64
|
+
const [, owner, repo] = repoMatch;
|
|
65
|
+
return { kind: "git", url: `https://github.com/${owner}/${repo}`, ref, subdir: void 0 };
|
|
66
|
+
}
|
|
67
|
+
if (/^https?:\/\//.test(base)) {
|
|
68
|
+
if (/\/SKILL\.md$/i.test(base)) return { kind: "url", url: base };
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Unsupported URL: ${base}
|
|
71
|
+
Supported: a git repository, or a direct link ending in /SKILL.md.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const ownerRepo = OWNER_REPO.exec(base);
|
|
75
|
+
if (ownerRepo) {
|
|
76
|
+
const [, owner, repo] = ownerRepo;
|
|
77
|
+
return { kind: "git", url: `https://github.com/${owner}/${repo}`, ref, subdir: void 0 };
|
|
78
|
+
}
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Could not understand skill source "${rawSpec}".
|
|
81
|
+
Try: owner/repo, github.com/owner/repo, a git URL, a local path, or a URL ending in /SKILL.md.`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
function describeSkillSource(rawSpec) {
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = parseSkillSource(rawSpec);
|
|
88
|
+
} catch {
|
|
89
|
+
return `Install skill from ${rawSpec}`;
|
|
90
|
+
}
|
|
91
|
+
const where = (u) => u.replace(/^https?:\/\//, "");
|
|
92
|
+
switch (parsed.kind) {
|
|
93
|
+
case "url":
|
|
94
|
+
return `Install one skill from ${where(parsed.url)}`;
|
|
95
|
+
case "path":
|
|
96
|
+
return `Install skill(s) from the local path ${parsed.path}`;
|
|
97
|
+
case "git": {
|
|
98
|
+
const ref = parsed.ref ? `#${parsed.ref}` : "";
|
|
99
|
+
return parsed.subdir ? `Install "${parsed.subdir}" from ${where(parsed.url)}${ref}` : `Install EVERY skill in ${where(parsed.url)}${ref} (whole repository)`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function splitRef(spec) {
|
|
104
|
+
const hash = spec.lastIndexOf("#");
|
|
105
|
+
if (hash === -1) return [spec, void 0];
|
|
106
|
+
return [spec.slice(0, hash), spec.slice(hash + 1) || void 0];
|
|
107
|
+
}
|
|
108
|
+
function walkFiles(root, rel = "") {
|
|
109
|
+
const out = [];
|
|
110
|
+
const dir = path.join(root, rel);
|
|
111
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
112
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
113
|
+
const childRel = rel ? path.join(rel, entry.name) : entry.name;
|
|
114
|
+
const full = path.join(root, childRel);
|
|
115
|
+
if (entry.isSymbolicLink()) continue;
|
|
116
|
+
if (entry.isDirectory()) out.push(...walkFiles(root, childRel));
|
|
117
|
+
else if (entry.isFile()) {
|
|
118
|
+
const st = fs.statSync(full);
|
|
119
|
+
out.push({ relPath: childRel, bytes: st.size, mode: st.mode });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
function findSkillDirs(root) {
|
|
125
|
+
const found = [];
|
|
126
|
+
const visit = (dir, depth) => {
|
|
127
|
+
if (depth > 5) return;
|
|
128
|
+
let entries;
|
|
129
|
+
try {
|
|
130
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
131
|
+
} catch {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (entries.some((e) => e.isFile() && e.name.toLowerCase() === "skill.md")) {
|
|
135
|
+
found.push(dir);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
for (const e of entries) {
|
|
139
|
+
if (!e.isDirectory() || e.name === ".git" || e.name === "node_modules") continue;
|
|
140
|
+
visit(path.join(dir, e.name), depth + 1);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
visit(root, 0);
|
|
144
|
+
return found.sort();
|
|
145
|
+
}
|
|
146
|
+
function copyTree(src, dest) {
|
|
147
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
148
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
149
|
+
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
150
|
+
if (entry.isSymbolicLink()) continue;
|
|
151
|
+
const s = path.join(src, entry.name);
|
|
152
|
+
const d = path.join(dest, entry.name);
|
|
153
|
+
if (entry.isDirectory()) copyTree(s, d);
|
|
154
|
+
else if (entry.isFile()) fs.copyFileSync(s, d);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function runGit(args, cwd, timeoutMs = 12e4) {
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
160
|
+
let stderr = "";
|
|
161
|
+
const timer = setTimeout(() => {
|
|
162
|
+
child.kill("SIGKILL");
|
|
163
|
+
reject(new Error(`git ${args[0]} timed out after ${timeoutMs / 1e3}s`));
|
|
164
|
+
}, timeoutMs);
|
|
165
|
+
child.stderr?.on("data", (d) => stderr += String(d));
|
|
166
|
+
child.on("error", (err) => {
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
reject(new Error(`git is required to install from a repository: ${err.message}`));
|
|
169
|
+
});
|
|
170
|
+
child.on("close", (code) => {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
if (code === 0) resolve();
|
|
173
|
+
else reject(new Error(`git ${args[0]} failed: ${stderr.trim().split("\n").slice(-3).join(" ")}`));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
var MAX_SKILL_BYTES = 10 * 1024 * 1024;
|
|
178
|
+
function readStaged(dir) {
|
|
179
|
+
const file = path.join(dir, "SKILL.md");
|
|
180
|
+
const raw = fs.readFileSync(file, "utf8");
|
|
181
|
+
const { name, description } = parseFrontmatter(raw);
|
|
182
|
+
if (!name.trim()) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`${file} has no \`name\` in its frontmatter. A skill needs:
|
|
185
|
+
---
|
|
186
|
+
name: my-skill
|
|
187
|
+
description: what it does
|
|
188
|
+
---`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const manifest = classifyFiles(walkFiles(dir));
|
|
192
|
+
if (manifest.totalBytes > MAX_SKILL_BYTES) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Skill "${name}" is ${(manifest.totalBytes / 1048576).toFixed(1)} MB; the limit is ${MAX_SKILL_BYTES / 1048576} MB.`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return { name: name.trim(), description: description.trim(), dir, manifest };
|
|
198
|
+
}
|
|
199
|
+
async function stageFromPath(source) {
|
|
200
|
+
const target = path.resolve(source.path);
|
|
201
|
+
if (!fs.existsSync(target)) throw new Error(`Path not found: ${target}`);
|
|
202
|
+
const stat = fs.statSync(target);
|
|
203
|
+
if (stat.isFile()) {
|
|
204
|
+
if (path.basename(target).toLowerCase() !== "skill.md")
|
|
205
|
+
throw new Error(`Expected a directory or a SKILL.md file, got ${target}`);
|
|
206
|
+
return [readStaged(path.dirname(target))];
|
|
207
|
+
}
|
|
208
|
+
const dirs = findSkillDirs(target);
|
|
209
|
+
if (!dirs.length) throw new Error(`No SKILL.md found under ${target}`);
|
|
210
|
+
return dirs.map(readStaged);
|
|
211
|
+
}
|
|
212
|
+
async function stageFromGit(source, tmpRoot = os.tmpdir()) {
|
|
213
|
+
const dir = fs.mkdtempSync(path.join(tmpRoot, "antbot-skill-git-"));
|
|
214
|
+
const cleanup = () => fs.rmSync(dir, { recursive: true, force: true });
|
|
215
|
+
try {
|
|
216
|
+
const args = ["clone", "--depth", "1", "--quiet"];
|
|
217
|
+
if (source.ref) args.push("--branch", source.ref);
|
|
218
|
+
args.push(source.url, dir);
|
|
219
|
+
await runGit(args);
|
|
220
|
+
const root = source.subdir ? path.join(dir, source.subdir) : dir;
|
|
221
|
+
if (!fs.existsSync(root)) throw new Error(`"${source.subdir}" does not exist in ${source.url}`);
|
|
222
|
+
const dirs = findSkillDirs(root);
|
|
223
|
+
if (!dirs.length) throw new Error(`No SKILL.md found in ${source.url}${source.subdir ? `/${source.subdir}` : ""}`);
|
|
224
|
+
return { staged: dirs.map(readStaged), cleanup };
|
|
225
|
+
} catch (err) {
|
|
226
|
+
cleanup();
|
|
227
|
+
throw err;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function stageFromUrl(source, tmpRoot = os.tmpdir(), fetchImpl = fetch) {
|
|
231
|
+
const dir = fs.mkdtempSync(path.join(tmpRoot, "antbot-skill-url-"));
|
|
232
|
+
const cleanup = () => fs.rmSync(dir, { recursive: true, force: true });
|
|
233
|
+
try {
|
|
234
|
+
const res = await fetchImpl(source.url, { redirect: "follow" });
|
|
235
|
+
if (!res.ok) throw new Error(`Fetching ${source.url} failed: HTTP ${res.status}`);
|
|
236
|
+
const body = await res.text();
|
|
237
|
+
if (body.length > MAX_SKILL_BYTES) throw new Error(`${source.url} is larger than the ${MAX_SKILL_BYTES / 1048576} MB limit.`);
|
|
238
|
+
fs.writeFileSync(path.join(dir, "SKILL.md"), body);
|
|
239
|
+
return { staged: [readStaged(dir)], cleanup };
|
|
240
|
+
} catch (err) {
|
|
241
|
+
cleanup();
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async function stage(source) {
|
|
246
|
+
if (source.kind === "path") return { staged: await stageFromPath(source), cleanup: () => {
|
|
247
|
+
} };
|
|
248
|
+
if (source.kind === "git") return stageFromGit(source);
|
|
249
|
+
return stageFromUrl(source);
|
|
250
|
+
}
|
|
251
|
+
function renderManifest(skill, installedAt) {
|
|
252
|
+
const lines = [`\u2713 Installed '${skill.name}'`, ""];
|
|
253
|
+
const width = Math.max(...skill.manifest.files.map((f) => f.relPath.length), 10);
|
|
254
|
+
for (const f of [...skill.manifest.files].sort((a, b) => a.relPath.localeCompare(b.relPath))) {
|
|
255
|
+
const size = f.bytes < 1024 ? `${f.bytes} B` : `${(f.bytes / 1024).toFixed(1)} KB`;
|
|
256
|
+
const flag = isExecutableFile(f.relPath, f.mode) ? " \u26A0 executable" : "";
|
|
257
|
+
lines.push(` ${f.relPath.padEnd(width)} ${size.padStart(8)}${flag}`);
|
|
258
|
+
}
|
|
259
|
+
if (skill.manifest.executables.length) {
|
|
260
|
+
const n = skill.manifest.executables.length;
|
|
261
|
+
lines.push(
|
|
262
|
+
"",
|
|
263
|
+
`\u26A0 This skill ships ${n} ${n === 1 ? "script" : "scripts"}. Scripts run with the same access`,
|
|
264
|
+
" as any bot command. Review before assigning:",
|
|
265
|
+
` ${installedAt}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return lines.join("\n");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export {
|
|
272
|
+
isExecutableFile,
|
|
273
|
+
classifyFiles,
|
|
274
|
+
parseSkillSource,
|
|
275
|
+
describeSkillSource,
|
|
276
|
+
walkFiles,
|
|
277
|
+
findSkillDirs,
|
|
278
|
+
copyTree,
|
|
279
|
+
runGit,
|
|
280
|
+
MAX_SKILL_BYTES,
|
|
281
|
+
stageFromPath,
|
|
282
|
+
stageFromGit,
|
|
283
|
+
stageFromUrl,
|
|
284
|
+
stage,
|
|
285
|
+
renderManifest
|
|
286
|
+
};
|
|
287
|
+
//# sourceMappingURL=chunk-7BOHBPB2.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../packages/server/src/skills/install.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { spawn } from 'node:child_process';\n\n/**\n * Installing a skill means adding instructions that later steer a bot, and possibly\n * scripts that run with the same access as any bot command. Every source is therefore\n * resolved explicitly rather than guessed at, everything lands under the skills\n * directory, and the caller is handed a manifest of exactly what arrived.\n */\n\nexport type SkillSource =\n | { kind: 'git'; url: string; ref?: string; subdir?: string }\n | { kind: 'path'; path: string }\n | { kind: 'url'; url: string };\n\nconst SCRIPT_EXTENSIONS = new Set([\n '.sh', '.bash', '.zsh', '.fish', '.py', '.rb', '.pl', '.js', '.mjs', '.cjs',\n '.ts', '.ps1', '.bat', '.cmd', '.exe', '.bin',\n]);\n\n/** A file worth warning about: it can be executed, not merely read. */\nexport function isExecutableFile(relPath: string, mode: number): boolean {\n if (SCRIPT_EXTENSIONS.has(path.extname(relPath).toLowerCase())) return true;\n return (mode & 0o111) !== 0;\n}\n\nexport interface FileStat {\n relPath: string;\n bytes: number;\n mode: number;\n}\n\nexport interface SkillManifest {\n files: FileStat[];\n executables: string[];\n totalBytes: number;\n}\n\nexport function classifyFiles(files: FileStat[]): SkillManifest {\n return {\n files,\n executables: files.filter((f) => isExecutableFile(f.relPath, f.mode)).map((f) => f.relPath),\n totalBytes: files.reduce((n, f) => n + f.bytes, 0),\n };\n}\n\nconst GITHUB_TREE = /^(?:https?:\\/\\/)?(?:www\\.)?github\\.com\\/([^/]+)\\/([^/#]+?)(?:\\.git)?\\/(?:tree|blob)\\/([^/]+)(?:\\/(.*))?$/;\nconst GITHUB_REPO = /^(?:https?:\\/\\/)?(?:www\\.)?github\\.com\\/([^/]+)\\/([^/#]+?)(?:\\.git)?$/;\nconst OWNER_REPO = /^([A-Za-z0-9][\\w.-]*)\\/([\\w.-]+)$/;\n\n/**\n * Turn a user-supplied spec into an explicit source.\n *\n * Accepts: `owner/repo`, `github.com/owner/repo`, a full https/ssh git URL, a GitHub\n * `/tree/<ref>/<subdir>` link, a local path, or a direct link to a raw `SKILL.md`.\n * Anything else throws rather than being guessed at.\n */\nexport function parseSkillSource(rawSpec: string): SkillSource {\n const spec = rawSpec.trim();\n if (!spec) throw new Error('A skill source is required.');\n\n // Local paths first \u2014 an explicit path prefix is unambiguous.\n if (spec.startsWith('./') || spec.startsWith('../') || spec.startsWith('/') || spec.startsWith('~')) {\n const expanded = spec.startsWith('~') ? path.join(os.homedir(), spec.slice(1)) : spec;\n return { kind: 'path', path: expanded };\n }\n\n if (spec.startsWith('git@') || spec.startsWith('ssh://') || spec.endsWith('.git')) {\n const [url, ref] = splitRef(spec);\n return { kind: 'git', url, ref, subdir: undefined };\n }\n\n const tree = GITHUB_TREE.exec(spec);\n if (tree) {\n const [, owner, repo, ref, subdir] = tree;\n // A `/blob/<ref>/<dir>/SKILL.md` link is what GitHub's UI hands you, and it is the most\n // natural thing to paste. Point it at the containing directory: a subdir naming a file\n // finds no skill directory and would fail with a confusing \"No SKILL.md found\".\n const dir = subdir ? subdir.replace(/(^|\\/)SKILL\\.md$/i, '') : '';\n return { kind: 'git', url: `https://github.com/${owner}/${repo}`, ref, subdir: dir || undefined };\n }\n\n const [base, ref] = splitRef(spec);\n\n const repoMatch = GITHUB_REPO.exec(base);\n if (repoMatch) {\n const [, owner, repo] = repoMatch;\n return { kind: 'git', url: `https://github.com/${owner}/${repo}`, ref, subdir: undefined };\n }\n\n if (/^https?:\\/\\//.test(base)) {\n if (/\\/SKILL\\.md$/i.test(base)) return { kind: 'url', url: base };\n throw new Error(\n `Unsupported URL: ${base}\\nSupported: a git repository, or a direct link ending in /SKILL.md.`,\n );\n }\n\n const ownerRepo = OWNER_REPO.exec(base);\n if (ownerRepo) {\n const [, owner, repo] = ownerRepo;\n return { kind: 'git', url: `https://github.com/${owner}/${repo}`, ref, subdir: undefined };\n }\n\n throw new Error(\n `Could not understand skill source \"${rawSpec}\".\\n` +\n 'Try: owner/repo, github.com/owner/repo, a git URL, a local path, or a URL ending in /SKILL.md.',\n );\n}\n\n/**\n * One line describing what installing `source` would pull in, for an approval card.\n * The distinction that matters is scope: one named skill, or every skill in a repository.\n * Never throws \u2014 an unparseable source is described verbatim so the card still renders.\n */\nexport function describeSkillSource(rawSpec: string): string {\n let parsed: SkillSource;\n try {\n parsed = parseSkillSource(rawSpec);\n } catch {\n return `Install skill from ${rawSpec}`;\n }\n const where = (u: string): string => u.replace(/^https?:\\/\\//, '');\n switch (parsed.kind) {\n case 'url':\n return `Install one skill from ${where(parsed.url)}`;\n case 'path':\n return `Install skill(s) from the local path ${parsed.path}`;\n case 'git': {\n const ref = parsed.ref ? `#${parsed.ref}` : '';\n return parsed.subdir\n ? `Install \"${parsed.subdir}\" from ${where(parsed.url)}${ref}`\n : `Install EVERY skill in ${where(parsed.url)}${ref} (whole repository)`;\n }\n }\n}\n\nfunction splitRef(spec: string): [string, string | undefined] {\n const hash = spec.lastIndexOf('#');\n if (hash === -1) return [spec, undefined];\n return [spec.slice(0, hash), spec.slice(hash + 1) || undefined];\n}\n\n/* ------------------------------- filesystem helpers ------------------------------- */\n\n/** Every file under `root`, relative-pathed, skipping VCS metadata. */\nexport function walkFiles(root: string, rel = ''): FileStat[] {\n const out: FileStat[] = [];\n const dir = path.join(root, rel);\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === '.git' || entry.name === 'node_modules') continue;\n const childRel = rel ? path.join(rel, entry.name) : entry.name;\n const full = path.join(root, childRel);\n if (entry.isSymbolicLink()) continue; // never follow links out of the tree\n if (entry.isDirectory()) out.push(...walkFiles(root, childRel));\n else if (entry.isFile()) {\n const st = fs.statSync(full);\n out.push({ relPath: childRel, bytes: st.size, mode: st.mode });\n }\n }\n return out;\n}\n\n/** Directories containing a SKILL.md, anywhere under `root`. A repo may hold many. */\nexport function findSkillDirs(root: string): string[] {\n const found: string[] = [];\n const visit = (dir: string, depth: number): void => {\n if (depth > 5) return;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n if (entries.some((e) => e.isFile() && e.name.toLowerCase() === 'skill.md')) {\n found.push(dir);\n return; // a skill directory owns its whole subtree\n }\n for (const e of entries) {\n if (!e.isDirectory() || e.name === '.git' || e.name === 'node_modules') continue;\n visit(path.join(dir, e.name), depth + 1);\n }\n };\n visit(root, 0);\n return found.sort();\n}\n\nexport function copyTree(src: string, dest: string): void {\n fs.mkdirSync(dest, { recursive: true });\n for (const entry of fs.readdirSync(src, { withFileTypes: true })) {\n if (entry.name === '.git' || entry.name === 'node_modules') continue;\n if (entry.isSymbolicLink()) continue;\n const s = path.join(src, entry.name);\n const d = path.join(dest, entry.name);\n if (entry.isDirectory()) copyTree(s, d);\n else if (entry.isFile()) fs.copyFileSync(s, d);\n }\n}\n\nexport function runGit(args: string[], cwd?: string, timeoutMs = 120_000): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn('git', args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });\n let stderr = '';\n const timer = setTimeout(() => {\n child.kill('SIGKILL');\n reject(new Error(`git ${args[0]} timed out after ${timeoutMs / 1000}s`));\n }, timeoutMs);\n child.stderr?.on('data', (d) => (stderr += String(d)));\n child.on('error', (err) => {\n clearTimeout(timer);\n reject(new Error(`git is required to install from a repository: ${err.message}`));\n });\n child.on('close', (code) => {\n clearTimeout(timer);\n if (code === 0) resolve();\n else reject(new Error(`git ${args[0]} failed: ${stderr.trim().split('\\n').slice(-3).join(' ')}`));\n });\n });\n}\n\n/* ---------------------------------- staging ---------------------------------- */\n\nimport { parseFrontmatter } from './skills.js';\n\nexport interface StagedSkill {\n /** Name from the SKILL.md frontmatter \u2014 what the SDK's Skill tool matches on. */\n name: string;\n description: string;\n /** Directory holding SKILL.md and any supporting files, ready to copy in. */\n dir: string;\n manifest: SkillManifest;\n}\n\n/** Hard cap so a mis-typed source cannot fill the disk. */\nexport const MAX_SKILL_BYTES = 10 * 1024 * 1024;\n\nfunction readStaged(dir: string): StagedSkill {\n const file = path.join(dir, 'SKILL.md');\n const raw = fs.readFileSync(file, 'utf8');\n const { name, description } = parseFrontmatter(raw);\n if (!name.trim()) {\n throw new Error(\n `${file} has no \\`name\\` in its frontmatter. A skill needs:\\n---\\nname: my-skill\\ndescription: what it does\\n---`,\n );\n }\n const manifest = classifyFiles(walkFiles(dir));\n if (manifest.totalBytes > MAX_SKILL_BYTES) {\n throw new Error(\n `Skill \"${name}\" is ${(manifest.totalBytes / 1048576).toFixed(1)} MB; the limit is ${MAX_SKILL_BYTES / 1048576} MB.`,\n );\n }\n return { name: name.trim(), description: description.trim(), dir, manifest };\n}\n\n/** Collect every skill under a local path. Accepts a skill dir, a SKILL.md, or a pack of skills. */\nexport async function stageFromPath(source: { kind: 'path'; path: string }): Promise<StagedSkill[]> {\n const target = path.resolve(source.path);\n if (!fs.existsSync(target)) throw new Error(`Path not found: ${target}`);\n\n const stat = fs.statSync(target);\n if (stat.isFile()) {\n if (path.basename(target).toLowerCase() !== 'skill.md')\n throw new Error(`Expected a directory or a SKILL.md file, got ${target}`);\n return [readStaged(path.dirname(target))];\n }\n\n const dirs = findSkillDirs(target);\n if (!dirs.length) throw new Error(`No SKILL.md found under ${target}`);\n return dirs.map(readStaged);\n}\n\n/** Clone a repo shallowly into a temp dir and stage the skills inside it. */\nexport async function stageFromGit(\n source: { kind: 'git'; url: string; ref?: string; subdir?: string },\n tmpRoot = os.tmpdir(),\n): Promise<{ staged: StagedSkill[]; cleanup: () => void }> {\n const dir = fs.mkdtempSync(path.join(tmpRoot, 'antbot-skill-git-'));\n const cleanup = (): void => fs.rmSync(dir, { recursive: true, force: true });\n try {\n const args = ['clone', '--depth', '1', '--quiet'];\n if (source.ref) args.push('--branch', source.ref);\n args.push(source.url, dir);\n await runGit(args);\n\n const root = source.subdir ? path.join(dir, source.subdir) : dir;\n if (!fs.existsSync(root)) throw new Error(`\"${source.subdir}\" does not exist in ${source.url}`);\n\n const dirs = findSkillDirs(root);\n if (!dirs.length) throw new Error(`No SKILL.md found in ${source.url}${source.subdir ? `/${source.subdir}` : ''}`);\n return { staged: dirs.map(readStaged), cleanup };\n } catch (err) {\n cleanup();\n throw err;\n }\n}\n\n/** Fetch a single raw SKILL.md into a temp dir. */\nexport async function stageFromUrl(\n source: { kind: 'url'; url: string },\n tmpRoot = os.tmpdir(),\n fetchImpl: typeof fetch = fetch,\n): Promise<{ staged: StagedSkill[]; cleanup: () => void }> {\n const dir = fs.mkdtempSync(path.join(tmpRoot, 'antbot-skill-url-'));\n const cleanup = (): void => fs.rmSync(dir, { recursive: true, force: true });\n try {\n const res = await fetchImpl(source.url, { redirect: 'follow' });\n if (!res.ok) throw new Error(`Fetching ${source.url} failed: HTTP ${res.status}`);\n const body = await res.text();\n if (body.length > MAX_SKILL_BYTES) throw new Error(`${source.url} is larger than the ${MAX_SKILL_BYTES / 1048576} MB limit.`);\n fs.writeFileSync(path.join(dir, 'SKILL.md'), body);\n return { staged: [readStaged(dir)], cleanup };\n } catch (err) {\n cleanup();\n throw err;\n }\n}\n\n/** Resolve any source to staged skills plus a cleanup for whatever temp state it created. */\nexport async function stage(source: SkillSource): Promise<{ staged: StagedSkill[]; cleanup: () => void }> {\n if (source.kind === 'path') return { staged: await stageFromPath(source), cleanup: () => {} };\n if (source.kind === 'git') return stageFromGit(source);\n return stageFromUrl(source);\n}\n\n/** Human-readable summary of what an install put on disk. */\nexport function renderManifest(skill: StagedSkill, installedAt: string): string {\n const lines = [`\u2713 Installed '${skill.name}'`, ''];\n const width = Math.max(...skill.manifest.files.map((f) => f.relPath.length), 10);\n for (const f of [...skill.manifest.files].sort((a, b) => a.relPath.localeCompare(b.relPath))) {\n const size = f.bytes < 1024 ? `${f.bytes} B` : `${(f.bytes / 1024).toFixed(1)} KB`;\n const flag = isExecutableFile(f.relPath, f.mode) ? ' \u26A0 executable' : '';\n lines.push(` ${f.relPath.padEnd(width)} ${size.padStart(8)}${flag}`);\n }\n if (skill.manifest.executables.length) {\n const n = skill.manifest.executables.length;\n lines.push(\n '',\n `\u26A0 This skill ships ${n} ${n === 1 ? 'script' : 'scripts'}. Scripts run with the same access`,\n ' as any bot command. Review before assigning:',\n ` ${installedAt}`,\n );\n }\n return lines.join('\\n');\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,aAAa;AActB,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EAAO;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EACrE;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AACzC,CAAC;AAGM,SAAS,iBAAiB,SAAiB,MAAuB;AACvE,MAAI,kBAAkB,IAAI,KAAK,QAAQ,OAAO,EAAE,YAAY,CAAC,EAAG,QAAO;AACvE,UAAQ,OAAO,QAAW;AAC5B;AAcO,SAAS,cAAc,OAAkC;AAC9D,SAAO;AAAA,IACL;AAAA,IACA,aAAa,MAAM,OAAO,CAAC,MAAM,iBAAiB,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO;AAAA,IAC1F,YAAY,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,CAAC;AAAA,EACnD;AACF;AAEA,IAAM,cAAc;AACpB,IAAM,cAAc;AACpB,IAAM,aAAa;AASZ,SAAS,iBAAiB,SAA8B;AAC7D,QAAM,OAAO,QAAQ,KAAK;AAC1B,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,6BAA6B;AAGxD,MAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,GAAG;AACnG,UAAM,WAAW,KAAK,WAAW,GAAG,IAAI,KAAK,KAAK,GAAG,QAAQ,GAAG,KAAK,MAAM,CAAC,CAAC,IAAI;AACjF,WAAO,EAAE,MAAM,QAAQ,MAAM,SAAS;AAAA,EACxC;AAEA,MAAI,KAAK,WAAW,MAAM,KAAK,KAAK,WAAW,QAAQ,KAAK,KAAK,SAAS,MAAM,GAAG;AACjF,UAAM,CAAC,KAAKA,IAAG,IAAI,SAAS,IAAI;AAChC,WAAO,EAAE,MAAM,OAAO,KAAK,KAAAA,MAAK,QAAQ,OAAU;AAAA,EACpD;AAEA,QAAM,OAAO,YAAY,KAAK,IAAI;AAClC,MAAI,MAAM;AACR,UAAM,CAAC,EAAE,OAAO,MAAMA,MAAK,MAAM,IAAI;AAIrC,UAAM,MAAM,SAAS,OAAO,QAAQ,qBAAqB,EAAE,IAAI;AAC/D,WAAO,EAAE,MAAM,OAAO,KAAK,sBAAsB,KAAK,IAAI,IAAI,IAAI,KAAAA,MAAK,QAAQ,OAAO,OAAU;AAAA,EAClG;AAEA,QAAM,CAAC,MAAM,GAAG,IAAI,SAAS,IAAI;AAEjC,QAAM,YAAY,YAAY,KAAK,IAAI;AACvC,MAAI,WAAW;AACb,UAAM,CAAC,EAAE,OAAO,IAAI,IAAI;AACxB,WAAO,EAAE,MAAM,OAAO,KAAK,sBAAsB,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,OAAU;AAAA,EAC3F;AAEA,MAAI,eAAe,KAAK,IAAI,GAAG;AAC7B,QAAI,gBAAgB,KAAK,IAAI,EAAG,QAAO,EAAE,MAAM,OAAO,KAAK,KAAK;AAChE,UAAM,IAAI;AAAA,MACR,oBAAoB,IAAI;AAAA;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,YAAY,WAAW,KAAK,IAAI;AACtC,MAAI,WAAW;AACb,UAAM,CAAC,EAAE,OAAO,IAAI,IAAI;AACxB,WAAO,EAAE,MAAM,OAAO,KAAK,sBAAsB,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,OAAU;AAAA,EAC3F;AAEA,QAAM,IAAI;AAAA,IACR,sCAAsC,OAAO;AAAA;AAAA,EAE/C;AACF;AAOO,SAAS,oBAAoB,SAAyB;AAC3D,MAAI;AACJ,MAAI;AACF,aAAS,iBAAiB,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO,sBAAsB,OAAO;AAAA,EACtC;AACA,QAAM,QAAQ,CAAC,MAAsB,EAAE,QAAQ,gBAAgB,EAAE;AACjE,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,0BAA0B,MAAM,OAAO,GAAG,CAAC;AAAA,IACpD,KAAK;AACH,aAAO,wCAAwC,OAAO,IAAI;AAAA,IAC5D,KAAK,OAAO;AACV,YAAM,MAAM,OAAO,MAAM,IAAI,OAAO,GAAG,KAAK;AAC5C,aAAO,OAAO,SACV,YAAY,OAAO,MAAM,UAAU,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,KAC1D,0BAA0B,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG;AAAA,IACvD;AAAA,EACF;AACF;AAEA,SAAS,SAAS,MAA4C;AAC5D,QAAM,OAAO,KAAK,YAAY,GAAG;AACjC,MAAI,SAAS,GAAI,QAAO,CAAC,MAAM,MAAS;AACxC,SAAO,CAAC,KAAK,MAAM,GAAG,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC,KAAK,MAAS;AAChE;AAKO,SAAS,UAAU,MAAc,MAAM,IAAgB;AAC5D,QAAM,MAAkB,CAAC;AACzB,QAAM,MAAM,KAAK,KAAK,MAAM,GAAG;AAC/B,aAAW,SAAS,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,QAAI,MAAM,SAAS,UAAU,MAAM,SAAS,eAAgB;AAC5D,UAAM,WAAW,MAAM,KAAK,KAAK,KAAK,MAAM,IAAI,IAAI,MAAM;AAC1D,UAAM,OAAO,KAAK,KAAK,MAAM,QAAQ;AACrC,QAAI,MAAM,eAAe,EAAG;AAC5B,QAAI,MAAM,YAAY,EAAG,KAAI,KAAK,GAAG,UAAU,MAAM,QAAQ,CAAC;AAAA,aACrD,MAAM,OAAO,GAAG;AACvB,YAAM,KAAK,GAAG,SAAS,IAAI;AAC3B,UAAI,KAAK,EAAE,SAAS,UAAU,OAAO,GAAG,MAAM,MAAM,GAAG,KAAK,CAAC;AAAA,IAC/D;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,cAAc,MAAwB;AACpD,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,CAAC,KAAa,UAAwB;AAClD,QAAI,QAAQ,EAAG;AACf,QAAI;AACJ,QAAI;AACF,gBAAU,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACvD,QAAQ;AACN;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,KAAK,YAAY,MAAM,UAAU,GAAG;AAC1E,YAAM,KAAK,GAAG;AACd;AAAA,IACF;AACA,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,EAAE,YAAY,KAAK,EAAE,SAAS,UAAU,EAAE,SAAS,eAAgB;AACxE,YAAM,KAAK,KAAK,KAAK,EAAE,IAAI,GAAG,QAAQ,CAAC;AAAA,IACzC;AAAA,EACF;AACA,QAAM,MAAM,CAAC;AACb,SAAO,MAAM,KAAK;AACpB;AAEO,SAAS,SAAS,KAAa,MAAoB;AACxD,KAAG,UAAU,MAAM,EAAE,WAAW,KAAK,CAAC;AACtC,aAAW,SAAS,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,QAAI,MAAM,SAAS,UAAU,MAAM,SAAS,eAAgB;AAC5D,QAAI,MAAM,eAAe,EAAG;AAC5B,UAAM,IAAI,KAAK,KAAK,KAAK,MAAM,IAAI;AACnC,UAAM,IAAI,KAAK,KAAK,MAAM,MAAM,IAAI;AACpC,QAAI,MAAM,YAAY,EAAG,UAAS,GAAG,CAAC;AAAA,aAC7B,MAAM,OAAO,EAAG,IAAG,aAAa,GAAG,CAAC;AAAA,EAC/C;AACF;AAEO,SAAS,OAAO,MAAgB,KAAc,YAAY,MAAwB;AACvF,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,MAAM,OAAO,MAAM,EAAE,KAAK,OAAO,CAAC,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC3E,QAAI,SAAS;AACb,UAAM,QAAQ,WAAW,MAAM;AAC7B,YAAM,KAAK,SAAS;AACpB,aAAO,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC,oBAAoB,YAAY,GAAI,GAAG,CAAC;AAAA,IACzE,GAAG,SAAS;AACZ,UAAM,QAAQ,GAAG,QAAQ,CAAC,MAAO,UAAU,OAAO,CAAC,CAAE;AACrD,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,mBAAa,KAAK;AAClB,aAAO,IAAI,MAAM,iDAAiD,IAAI,OAAO,EAAE,CAAC;AAAA,IAClF,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,mBAAa,KAAK;AAClB,UAAI,SAAS,EAAG,SAAQ;AAAA,UACnB,QAAO,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC,YAAY,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;AAAA,IAClG,CAAC;AAAA,EACH,CAAC;AACH;AAgBO,IAAM,kBAAkB,KAAK,OAAO;AAE3C,SAAS,WAAW,KAA0B;AAC5C,QAAM,OAAO,KAAK,KAAK,KAAK,UAAU;AACtC,QAAM,MAAM,GAAG,aAAa,MAAM,MAAM;AACxC,QAAM,EAAE,MAAM,YAAY,IAAI,iBAAiB,GAAG;AAClD,MAAI,CAAC,KAAK,KAAK,GAAG;AAChB,UAAM,IAAI;AAAA,MACR,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,IACT;AAAA,EACF;AACA,QAAM,WAAW,cAAc,UAAU,GAAG,CAAC;AAC7C,MAAI,SAAS,aAAa,iBAAiB;AACzC,UAAM,IAAI;AAAA,MACR,UAAU,IAAI,SAAS,SAAS,aAAa,SAAS,QAAQ,CAAC,CAAC,qBAAqB,kBAAkB,OAAO;AAAA,IAChH;AAAA,EACF;AACA,SAAO,EAAE,MAAM,KAAK,KAAK,GAAG,aAAa,YAAY,KAAK,GAAG,KAAK,SAAS;AAC7E;AAGA,eAAsB,cAAc,QAAgE;AAClG,QAAM,SAAS,KAAK,QAAQ,OAAO,IAAI;AACvC,MAAI,CAAC,GAAG,WAAW,MAAM,EAAG,OAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAEvE,QAAM,OAAO,GAAG,SAAS,MAAM;AAC/B,MAAI,KAAK,OAAO,GAAG;AACjB,QAAI,KAAK,SAAS,MAAM,EAAE,YAAY,MAAM;AAC1C,YAAM,IAAI,MAAM,gDAAgD,MAAM,EAAE;AAC1E,WAAO,CAAC,WAAW,KAAK,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC1C;AAEA,QAAM,OAAO,cAAc,MAAM;AACjC,MAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,2BAA2B,MAAM,EAAE;AACrE,SAAO,KAAK,IAAI,UAAU;AAC5B;AAGA,eAAsB,aACpB,QACA,UAAU,GAAG,OAAO,GACqC;AACzD,QAAM,MAAM,GAAG,YAAY,KAAK,KAAK,SAAS,mBAAmB,CAAC;AAClE,QAAM,UAAU,MAAY,GAAG,OAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC3E,MAAI;AACF,UAAM,OAAO,CAAC,SAAS,WAAW,KAAK,SAAS;AAChD,QAAI,OAAO,IAAK,MAAK,KAAK,YAAY,OAAO,GAAG;AAChD,SAAK,KAAK,OAAO,KAAK,GAAG;AACzB,UAAM,OAAO,IAAI;AAEjB,UAAM,OAAO,OAAO,SAAS,KAAK,KAAK,KAAK,OAAO,MAAM,IAAI;AAC7D,QAAI,CAAC,GAAG,WAAW,IAAI,EAAG,OAAM,IAAI,MAAM,IAAI,OAAO,MAAM,uBAAuB,OAAO,GAAG,EAAE;AAE9F,UAAM,OAAO,cAAc,IAAI;AAC/B,QAAI,CAAC,KAAK,OAAQ,OAAM,IAAI,MAAM,wBAAwB,OAAO,GAAG,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,KAAK,EAAE,EAAE;AACjH,WAAO,EAAE,QAAQ,KAAK,IAAI,UAAU,GAAG,QAAQ;AAAA,EACjD,SAAS,KAAK;AACZ,YAAQ;AACR,UAAM;AAAA,EACR;AACF;AAGA,eAAsB,aACpB,QACA,UAAU,GAAG,OAAO,GACpB,YAA0B,OAC+B;AACzD,QAAM,MAAM,GAAG,YAAY,KAAK,KAAK,SAAS,mBAAmB,CAAC;AAClE,QAAM,UAAU,MAAY,GAAG,OAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC3E,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,OAAO,KAAK,EAAE,UAAU,SAAS,CAAC;AAC9D,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,YAAY,OAAO,GAAG,iBAAiB,IAAI,MAAM,EAAE;AAChF,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,KAAK,SAAS,gBAAiB,OAAM,IAAI,MAAM,GAAG,OAAO,GAAG,uBAAuB,kBAAkB,OAAO,YAAY;AAC5H,OAAG,cAAc,KAAK,KAAK,KAAK,UAAU,GAAG,IAAI;AACjD,WAAO,EAAE,QAAQ,CAAC,WAAW,GAAG,CAAC,GAAG,QAAQ;AAAA,EAC9C,SAAS,KAAK;AACZ,YAAQ;AACR,UAAM;AAAA,EACR;AACF;AAGA,eAAsB,MAAM,QAA8E;AACxG,MAAI,OAAO,SAAS,OAAQ,QAAO,EAAE,QAAQ,MAAM,cAAc,MAAM,GAAG,SAAS,MAAM;AAAA,EAAC,EAAE;AAC5F,MAAI,OAAO,SAAS,MAAO,QAAO,aAAa,MAAM;AACrD,SAAO,aAAa,MAAM;AAC5B;AAGO,SAAS,eAAe,OAAoB,aAA6B;AAC9E,QAAM,QAAQ,CAAC,qBAAgB,MAAM,IAAI,KAAK,EAAE;AAChD,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,SAAS,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM,GAAG,EAAE;AAC/E,aAAW,KAAK,CAAC,GAAG,MAAM,SAAS,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC,GAAG;AAC5F,UAAM,OAAO,EAAE,QAAQ,OAAO,GAAG,EAAE,KAAK,OAAO,IAAI,EAAE,QAAQ,MAAM,QAAQ,CAAC,CAAC;AAC7E,UAAM,OAAO,iBAAiB,EAAE,SAAS,EAAE,IAAI,IAAI,yBAAoB;AACvE,UAAM,KAAK,KAAK,EAAE,QAAQ,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,GAAG,IAAI,EAAE;AAAA,EACvE;AACA,MAAI,MAAM,SAAS,YAAY,QAAQ;AACrC,UAAM,IAAI,MAAM,SAAS,YAAY;AACrC,UAAM;AAAA,MACJ;AAAA,MACA,2BAAsB,CAAC,IAAI,MAAM,IAAI,WAAW,SAAS;AAAA,MACzD;AAAA,MACA,KAAK,WAAW;AAAA,IAClB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;",
|
|
6
|
+
"names": ["ref"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createRequire as __antbotCreateRequire } from 'node:module';
|
|
2
|
+
const require = __antbotCreateRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// packages/server/src/util/log.ts
|
|
5
|
+
var order = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
6
|
+
var min = order[process.env.ANTBOT_LOG_LEVEL ?? "info"] ?? 20;
|
|
7
|
+
function emit(level, scope, msg, extra) {
|
|
8
|
+
if (order[level] < min) return;
|
|
9
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase().padEnd(5)} [${scope}] ${msg}`;
|
|
10
|
+
if (level === "error") console.error(line, extra ?? "");
|
|
11
|
+
else if (level === "warn") console.warn(line, extra ?? "");
|
|
12
|
+
else console.log(line, extra ?? "");
|
|
13
|
+
}
|
|
14
|
+
var logger = (scope) => ({
|
|
15
|
+
debug: (m, e) => emit("debug", scope, m, e),
|
|
16
|
+
info: (m, e) => emit("info", scope, m, e),
|
|
17
|
+
warn: (m, e) => emit("warn", scope, m, e),
|
|
18
|
+
error: (m, e) => emit("error", scope, m, e)
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
logger
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=chunk-AHAON6J7.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../packages/server/src/util/log.ts"],
|
|
4
|
+
"sourcesContent": ["type Level = 'debug' | 'info' | 'warn' | 'error';\nconst order: Record<Level, number> = { debug: 10, info: 20, warn: 30, error: 40 };\nconst min = order[(process.env.ANTBOT_LOG_LEVEL as Level) ?? 'info'] ?? 20;\n\nfunction emit(level: Level, scope: string, msg: string, extra?: unknown) {\n if (order[level] < min) return;\n const line = `${new Date().toISOString()} ${level.toUpperCase().padEnd(5)} [${scope}] ${msg}`;\n if (level === 'error') console.error(line, extra ?? '');\n else if (level === 'warn') console.warn(line, extra ?? '');\n else console.log(line, extra ?? '');\n}\n\nexport const logger = (scope: string) => ({\n debug: (m: string, e?: unknown) => emit('debug', scope, m, e),\n info: (m: string, e?: unknown) => emit('info', scope, m, e),\n warn: (m: string, e?: unknown) => emit('warn', scope, m, e),\n error: (m: string, e?: unknown) => emit('error', scope, m, e),\n});\n"],
|
|
5
|
+
"mappings": ";;;;AACA,IAAM,QAA+B,EAAE,OAAO,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,GAAG;AAChF,IAAM,MAAM,MAAO,QAAQ,IAAI,oBAA8B,MAAM,KAAK;AAExE,SAAS,KAAK,OAAc,OAAe,KAAa,OAAiB;AACvE,MAAI,MAAM,KAAK,IAAI,IAAK;AACxB,QAAM,OAAO,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,IAAI,MAAM,YAAY,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,GAAG;AAC3F,MAAI,UAAU,QAAS,SAAQ,MAAM,MAAM,SAAS,EAAE;AAAA,WAC7C,UAAU,OAAQ,SAAQ,KAAK,MAAM,SAAS,EAAE;AAAA,MACpD,SAAQ,IAAI,MAAM,SAAS,EAAE;AACpC;AAEO,IAAM,SAAS,CAAC,WAAmB;AAAA,EACxC,OAAO,CAAC,GAAW,MAAgB,KAAK,SAAS,OAAO,GAAG,CAAC;AAAA,EAC5D,MAAM,CAAC,GAAW,MAAgB,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,EAC1D,MAAM,CAAC,GAAW,MAAgB,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,EAC1D,OAAO,CAAC,GAAW,MAAgB,KAAK,SAAS,OAAO,GAAG,CAAC;AAC9D;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|