unagent 0.0.1 → 0.0.4
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/README.md +47 -0
- package/dist/clone-DgXhqC05.mjs +104 -0
- package/dist/context/index.d.mts +2 -0
- package/dist/context/index.mjs +3 -0
- package/dist/context-CAk5kS7q.mjs +59 -0
- package/dist/{env-BUegcU7a.js → detect-CDtKsCsD.mjs} +1 -1
- package/dist/env/{index.d.ts → index.d.mts} +1 -1
- package/dist/env/{index.js → index.mjs} +3 -2
- package/dist/env-DeXFcrWN.mjs +1 -0
- package/dist/exec/index.d.mts +2 -0
- package/dist/exec/index.mjs +3 -0
- package/dist/exec-Dzl5r4Ui.mjs +116 -0
- package/dist/fs/index.d.mts +2 -0
- package/dist/fs/index.mjs +3 -0
- package/dist/fs-nJz4v9pE.mjs +269 -0
- package/dist/git/{index.d.ts → index.d.mts} +1 -1
- package/dist/git/index.mjs +4 -0
- package/dist/git-D3qsdy9d.mjs +86 -0
- package/dist/hooks/index.d.mts +3 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/hooks-335rp9Cp.mjs +9 -0
- package/dist/index-BZaywR9E.d.mts +97 -0
- package/dist/index-Bd1gSwMB.d.mts +31 -0
- package/dist/index-Bd4x_1H9.d.mts +33 -0
- package/dist/index-C0ulBa5T.d.mts +21 -0
- package/dist/{index-DwU61LUW.d.ts → index-C55JaUgw.d.mts} +10 -10
- package/dist/index-Csv1G0zj.d.mts +36 -0
- package/dist/index-CvCCCs-_.d.mts +20 -0
- package/dist/{index-LzafUiEo.d.ts → index-Cy8LThTV.d.mts} +1 -1
- package/dist/index-D5A0wwzb.d.mts +55 -0
- package/dist/index-DFqD_DAh.d.mts +80 -0
- package/dist/index-DShEKmmL.d.mts +43 -0
- package/dist/index-DpFup4kC.d.mts +21 -0
- package/dist/index-ucMWydcs.d.mts +19 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +24 -0
- package/dist/link/{index.d.ts → index.d.mts} +1 -1
- package/dist/link/{index.js → index.mjs} +2 -1
- package/dist/link-BRZABZ3A.mjs +1 -0
- package/dist/lock/{index.d.ts → index.d.mts} +1 -1
- package/dist/lock/{index.js → index.mjs} +1 -1
- package/dist/{lock-BeR43Izo.js → lock-CB9Xr9pv.mjs} +1 -1
- package/dist/{path-nPuHl-f5.js → path-Dm-URQvz.mjs} +3 -3
- package/dist/registry/index.d.mts +2 -0
- package/dist/registry/index.mjs +3 -0
- package/dist/registry-DvxT2enn.mjs +30 -0
- package/dist/sandbox/index.d.mts +2 -0
- package/dist/sandbox/index.mjs +3 -0
- package/dist/sandbox-Dyz9jTaL.mjs +139 -0
- package/dist/skill/index.d.mts +2 -0
- package/dist/skill/index.mjs +9 -0
- package/dist/skill-BnKVgm4n.mjs +358 -0
- package/dist/source/{index.d.ts → index.d.mts} +1 -1
- package/dist/source/{index.js → index.mjs} +2 -1
- package/dist/source-BCRylzkW.mjs +1 -0
- package/dist/stop/index.d.mts +3 -0
- package/dist/stop/index.mjs +4 -0
- package/dist/stop-B9w8PiPj.mjs +39 -0
- package/dist/stream/index.d.mts +3 -0
- package/dist/stream/index.mjs +3 -0
- package/dist/stream-CzGvLSeV.mjs +99 -0
- package/dist/tool/index.d.mts +2 -0
- package/dist/tool/index.mjs +3 -0
- package/dist/tool-CESxMfOv.mjs +45 -0
- package/dist/usage/index.d.mts +2 -0
- package/dist/usage/index.mjs +3 -0
- package/dist/usage-DTaeWIUK.mjs +165 -0
- package/dist/utils/{index.d.ts → index.d.mts} +1 -1
- package/dist/utils/{index.js → index.mjs} +2 -2
- package/package.json +104 -34
- package/dist/git/index.js +0 -3
- package/dist/git-D4ZclaF6.js +0 -132
- package/dist/index-y5JZ6STt.d.ts +0 -36
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -10
- package/dist/skill/index.d.ts +0 -2
- package/dist/skill/index.js +0 -3
- package/dist/skill-BmLJYiQz.js +0 -69
- /package/dist/{index-CirIJDiq.d.ts → index-CbYchDwg.d.mts} +0 -0
- /package/dist/{index-BW91Ai1Y.d.ts → index-CzgY9GU2.d.mts} +0 -0
- /package/dist/{index-CFwFmDD-.d.ts → index-DPt7J0hF.d.mts} +0 -0
- /package/dist/{index-DmiDvQ8Q.d.ts → index-wTumcHrH.d.mts} +0 -0
- /package/dist/{source-IfQxnt_F.js → parse-CEbeorIB.mjs} +0 -0
- /package/dist/{link-C4PSDr4v.js → symlink-CbP-heyc.mjs} +0 -0
- /package/dist/{utils-CDyPZons.js → utils-bP3i6rq3.mjs} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# unagent
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/unagent)
|
|
4
|
+
[](https://npmjs.com/package/unagent)
|
|
5
|
+
|
|
6
|
+
Unified primitives for AI coding agents.
|
|
7
|
+
|
|
8
|
+
- Detect 45+ AI coding agents by env vars or config
|
|
9
|
+
- Discover and parse markdown-based skill files
|
|
10
|
+
- Parse GitHub/GitLab URLs, owner/repo shortcuts, local paths
|
|
11
|
+
- Clone repos, manage temp dirs, check git status
|
|
12
|
+
- Copy directories, create symlinks safely
|
|
13
|
+
- Track installed skills with lockfile and hashes
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install unagent
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { detectCurrentAgent, discoverSkills, parseSource } from 'unagent'
|
|
25
|
+
|
|
26
|
+
// Detect which agent is running
|
|
27
|
+
const agent = detectCurrentAgent()
|
|
28
|
+
if (agent) {
|
|
29
|
+
console.log(`Running in ${agent.config.name}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Discover skills
|
|
33
|
+
const skills = discoverSkills('~/.claude/skills', { recursive: true })
|
|
34
|
+
|
|
35
|
+
// Parse source strings
|
|
36
|
+
parseSource('unjs/unagent') // github
|
|
37
|
+
parseSource('github:user/repo#main') // github with ref
|
|
38
|
+
parseSource('./local/path') // local
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
[unagent.onmax.me](https://unagent.onmax.me)
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "pathe";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
//#region src/git/_exec.ts
|
|
7
|
+
function git(args, opts) {
|
|
8
|
+
const result = spawnSync("git", args, {
|
|
9
|
+
cwd: opts?.cwd,
|
|
10
|
+
timeout: opts?.timeout,
|
|
11
|
+
encoding: "utf8",
|
|
12
|
+
stdio: [
|
|
13
|
+
"pipe",
|
|
14
|
+
"pipe",
|
|
15
|
+
"pipe"
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
if (result.error) throw result.error;
|
|
19
|
+
if (result.status !== 0) throw new Error(result.stderr || `git ${args[0]} failed with code ${result.status}`);
|
|
20
|
+
return (result.stdout ?? "").trim();
|
|
21
|
+
}
|
|
22
|
+
function parseGitStatus(output) {
|
|
23
|
+
const lines = output.split("\n").filter(Boolean);
|
|
24
|
+
const result = {
|
|
25
|
+
branch: "HEAD",
|
|
26
|
+
ahead: 0,
|
|
27
|
+
behind: 0,
|
|
28
|
+
staged: [],
|
|
29
|
+
modified: [],
|
|
30
|
+
untracked: []
|
|
31
|
+
};
|
|
32
|
+
for (const line of lines) if (line.startsWith("# branch.head ")) result.branch = line.slice(14);
|
|
33
|
+
else if (line.startsWith("# branch.ab ")) {
|
|
34
|
+
const match = line.match(/\+(\d+) -(\d+)/);
|
|
35
|
+
if (match) {
|
|
36
|
+
result.ahead = Number.parseInt(match[1], 10);
|
|
37
|
+
result.behind = Number.parseInt(match[2], 10);
|
|
38
|
+
}
|
|
39
|
+
} else if (line.startsWith("1 ") || line.startsWith("2 ")) {
|
|
40
|
+
const xy = line.slice(2, 4);
|
|
41
|
+
const path = line.split(" ").pop() || line.split(" ").pop() || "";
|
|
42
|
+
if (xy[0] !== ".") result.staged.push(path);
|
|
43
|
+
if (xy[1] !== ".") result.modified.push(path);
|
|
44
|
+
} else if (line.startsWith("? ")) result.untracked.push(line.slice(2));
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/git/clone.ts
|
|
50
|
+
var GitCloneError = class extends Error {
|
|
51
|
+
url;
|
|
52
|
+
cause;
|
|
53
|
+
constructor(message, url, cause) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "GitCloneError";
|
|
56
|
+
this.url = url;
|
|
57
|
+
this.cause = cause;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function cloneRepo(url, dest, options = {}) {
|
|
61
|
+
const { depth = 1, branch, timeout = 6e4 } = options;
|
|
62
|
+
try {
|
|
63
|
+
const args = ["clone"];
|
|
64
|
+
if (depth > 0) args.push("--depth", String(depth));
|
|
65
|
+
if (branch) args.push("--branch", branch);
|
|
66
|
+
args.push(url, dest);
|
|
67
|
+
const destDir = join(dest, "..");
|
|
68
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
69
|
+
git(args, { timeout });
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
path: dest
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
path: dest,
|
|
78
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function cloneToTemp(url, options = {}) {
|
|
83
|
+
const { tempDir = tmpdir() } = options;
|
|
84
|
+
return cloneRepo(url, join(tempDir, `unagent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`), options);
|
|
85
|
+
}
|
|
86
|
+
function cleanupTempDir(path) {
|
|
87
|
+
const temp = tmpdir();
|
|
88
|
+
if (!path.startsWith(temp)) return false;
|
|
89
|
+
try {
|
|
90
|
+
if (existsSync(path)) rmSync(path, {
|
|
91
|
+
recursive: true,
|
|
92
|
+
force: true
|
|
93
|
+
});
|
|
94
|
+
return true;
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function isTempDir(path) {
|
|
100
|
+
return path.startsWith(tmpdir());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
export { isTempDir as a, cloneToTemp as i, cleanupTempDir as n, git as o, cloneRepo as r, parseGitStatus as s, GitCloneError as t };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as TruncateOptions, c as splitByTokens, i as TokenEstimateOptions, l as truncateToFit, n as BuildContextResult, o as buildContext, r as ContextItem, s as estimateTokens, t as BuildContextOptions } from "../index-Bd1gSwMB.mjs";
|
|
2
|
+
export { BuildContextOptions, BuildContextResult, ContextItem, TokenEstimateOptions, TruncateOptions, buildContext, estimateTokens, splitByTokens, truncateToFit };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//#region src/context/index.ts
|
|
2
|
+
const CHARS_PER_TOKEN = 4;
|
|
3
|
+
function estimateTokens(text, options = {}) {
|
|
4
|
+
const { charsPerToken = CHARS_PER_TOKEN } = options;
|
|
5
|
+
return Math.ceil(text.length / charsPerToken);
|
|
6
|
+
}
|
|
7
|
+
function truncateToFit(content, options) {
|
|
8
|
+
const { maxTokens, charsPerToken = CHARS_PER_TOKEN, suffix = "\n...[truncated]" } = options;
|
|
9
|
+
const maxChars = maxTokens * charsPerToken;
|
|
10
|
+
if (content.length <= maxChars) return content;
|
|
11
|
+
const truncateAt = maxChars - suffix.length;
|
|
12
|
+
if (truncateAt <= 0) return suffix.slice(0, maxChars);
|
|
13
|
+
return content.slice(0, truncateAt) + suffix;
|
|
14
|
+
}
|
|
15
|
+
function buildContext(items, options) {
|
|
16
|
+
const { maxTokens, charsPerToken = CHARS_PER_TOKEN, separator = "\n\n" } = options;
|
|
17
|
+
const maxChars = maxTokens * charsPerToken;
|
|
18
|
+
const sorted = [...items].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
19
|
+
const included = [];
|
|
20
|
+
const excluded = [];
|
|
21
|
+
const parts = [];
|
|
22
|
+
let currentChars = 0;
|
|
23
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
24
|
+
const item = sorted[i];
|
|
25
|
+
const itemChars = item.content.length;
|
|
26
|
+
const separatorChars = parts.length > 0 ? separator.length : 0;
|
|
27
|
+
const totalChars = currentChars + itemChars + separatorChars;
|
|
28
|
+
if (totalChars <= maxChars) {
|
|
29
|
+
parts.push(item.content);
|
|
30
|
+
currentChars = totalChars;
|
|
31
|
+
included.push(item.id ?? `item-${i}`);
|
|
32
|
+
} else excluded.push(item.id ?? `item-${i}`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
content: parts.join(separator),
|
|
36
|
+
tokens: estimateTokens(parts.join(separator), { charsPerToken }),
|
|
37
|
+
included,
|
|
38
|
+
excluded
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function splitByTokens(text, maxTokensPerChunk, options = {}) {
|
|
42
|
+
const { charsPerToken = CHARS_PER_TOKEN } = options;
|
|
43
|
+
const maxChars = maxTokensPerChunk * charsPerToken;
|
|
44
|
+
const chunks = [];
|
|
45
|
+
let start = 0;
|
|
46
|
+
while (start < text.length) {
|
|
47
|
+
let end = Math.min(start + maxChars, text.length);
|
|
48
|
+
if (end < text.length) {
|
|
49
|
+
const newlineIdx = text.lastIndexOf("\n", end);
|
|
50
|
+
if (newlineIdx > start) end = newlineIdx + 1;
|
|
51
|
+
}
|
|
52
|
+
chunks.push(text.slice(start, end));
|
|
53
|
+
start = end;
|
|
54
|
+
}
|
|
55
|
+
return chunks;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//#endregion
|
|
59
|
+
export { truncateToFit as i, estimateTokens as n, splitByTokens as r, buildContext as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "../index-
|
|
1
|
+
import { _ as getAgentConfig, a as getAgentSkillsDir, c as isCI, d as detectAgentByEnv, f as detectCurrentAgent, g as agents, h as AgentConfig, i as getAgentRulesPath, l as isTTY, m as isRunningInAgent, n as agentConfigExists, o as getXDGPaths, p as detectInstalledAgents, r as getAgentConfigDir, s as hasTTY, t as XDGPaths, u as DetectedAgent, v as getAgentIds, y as getAllAgents } from "../index-CzgY9GU2.mjs";
|
|
2
2
|
export { AgentConfig, DetectedAgent, XDGPaths, agentConfigExists, agents, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getXDGPaths, hasTTY, isCI, isRunningInAgent, isTTY };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "../
|
|
2
|
-
import "../path-
|
|
1
|
+
import { a as agentConfigExists, c as getAgentSkillsDir, d as isCI, f as isTTY, g as getAllAgents, h as getAgentIds, i as isRunningInAgent, l as getXDGPaths, m as getAgentConfig, n as detectCurrentAgent, o as getAgentConfigDir, p as agents, r as detectInstalledAgents, s as getAgentRulesPath, t as detectAgentByEnv, u as hasTTY } from "../detect-CDtKsCsD.mjs";
|
|
2
|
+
import "../path-Dm-URQvz.mjs";
|
|
3
|
+
import "../env-DeXFcrWN.mjs";
|
|
3
4
|
|
|
4
5
|
export { agentConfigExists, agents, detectAgentByEnv, detectCurrentAgent, detectInstalledAgents, getAgentConfig, getAgentConfigDir, getAgentIds, getAgentRulesPath, getAgentSkillsDir, getAllAgents, getXDGPaths, hasTTY, isCI, isRunningInAgent, isTTY };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
//#region src/exec/index.ts
|
|
4
|
+
const DEFAULT_TIMEOUT = 3e4;
|
|
5
|
+
const DEFAULT_MAX_OUTPUT = 1e5;
|
|
6
|
+
function execSafe(command, args, options = {}) {
|
|
7
|
+
const { cwd, timeout = DEFAULT_TIMEOUT, maxOutput = DEFAULT_MAX_OUTPUT, env, stdin } = options;
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
let stdout = "";
|
|
10
|
+
let stderr = "";
|
|
11
|
+
let truncated = false;
|
|
12
|
+
let timedOut = false;
|
|
13
|
+
let timeoutId;
|
|
14
|
+
const proc = spawn(command, args, {
|
|
15
|
+
cwd,
|
|
16
|
+
env: env ? {
|
|
17
|
+
...process.env,
|
|
18
|
+
...env
|
|
19
|
+
} : process.env,
|
|
20
|
+
stdio: [
|
|
21
|
+
"pipe",
|
|
22
|
+
"pipe",
|
|
23
|
+
"pipe"
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
const truncateOutput = (current, chunk) => {
|
|
27
|
+
const combined = current + chunk;
|
|
28
|
+
if (combined.length > maxOutput) {
|
|
29
|
+
truncated = true;
|
|
30
|
+
return combined.slice(0, maxOutput);
|
|
31
|
+
}
|
|
32
|
+
return combined;
|
|
33
|
+
};
|
|
34
|
+
proc.stdout?.on("data", (chunk) => {
|
|
35
|
+
stdout = truncateOutput(stdout, chunk.toString());
|
|
36
|
+
});
|
|
37
|
+
proc.stderr?.on("data", (chunk) => {
|
|
38
|
+
stderr = truncateOutput(stderr, chunk.toString());
|
|
39
|
+
});
|
|
40
|
+
if (stdin !== void 0) {
|
|
41
|
+
proc.stdin?.write(stdin);
|
|
42
|
+
proc.stdin?.end();
|
|
43
|
+
} else proc.stdin?.end();
|
|
44
|
+
if (timeout > 0) timeoutId = setTimeout(() => {
|
|
45
|
+
timedOut = true;
|
|
46
|
+
proc.kill("SIGTERM");
|
|
47
|
+
setTimeout(() => proc.kill("SIGKILL"), 1e3);
|
|
48
|
+
}, timeout);
|
|
49
|
+
proc.on("close", (code, signal) => {
|
|
50
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
51
|
+
resolve({
|
|
52
|
+
ok: code === 0,
|
|
53
|
+
code,
|
|
54
|
+
stdout: stdout.trim(),
|
|
55
|
+
stderr: stderr.trim(),
|
|
56
|
+
signal: signal ?? null,
|
|
57
|
+
truncated,
|
|
58
|
+
timedOut
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
proc.on("error", (err) => {
|
|
62
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
63
|
+
resolve({
|
|
64
|
+
ok: false,
|
|
65
|
+
code: null,
|
|
66
|
+
stdout: stdout.trim(),
|
|
67
|
+
stderr: err.message,
|
|
68
|
+
signal: null,
|
|
69
|
+
truncated,
|
|
70
|
+
timedOut
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function execSafeSync(command, args, options = {}) {
|
|
76
|
+
const { cwd, timeout = DEFAULT_TIMEOUT, maxOutput = DEFAULT_MAX_OUTPUT, env } = options;
|
|
77
|
+
let truncated = false;
|
|
78
|
+
const result = spawnSync(command, args, {
|
|
79
|
+
cwd,
|
|
80
|
+
timeout,
|
|
81
|
+
env: env ? {
|
|
82
|
+
...process.env,
|
|
83
|
+
...env
|
|
84
|
+
} : process.env,
|
|
85
|
+
encoding: "utf8",
|
|
86
|
+
stdio: [
|
|
87
|
+
"pipe",
|
|
88
|
+
"pipe",
|
|
89
|
+
"pipe"
|
|
90
|
+
],
|
|
91
|
+
maxBuffer: maxOutput * 2
|
|
92
|
+
});
|
|
93
|
+
const timedOut = result.signal === "SIGTERM";
|
|
94
|
+
let stdout = result.stdout ?? "";
|
|
95
|
+
let stderr = result.stderr ?? "";
|
|
96
|
+
if (stdout.length > maxOutput) {
|
|
97
|
+
stdout = stdout.slice(0, maxOutput);
|
|
98
|
+
truncated = true;
|
|
99
|
+
}
|
|
100
|
+
if (stderr.length > maxOutput) {
|
|
101
|
+
stderr = stderr.slice(0, maxOutput);
|
|
102
|
+
truncated = true;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
ok: result.status === 0,
|
|
106
|
+
code: result.status,
|
|
107
|
+
stdout: stdout.trim(),
|
|
108
|
+
stderr: stderr.trim(),
|
|
109
|
+
signal: result.signal ?? null,
|
|
110
|
+
truncated,
|
|
111
|
+
timedOut
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
//#endregion
|
|
116
|
+
export { execSafeSync as n, execSafe as t };
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { dirname, join, normalize } from "pathe";
|
|
2
|
+
import * as nodeFs from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
//#region src/fs/index.ts
|
|
5
|
+
function ensureLeadingSlash(path) {
|
|
6
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
7
|
+
}
|
|
8
|
+
var InMemoryFS = class {
|
|
9
|
+
files = /* @__PURE__ */ new Map();
|
|
10
|
+
dirs = new Set(["/"]);
|
|
11
|
+
normalizePath(path) {
|
|
12
|
+
return ensureLeadingSlash(path);
|
|
13
|
+
}
|
|
14
|
+
async readFile(path) {
|
|
15
|
+
const p = this.normalizePath(path);
|
|
16
|
+
const content = this.files.get(p);
|
|
17
|
+
if (content === void 0) throw new Error(`ENOENT: no such file: ${p}`);
|
|
18
|
+
return content;
|
|
19
|
+
}
|
|
20
|
+
async writeFile(path, content) {
|
|
21
|
+
const p = this.normalizePath(path);
|
|
22
|
+
const dir = dirname(p);
|
|
23
|
+
if (dir !== "/" && !this.dirs.has(dir)) throw new Error(`ENOENT: no such directory: ${dir}`);
|
|
24
|
+
this.files.set(p, content);
|
|
25
|
+
}
|
|
26
|
+
async exists(path) {
|
|
27
|
+
const p = this.normalizePath(path);
|
|
28
|
+
return this.files.has(p) || this.dirs.has(p);
|
|
29
|
+
}
|
|
30
|
+
async readdir(path) {
|
|
31
|
+
const p = this.normalizePath(path);
|
|
32
|
+
if (!this.dirs.has(p)) throw new Error(`ENOENT: no such directory: ${p}`);
|
|
33
|
+
const entries = /* @__PURE__ */ new Set();
|
|
34
|
+
const prefix = p === "/" ? "/" : `${p}/`;
|
|
35
|
+
for (const filePath of this.files.keys()) if (filePath.startsWith(prefix)) {
|
|
36
|
+
const name = filePath.slice(prefix.length).split("/")[0];
|
|
37
|
+
if (name) entries.add(name);
|
|
38
|
+
}
|
|
39
|
+
for (const dirPath of this.dirs) if (dirPath.startsWith(prefix) && dirPath !== p) {
|
|
40
|
+
const name = dirPath.slice(prefix.length).split("/")[0];
|
|
41
|
+
if (name) entries.add(name);
|
|
42
|
+
}
|
|
43
|
+
return [...entries];
|
|
44
|
+
}
|
|
45
|
+
async mkdir(path, options) {
|
|
46
|
+
const p = this.normalizePath(path);
|
|
47
|
+
if (this.dirs.has(p)) return;
|
|
48
|
+
if (options?.recursive) {
|
|
49
|
+
const parts = p.split("/").filter(Boolean);
|
|
50
|
+
let current = "";
|
|
51
|
+
for (const part of parts) {
|
|
52
|
+
current += `/${part}`;
|
|
53
|
+
this.dirs.add(current);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
const parent = dirname(p);
|
|
57
|
+
if (parent !== "/" && !this.dirs.has(parent)) throw new Error(`ENOENT: no such directory: ${parent}`);
|
|
58
|
+
this.dirs.add(p);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async rm(path, options) {
|
|
62
|
+
const p = this.normalizePath(path);
|
|
63
|
+
if (this.files.has(p)) {
|
|
64
|
+
this.files.delete(p);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!this.dirs.has(p)) throw new Error(`ENOENT: no such file or directory: ${p}`);
|
|
68
|
+
const prefix = p === "/" ? "/" : `${p}/`;
|
|
69
|
+
if (([...this.files.keys()].some((f) => f.startsWith(prefix)) || [...this.dirs].some((d) => d.startsWith(prefix) && d !== p)) && !options?.recursive) throw new Error(`ENOTEMPTY: directory not empty: ${p}`);
|
|
70
|
+
for (const filePath of this.files.keys()) if (filePath.startsWith(prefix)) this.files.delete(filePath);
|
|
71
|
+
for (const dirPath of this.dirs) if (dirPath.startsWith(prefix) || dirPath === p) this.dirs.delete(dirPath);
|
|
72
|
+
}
|
|
73
|
+
async stat(path) {
|
|
74
|
+
const p = this.normalizePath(path);
|
|
75
|
+
if (this.files.has(p)) return {
|
|
76
|
+
size: this.files.get(p).length,
|
|
77
|
+
isFile: true,
|
|
78
|
+
isDirectory: false
|
|
79
|
+
};
|
|
80
|
+
if (this.dirs.has(p)) return {
|
|
81
|
+
size: 0,
|
|
82
|
+
isFile: false,
|
|
83
|
+
isDirectory: true
|
|
84
|
+
};
|
|
85
|
+
throw new Error(`ENOENT: no such file or directory: ${p}`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var OverlayFS = class {
|
|
89
|
+
overlay;
|
|
90
|
+
deleted = /* @__PURE__ */ new Set();
|
|
91
|
+
constructor(base) {
|
|
92
|
+
this.base = base;
|
|
93
|
+
this.overlay = new InMemoryFS();
|
|
94
|
+
}
|
|
95
|
+
normalizePath(path) {
|
|
96
|
+
return ensureLeadingSlash(path);
|
|
97
|
+
}
|
|
98
|
+
async readFile(path) {
|
|
99
|
+
const p = this.normalizePath(path);
|
|
100
|
+
if (this.deleted.has(p)) throw new Error(`ENOENT: no such file: ${p}`);
|
|
101
|
+
try {
|
|
102
|
+
return await this.overlay.readFile(p);
|
|
103
|
+
} catch {
|
|
104
|
+
return await this.base.readFile(p);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async writeFile(path, content) {
|
|
108
|
+
const p = this.normalizePath(path);
|
|
109
|
+
this.deleted.delete(p);
|
|
110
|
+
const dir = dirname(p);
|
|
111
|
+
if (dir !== "/") try {
|
|
112
|
+
await this.overlay.mkdir(dir, { recursive: true });
|
|
113
|
+
} catch {}
|
|
114
|
+
await this.overlay.writeFile(p, content);
|
|
115
|
+
}
|
|
116
|
+
async exists(path) {
|
|
117
|
+
const p = this.normalizePath(path);
|
|
118
|
+
if (this.deleted.has(p)) return false;
|
|
119
|
+
if (await this.overlay.exists(p)) return true;
|
|
120
|
+
return await this.base.exists(p);
|
|
121
|
+
}
|
|
122
|
+
async readdir(path) {
|
|
123
|
+
const p = this.normalizePath(path);
|
|
124
|
+
const entries = /* @__PURE__ */ new Set();
|
|
125
|
+
try {
|
|
126
|
+
for (const e of await this.base.readdir(p)) entries.add(e);
|
|
127
|
+
} catch {}
|
|
128
|
+
try {
|
|
129
|
+
for (const e of await this.overlay.readdir(p)) entries.add(e);
|
|
130
|
+
} catch {}
|
|
131
|
+
for (const deleted of this.deleted) {
|
|
132
|
+
const prefix = p === "/" ? "/" : `${p}/`;
|
|
133
|
+
if (deleted.startsWith(prefix)) {
|
|
134
|
+
const name = deleted.slice(prefix.length).split("/")[0];
|
|
135
|
+
if (name && !deleted.slice(prefix.length).includes("/")) entries.delete(name);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return [...entries];
|
|
139
|
+
}
|
|
140
|
+
async mkdir(path, options) {
|
|
141
|
+
const p = this.normalizePath(path);
|
|
142
|
+
this.deleted.delete(p);
|
|
143
|
+
await this.overlay.mkdir(p, options);
|
|
144
|
+
}
|
|
145
|
+
async rm(path, options) {
|
|
146
|
+
const p = this.normalizePath(path);
|
|
147
|
+
try {
|
|
148
|
+
await this.overlay.rm(p, options);
|
|
149
|
+
} catch {}
|
|
150
|
+
this.deleted.add(p);
|
|
151
|
+
if (options?.recursive) {
|
|
152
|
+
const prefix = p === "/" ? "/" : `${p}/`;
|
|
153
|
+
for (const deleted of [...this.deleted]) if (deleted.startsWith(prefix)) this.deleted.add(deleted);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async stat(path) {
|
|
157
|
+
const p = this.normalizePath(path);
|
|
158
|
+
if (this.deleted.has(p)) throw new Error(`ENOENT: no such file or directory: ${p}`);
|
|
159
|
+
try {
|
|
160
|
+
return await this.overlay.stat(p);
|
|
161
|
+
} catch {
|
|
162
|
+
return await this.base.stat(p);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var RealFS = class {
|
|
167
|
+
normalizedBase;
|
|
168
|
+
constructor(basePath = "/") {
|
|
169
|
+
this.basePath = basePath;
|
|
170
|
+
this.normalizedBase = normalize(basePath);
|
|
171
|
+
}
|
|
172
|
+
resolve(path) {
|
|
173
|
+
const resolved = normalize(join(this.basePath, path));
|
|
174
|
+
if (!resolved.startsWith(this.normalizedBase) && resolved !== this.normalizedBase) throw new Error(`EACCES: path traversal not allowed: ${path}`);
|
|
175
|
+
return resolved;
|
|
176
|
+
}
|
|
177
|
+
async readFile(path) {
|
|
178
|
+
return await nodeFs.readFile(this.resolve(path), "utf-8");
|
|
179
|
+
}
|
|
180
|
+
async writeFile(path, content) {
|
|
181
|
+
await nodeFs.writeFile(this.resolve(path), content, "utf-8");
|
|
182
|
+
}
|
|
183
|
+
async exists(path) {
|
|
184
|
+
try {
|
|
185
|
+
await nodeFs.access(this.resolve(path));
|
|
186
|
+
return true;
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async readdir(path) {
|
|
192
|
+
return await nodeFs.readdir(this.resolve(path));
|
|
193
|
+
}
|
|
194
|
+
async mkdir(path, options) {
|
|
195
|
+
await nodeFs.mkdir(this.resolve(path), options);
|
|
196
|
+
}
|
|
197
|
+
async rm(path, options) {
|
|
198
|
+
await nodeFs.rm(this.resolve(path), options);
|
|
199
|
+
}
|
|
200
|
+
async stat(path) {
|
|
201
|
+
const stats = await nodeFs.stat(this.resolve(path));
|
|
202
|
+
return {
|
|
203
|
+
size: stats.size,
|
|
204
|
+
isFile: stats.isFile(),
|
|
205
|
+
isDirectory: stats.isDirectory(),
|
|
206
|
+
mtime: stats.mtime
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function createFSFromStorage(storage) {
|
|
211
|
+
const dirs = new Set(["/"]);
|
|
212
|
+
return {
|
|
213
|
+
async readFile(path) {
|
|
214
|
+
const content = await storage.getItem(path);
|
|
215
|
+
if (content === null) throw new Error(`ENOENT: no such file: ${path}`);
|
|
216
|
+
return content;
|
|
217
|
+
},
|
|
218
|
+
async writeFile(path, content) {
|
|
219
|
+
const dir = dirname(path);
|
|
220
|
+
if (dir !== "/") dirs.add(dir);
|
|
221
|
+
await storage.setItem(path, content);
|
|
222
|
+
},
|
|
223
|
+
async exists(path) {
|
|
224
|
+
if (dirs.has(path)) return true;
|
|
225
|
+
return await storage.getItem(path) !== null;
|
|
226
|
+
},
|
|
227
|
+
async readdir(path) {
|
|
228
|
+
const keys = await storage.getKeys(path);
|
|
229
|
+
const prefix = path === "/" ? "" : path;
|
|
230
|
+
const entries = /* @__PURE__ */ new Set();
|
|
231
|
+
for (const key of keys) if (key.startsWith(prefix)) {
|
|
232
|
+
const name = key.slice(prefix.length).replace(/^\//, "").split("/")[0];
|
|
233
|
+
if (name) entries.add(name);
|
|
234
|
+
}
|
|
235
|
+
return [...entries];
|
|
236
|
+
},
|
|
237
|
+
async mkdir(path, options) {
|
|
238
|
+
if (options?.recursive) {
|
|
239
|
+
const parts = path.split("/").filter(Boolean);
|
|
240
|
+
let current = "";
|
|
241
|
+
for (const part of parts) {
|
|
242
|
+
current += `/${part}`;
|
|
243
|
+
dirs.add(current);
|
|
244
|
+
}
|
|
245
|
+
} else dirs.add(path);
|
|
246
|
+
},
|
|
247
|
+
async rm(path) {
|
|
248
|
+
await storage.removeItem(path);
|
|
249
|
+
dirs.delete(path);
|
|
250
|
+
},
|
|
251
|
+
async stat(path) {
|
|
252
|
+
if (dirs.has(path)) return {
|
|
253
|
+
size: 0,
|
|
254
|
+
isFile: false,
|
|
255
|
+
isDirectory: true
|
|
256
|
+
};
|
|
257
|
+
const content = await storage.getItem(path);
|
|
258
|
+
if (content === null) throw new Error(`ENOENT: no such file or directory: ${path}`);
|
|
259
|
+
return {
|
|
260
|
+
size: content.length,
|
|
261
|
+
isFile: true,
|
|
262
|
+
isDirectory: false
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
export { createFSFromStorage as i, OverlayFS as n, RealFS as r, InMemoryFS as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "../index-
|
|
1
|
+
import { a as getGitStatus, c as hasUncommittedChanges, d as CloneResult, f as GitCloneError, g as isTempDir, h as cloneToTemp, i as getCurrentBranch, l as pull, m as cloneRepo, n as checkout, o as getLatestCommitHash, p as cleanupTempDir, r as fetch, s as getRemoteUrl, t as GitStatus, u as CloneOptions } from "../index-C55JaUgw.mjs";
|
|
2
2
|
export { CloneOptions, CloneResult, GitCloneError, GitStatus, checkout, cleanupTempDir, cloneRepo, cloneToTemp, fetch, getCurrentBranch, getGitStatus, getLatestCommitHash, getRemoteUrl, hasUncommittedChanges, isTempDir, pull };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { a as isTempDir, i as cloneToTemp, n as cleanupTempDir, r as cloneRepo, t as GitCloneError } from "../clone-DgXhqC05.mjs";
|
|
2
|
+
import { a as getLatestCommitHash, c as pull, i as getGitStatus, n as fetch, o as getRemoteUrl, r as getCurrentBranch, s as hasUncommittedChanges, t as checkout } from "../git-D3qsdy9d.mjs";
|
|
3
|
+
|
|
4
|
+
export { GitCloneError, checkout, cleanupTempDir, cloneRepo, cloneToTemp, fetch, getCurrentBranch, getGitStatus, getLatestCommitHash, getRemoteUrl, hasUncommittedChanges, isTempDir, pull };
|