@mclawnet/swarm 0.1.5 → 0.1.6
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/dist/__tests__/project-files.test.d.ts +2 -0
- package/dist/__tests__/project-files.test.d.ts.map +1 -0
- package/dist/__tests__/project-files.test.js +143 -0
- package/dist/__tests__/project-files.test.js.map +1 -0
- package/dist/__tests__/projects-fs.test.d.ts +2 -0
- package/dist/__tests__/projects-fs.test.d.ts.map +1 -0
- package/dist/__tests__/projects-fs.test.js +107 -0
- package/dist/__tests__/projects-fs.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/project-files.d.ts +60 -0
- package/dist/project-files.d.ts.map +1 -0
- package/dist/project-files.js +214 -0
- package/dist/project-files.js.map +1 -0
- package/dist/projects-fs.d.ts +28 -0
- package/dist/projects-fs.d.ts.map +1 -0
- package/dist/projects-fs.js +111 -0
- package/dist/projects-fs.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-files.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/project-files.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, symlinkSync, existsSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { PROJECT_FILE_WHITELIST, ProjectFilesError, isWhitelistedRelPath, listProjectFiles, readProjectFile, writeProjectFile, } from "../project-files.js";
|
|
6
|
+
let workDir;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
workDir = mkdtempSync(join(tmpdir(), "clawnet-project-files-"));
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
12
|
+
});
|
|
13
|
+
describe("isWhitelistedRelPath", () => {
|
|
14
|
+
it("accepts the four canonical paths", () => {
|
|
15
|
+
for (const p of PROJECT_FILE_WHITELIST)
|
|
16
|
+
expect(isWhitelistedRelPath(p)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it("rejects everything else", () => {
|
|
19
|
+
for (const p of [
|
|
20
|
+
"claude.md", // wrong case
|
|
21
|
+
"../CLAUDE.md", // traversal
|
|
22
|
+
".claude/rules/x.md", // not in P1 whitelist
|
|
23
|
+
".git/config",
|
|
24
|
+
"",
|
|
25
|
+
"CLAUDE.md/", // trailing slash mismatch
|
|
26
|
+
])
|
|
27
|
+
expect(isWhitelistedRelPath(p)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("listProjectFiles", () => {
|
|
31
|
+
it("returns whitelist entries with exists=false when nothing on disk", () => {
|
|
32
|
+
const files = listProjectFiles(workDir);
|
|
33
|
+
expect(files).toHaveLength(PROJECT_FILE_WHITELIST.length);
|
|
34
|
+
for (const f of files) {
|
|
35
|
+
expect(f.exists).toBe(false);
|
|
36
|
+
expect(f.loadedByCli).toBe(true);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
it("reports size + mtime for files that exist", () => {
|
|
40
|
+
writeFileSync(join(workDir, "CLAUDE.md"), "hi");
|
|
41
|
+
const files = listProjectFiles(workDir);
|
|
42
|
+
const claude = files.find((f) => f.relPath === "CLAUDE.md");
|
|
43
|
+
expect(claude.exists).toBe(true);
|
|
44
|
+
expect(claude.size).toBe(2);
|
|
45
|
+
expect(typeof claude.mtime).toBe("number");
|
|
46
|
+
});
|
|
47
|
+
it("does not throw when workDir was deleted out from under us", () => {
|
|
48
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
49
|
+
const files = listProjectFiles(workDir);
|
|
50
|
+
expect(files.every((f) => !f.exists)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("readProjectFile", () => {
|
|
54
|
+
it("returns empty content + valid etag for a missing file", () => {
|
|
55
|
+
const r = readProjectFile(workDir, "CLAUDE.md");
|
|
56
|
+
expect(r.content).toBe("");
|
|
57
|
+
expect(r.etag).toMatch(/^[0-9a-f]{12}$/);
|
|
58
|
+
expect(r.mtime).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
it("reads existing file content", () => {
|
|
61
|
+
writeFileSync(join(workDir, "CLAUDE.md"), "always reply in chinese\n");
|
|
62
|
+
const r = readProjectFile(workDir, "CLAUDE.md");
|
|
63
|
+
expect(r.content).toBe("always reply in chinese\n");
|
|
64
|
+
expect(r.mtime).toBeGreaterThan(0);
|
|
65
|
+
});
|
|
66
|
+
it("rejects non-whitelisted paths", () => {
|
|
67
|
+
expect(() => readProjectFile(workDir, "../etc/passwd")).toThrow(ProjectFilesError);
|
|
68
|
+
expect(() => readProjectFile(workDir, ".git/config")).toThrow(ProjectFilesError);
|
|
69
|
+
});
|
|
70
|
+
it("rejects symlinks pointing outside workDir", () => {
|
|
71
|
+
// Create an outside-workDir target that definitely exists.
|
|
72
|
+
const outside = mkdtempSync(join(tmpdir(), "clawnet-outside-"));
|
|
73
|
+
const target = join(outside, "leaked.md");
|
|
74
|
+
writeFileSync(target, "secret");
|
|
75
|
+
mkdirSync(join(workDir, ".claude"));
|
|
76
|
+
try {
|
|
77
|
+
symlinkSync(target, join(workDir, ".claude", "CLAUDE.md"));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
rmSync(outside, { recursive: true, force: true });
|
|
81
|
+
return; // skip on platforms where we can't symlink
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
expect(() => readProjectFile(workDir, ".claude/CLAUDE.md")).toThrow(ProjectFilesError);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
rmSync(outside, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("writeProjectFile", () => {
|
|
92
|
+
it("creates the file when it does not exist", () => {
|
|
93
|
+
const r = writeProjectFile(workDir, "CLAUDE.md", "hello");
|
|
94
|
+
expect(r.content).toBe("hello\n"); // md gets trailing newline
|
|
95
|
+
expect(readFileSync(join(workDir, "CLAUDE.md"), "utf-8")).toBe("hello\n");
|
|
96
|
+
});
|
|
97
|
+
it("creates .claude/ directory automatically", () => {
|
|
98
|
+
writeProjectFile(workDir, ".claude/settings.json", '{"foo":1}');
|
|
99
|
+
expect(existsSync(join(workDir, ".claude"))).toBe(true);
|
|
100
|
+
expect(readFileSync(join(workDir, ".claude", "settings.json"), "utf-8")).toBe('{"foo":1}');
|
|
101
|
+
});
|
|
102
|
+
it("rejects invalid JSON for .json files", () => {
|
|
103
|
+
expect(() => writeProjectFile(workDir, ".claude/settings.json", "{not json")).toThrow(ProjectFilesError);
|
|
104
|
+
expect(existsSync(join(workDir, ".claude", "settings.json"))).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it("normalizes empty JSON to '{}'", () => {
|
|
107
|
+
writeProjectFile(workDir, ".claude/settings.json", "");
|
|
108
|
+
const txt = readFileSync(join(workDir, ".claude", "settings.json"), "utf-8");
|
|
109
|
+
expect(txt).toBe("{}\n");
|
|
110
|
+
});
|
|
111
|
+
it("etag mismatch returns 409 with current content", () => {
|
|
112
|
+
writeProjectFile(workDir, "CLAUDE.md", "v1");
|
|
113
|
+
expect(() => writeProjectFile(workDir, "CLAUDE.md", "v2", { ifMatchEtag: "stale-etag" })).toThrow(ProjectFilesError);
|
|
114
|
+
try {
|
|
115
|
+
writeProjectFile(workDir, "CLAUDE.md", "v2", { ifMatchEtag: "stale-etag" });
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const e = err;
|
|
119
|
+
expect(e.status).toBe(409);
|
|
120
|
+
expect(e.details?.currentContent).toBe("v1\n");
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
it("etag match permits the write", () => {
|
|
124
|
+
const first = writeProjectFile(workDir, "CLAUDE.md", "v1");
|
|
125
|
+
const second = writeProjectFile(workDir, "CLAUDE.md", "v2", { ifMatchEtag: first.etag });
|
|
126
|
+
expect(second.content).toBe("v2\n");
|
|
127
|
+
expect(second.etag).not.toBe(first.etag);
|
|
128
|
+
});
|
|
129
|
+
it("rejects non-whitelisted writes", () => {
|
|
130
|
+
expect(() => writeProjectFile(workDir, "../escape.md", "x")).toThrow(ProjectFilesError);
|
|
131
|
+
});
|
|
132
|
+
it("write is atomic (no partial file visible if rename fails — smoke check via tmp cleanup)", () => {
|
|
133
|
+
writeProjectFile(workDir, "CLAUDE.md", "atomic");
|
|
134
|
+
// Confirm no `.tmp` siblings remain.
|
|
135
|
+
const entries = readFileSync(join(workDir, "CLAUDE.md"), "utf-8");
|
|
136
|
+
expect(entries).toBe("atomic\n");
|
|
137
|
+
// The tmp pattern is `.<hex>.tmp` — none should remain.
|
|
138
|
+
const dirents = require("node:fs").readdirSync(workDir);
|
|
139
|
+
for (const d of dirents)
|
|
140
|
+
expect(d).not.toMatch(/^\..*\.tmp$/);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=project-files.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-files.test.js","sourceRoot":"","sources":["../../src/__tests__/project-files.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects-fs.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/projects-fs.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore, projectRoot } from "@mclawnet/task";
|
|
6
|
+
import { listAllProjectSummaries, loadProjectSummary, loadSwarmSummaries, resolveWorkDir, } from "../projects-fs.js";
|
|
7
|
+
let home;
|
|
8
|
+
let workDir;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
home = mkdtempSync(join(tmpdir(), "swarm-projects-fs-"));
|
|
11
|
+
workDir = mkdtempSync(join(tmpdir(), "swarm-projects-fs-wd-"));
|
|
12
|
+
const root = projectRoot(workDir, home);
|
|
13
|
+
mkdirSync(root, { recursive: true });
|
|
14
|
+
writeFileSync(join(root, "meta.json"), JSON.stringify({
|
|
15
|
+
version: 1, workDir, createdAt: "2026-05-09T00:00:00Z",
|
|
16
|
+
}));
|
|
17
|
+
await new TaskStore({ workDir, home }).create({
|
|
18
|
+
teamName: "dev", subject: "task A",
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
rmSync(home, { recursive: true, force: true });
|
|
23
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
describe("projects-fs", () => {
|
|
26
|
+
it("listAllProjectSummaries returns one summary with task count 1", () => {
|
|
27
|
+
const summaries = listAllProjectSummaries(home);
|
|
28
|
+
expect(summaries).toHaveLength(1);
|
|
29
|
+
expect(summaries[0].workDir).toBe(workDir);
|
|
30
|
+
expect(summaries[0].taskCount).toBe(1);
|
|
31
|
+
expect(summaries[0].taskCounts.pending).toBe(1);
|
|
32
|
+
});
|
|
33
|
+
it("resolveWorkDir round-trips an encodedCwd", () => {
|
|
34
|
+
const enc = listAllProjectSummaries(home)[0].encodedCwd;
|
|
35
|
+
expect(resolveWorkDir(home, enc)).toBe(workDir);
|
|
36
|
+
});
|
|
37
|
+
it("loadProjectSummary returns null for unknown encodedCwd", () => {
|
|
38
|
+
expect(loadProjectSummary(home, "-not-a-real-dir")).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
it("loadSwarmSummaries returns [] when no swarms exist", () => {
|
|
41
|
+
expect(loadSwarmSummaries(home, workDir)).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
it("rejects unsafe encodedCwd containing slashes or ..", () => {
|
|
44
|
+
expect(resolveWorkDir(home, "../etc")).toBeNull();
|
|
45
|
+
expect(resolveWorkDir(home, "a/b")).toBeNull();
|
|
46
|
+
expect(resolveWorkDir(home, "")).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
it("loadSwarmSummaries returns one summary mapping snapshot fields", () => {
|
|
49
|
+
// Build a minimal recovery.json snapshot under
|
|
50
|
+
// <projectRoot>/swarms/<swarmId>/recovery.json — this is the actual
|
|
51
|
+
// filename that loadSwarmSnapshot() reads (see persistence.ts).
|
|
52
|
+
// Note: persistence.ts derives its own home from CLAWNET_HOME, so we
|
|
53
|
+
// must set it here to point swarm-side resolution at our temp dir.
|
|
54
|
+
const swarmId = "swarm-test-1";
|
|
55
|
+
const swarmsDir = join(projectRoot(workDir, home), "swarms", swarmId);
|
|
56
|
+
mkdirSync(swarmsDir, { recursive: true });
|
|
57
|
+
const snapshot = {
|
|
58
|
+
id: swarmId,
|
|
59
|
+
hubSessionId: "hub-1",
|
|
60
|
+
workDir,
|
|
61
|
+
teamName: "dev",
|
|
62
|
+
roles: [
|
|
63
|
+
{ instanceId: "queen-0", roleName: "queen", status: "ready" },
|
|
64
|
+
{ instanceId: "worker-0", roleName: "worker", status: "ready" },
|
|
65
|
+
],
|
|
66
|
+
plan: null,
|
|
67
|
+
nextInstanceSeq: {},
|
|
68
|
+
savedAt: 12345,
|
|
69
|
+
status: "active",
|
|
70
|
+
};
|
|
71
|
+
writeFileSync(join(swarmsDir, "recovery.json"), JSON.stringify(snapshot));
|
|
72
|
+
const prevHome = process.env.CLAWNET_HOME;
|
|
73
|
+
process.env.CLAWNET_HOME = home;
|
|
74
|
+
try {
|
|
75
|
+
const summaries = loadSwarmSummaries(home, workDir);
|
|
76
|
+
expect(summaries).toHaveLength(1);
|
|
77
|
+
expect(summaries[0]).toEqual({
|
|
78
|
+
swarmId,
|
|
79
|
+
teamName: "dev",
|
|
80
|
+
status: "active",
|
|
81
|
+
roleCount: 2,
|
|
82
|
+
savedAt: 12345,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
if (prevHome === undefined)
|
|
87
|
+
delete process.env.CLAWNET_HOME;
|
|
88
|
+
else
|
|
89
|
+
process.env.CLAWNET_HOME = prevHome;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
it("counts non-pending task statuses correctly", async () => {
|
|
93
|
+
// beforeEach already created one pending task. Add a second task and
|
|
94
|
+
// mutate it to in_progress via TaskStore.update() so the
|
|
95
|
+
// `if (t.status in counts)` branch at projects-fs.ts:90 is exercised
|
|
96
|
+
// for a non-pending status.
|
|
97
|
+
const store = new TaskStore({ workDir, home });
|
|
98
|
+
const second = await store.create({ teamName: "dev", subject: "task B" });
|
|
99
|
+
await store.update(second.id, { status: "in_progress" });
|
|
100
|
+
const summaries = listAllProjectSummaries(home);
|
|
101
|
+
expect(summaries).toHaveLength(1);
|
|
102
|
+
expect(summaries[0].taskCount).toBe(2);
|
|
103
|
+
expect(summaries[0].taskCounts.pending).toBe(1);
|
|
104
|
+
expect(summaries[0].taskCounts.in_progress).toBe(1);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=projects-fs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects-fs.test.js","sourceRoot":"","sources":["../../src/__tests__/projects-fs.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,4 +13,8 @@ export type { SessionAdapter, HubAdapter, SwarmInstance, RoleInstance, SwarmActi
|
|
|
13
13
|
export type { RoleDefinition, DelegationMap, DelegationEntry } from "./roles/types.js";
|
|
14
14
|
export { initTemplates, listTemplates, loadTemplate } from "./templates/template-loader.js";
|
|
15
15
|
export type { TemplateDefinition, TemplateRoleSpec } from "./templates/types.js";
|
|
16
|
+
export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
|
|
17
|
+
export type { TaskStatus, TaskCounts, ProjectSummary, SwarmSummary, } from "./projects-fs.js";
|
|
18
|
+
export { PROJECT_FILE_WHITELIST, isWhitelistedRelPath, listProjectFiles, readProjectFile, writeProjectFile, ProjectFilesError, } from "./project-files.js";
|
|
19
|
+
export type { ProjectFileRelPath, ProjectFileEntry, ProjectFileContent, } from "./project-files.js";
|
|
16
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACxG,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhG,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC5F,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACxG,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhG,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEvF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC5F,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,4 +8,6 @@ export { InboxStore } from "./inbox-store.js";
|
|
|
8
8
|
export { InboxRelay } from "./inbox-relay.js";
|
|
9
9
|
export { InboxWatcher } from "./inbox-watcher.js";
|
|
10
10
|
export { initTemplates, listTemplates, loadTemplate } from "./templates/template-loader.js";
|
|
11
|
+
export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
|
|
12
|
+
export { PROJECT_FILE_WHITELIST, isWhitelistedRelPath, listProjectFiles, readProjectFile, writeProjectFile, ProjectFilesError, } from "./project-files.js";
|
|
11
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACxG,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,CAAC;AAiBhG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACxG,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,CAAC;AAiBhG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAG5F,OAAO,EACL,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project files endpoint helpers.
|
|
3
|
+
*
|
|
4
|
+
* Surfaces a fixed whitelist of files inside a project's `workDir` (CLAUDE.md
|
|
5
|
+
* and .claude/settings*.json) so the UI can read/write them without giving
|
|
6
|
+
* blanket FS access.
|
|
7
|
+
*
|
|
8
|
+
* Why a hard-coded whitelist (no globs in P1):
|
|
9
|
+
* The Claude CLI auto-loads exactly these paths from cwd. Limiting to the
|
|
10
|
+
* exact set lets us audit every read/write and rules out mistakes like a
|
|
11
|
+
* user typing `relPath=../../etc/passwd` or `.git/config`. Phase 2 may add
|
|
12
|
+
* `.claude/rules/*.md`, which will require a careful prefix-glob design.
|
|
13
|
+
*
|
|
14
|
+
* Defense-in-depth path checks:
|
|
15
|
+
* 1. relPath must be in WHITELIST (exact string match)
|
|
16
|
+
* 2. Resolved absolute path must live under realpath(workDir)
|
|
17
|
+
* 3. realpath() resolves symlinks, so a symlink inside .claude/ that
|
|
18
|
+
* points outside workDir is still rejected by check (2).
|
|
19
|
+
*/
|
|
20
|
+
export declare const PROJECT_FILE_WHITELIST: readonly ["CLAUDE.md", ".claude/CLAUDE.md", ".claude/settings.json", ".claude/settings.local.json"];
|
|
21
|
+
export type ProjectFileRelPath = (typeof PROJECT_FILE_WHITELIST)[number];
|
|
22
|
+
export interface ProjectFileEntry {
|
|
23
|
+
relPath: ProjectFileRelPath;
|
|
24
|
+
exists: boolean;
|
|
25
|
+
size?: number;
|
|
26
|
+
mtime?: number;
|
|
27
|
+
loadedByCli: true;
|
|
28
|
+
}
|
|
29
|
+
export interface ProjectFileContent {
|
|
30
|
+
relPath: ProjectFileRelPath;
|
|
31
|
+
content: string;
|
|
32
|
+
mtime: number;
|
|
33
|
+
etag: string;
|
|
34
|
+
}
|
|
35
|
+
export declare class ProjectFilesError extends Error {
|
|
36
|
+
status: number;
|
|
37
|
+
details?: Record<string, unknown> | undefined;
|
|
38
|
+
constructor(status: number, message: string, details?: Record<string, unknown> | undefined);
|
|
39
|
+
}
|
|
40
|
+
/** Type guard — narrows an arbitrary string to the whitelist union. */
|
|
41
|
+
export declare function isWhitelistedRelPath(rel: string): rel is ProjectFileRelPath;
|
|
42
|
+
/** List the whitelist with stat info for present files. Never throws. */
|
|
43
|
+
export declare function listProjectFiles(workDir: string): ProjectFileEntry[];
|
|
44
|
+
/**
|
|
45
|
+
* Read a whitelisted file. Returns content="" + etag of empty string when
|
|
46
|
+
* the file does not exist, so the editor UI can populate a blank textarea
|
|
47
|
+
* with a valid etag for first-write conflict detection.
|
|
48
|
+
*/
|
|
49
|
+
export declare function readProjectFile(workDir: string, relPath: string): ProjectFileContent;
|
|
50
|
+
export interface WriteOptions {
|
|
51
|
+
ifMatchEtag?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Atomic write: validates JSON syntax for `.json` files, ensures parent
|
|
55
|
+
* `.claude/` exists, writes to a tmp sibling, then renames. Returns the
|
|
56
|
+
* post-write content snapshot. On etag mismatch throws 409 with the current
|
|
57
|
+
* server-side content so the UI can render a 3-way diff.
|
|
58
|
+
*/
|
|
59
|
+
export declare function writeProjectFile(workDir: string, relPath: string, newContent: string, opts?: WriteOptions): ProjectFileContent;
|
|
60
|
+
//# sourceMappingURL=project-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-files.d.ts","sourceRoot":"","sources":["../src/project-files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAcH,eAAO,MAAM,sBAAsB,qGAKzB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAEjC,MAAM,EAAE,MAAM;IAEd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjC,MAAM,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACR,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAK3C;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,kBAAkB,CAE3E;AA8CD,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmCpE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAmBpF;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,YAAiB,GACtB,kBAAkB,CA6DpB"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project files endpoint helpers.
|
|
3
|
+
*
|
|
4
|
+
* Surfaces a fixed whitelist of files inside a project's `workDir` (CLAUDE.md
|
|
5
|
+
* and .claude/settings*.json) so the UI can read/write them without giving
|
|
6
|
+
* blanket FS access.
|
|
7
|
+
*
|
|
8
|
+
* Why a hard-coded whitelist (no globs in P1):
|
|
9
|
+
* The Claude CLI auto-loads exactly these paths from cwd. Limiting to the
|
|
10
|
+
* exact set lets us audit every read/write and rules out mistakes like a
|
|
11
|
+
* user typing `relPath=../../etc/passwd` or `.git/config`. Phase 2 may add
|
|
12
|
+
* `.claude/rules/*.md`, which will require a careful prefix-glob design.
|
|
13
|
+
*
|
|
14
|
+
* Defense-in-depth path checks:
|
|
15
|
+
* 1. relPath must be in WHITELIST (exact string match)
|
|
16
|
+
* 2. Resolved absolute path must live under realpath(workDir)
|
|
17
|
+
* 3. realpath() resolves symlinks, so a symlink inside .claude/ that
|
|
18
|
+
* points outside workDir is still rejected by check (2).
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync, } from "node:fs";
|
|
21
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
22
|
+
import { dirname, join, sep } from "node:path";
|
|
23
|
+
export const PROJECT_FILE_WHITELIST = [
|
|
24
|
+
"CLAUDE.md",
|
|
25
|
+
".claude/CLAUDE.md",
|
|
26
|
+
".claude/settings.json",
|
|
27
|
+
".claude/settings.local.json",
|
|
28
|
+
];
|
|
29
|
+
export class ProjectFilesError extends Error {
|
|
30
|
+
status;
|
|
31
|
+
details;
|
|
32
|
+
constructor(status, message, details) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.status = status;
|
|
35
|
+
this.details = details;
|
|
36
|
+
this.name = "ProjectFilesError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Type guard — narrows an arbitrary string to the whitelist union. */
|
|
40
|
+
export function isWhitelistedRelPath(rel) {
|
|
41
|
+
return PROJECT_FILE_WHITELIST.includes(rel);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve `relPath` against `workDir` and verify the result is still inside
|
|
45
|
+
* workDir even after symlink resolution. Throws ProjectFilesError on any
|
|
46
|
+
* violation. Returns the absolute path (which may not exist yet — caller
|
|
47
|
+
* decides whether absence is OK).
|
|
48
|
+
*/
|
|
49
|
+
function resolveSafe(workDir, relPath) {
|
|
50
|
+
// workDir itself must exist for any of this to make sense.
|
|
51
|
+
let workDirReal;
|
|
52
|
+
try {
|
|
53
|
+
workDirReal = realpathSync(workDir);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new ProjectFilesError(404, "workDir does not exist on disk");
|
|
57
|
+
}
|
|
58
|
+
const candidate = join(workDirReal, relPath);
|
|
59
|
+
// For an existing file, realpath the file. For a not-yet-existing file,
|
|
60
|
+
// realpath the *parent* (which we may need to mkdir later) — this still
|
|
61
|
+
// catches symlink escapes from intermediate components.
|
|
62
|
+
let probe;
|
|
63
|
+
if (existsSync(candidate)) {
|
|
64
|
+
probe = realpathSync(candidate);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Walk up to the first existing ancestor and realpath it; that's enough
|
|
68
|
+
// to ensure we won't end up writing outside workDir even after rename().
|
|
69
|
+
let parent = dirname(candidate);
|
|
70
|
+
while (!existsSync(parent))
|
|
71
|
+
parent = dirname(parent);
|
|
72
|
+
probe = realpathSync(parent);
|
|
73
|
+
}
|
|
74
|
+
if (probe !== workDirReal && !probe.startsWith(workDirReal + sep)) {
|
|
75
|
+
throw new ProjectFilesError(403, "Path resolves outside workDir", {
|
|
76
|
+
relPath,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return candidate;
|
|
80
|
+
}
|
|
81
|
+
function computeEtag(content) {
|
|
82
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
83
|
+
}
|
|
84
|
+
/** List the whitelist with stat info for present files. Never throws. */
|
|
85
|
+
export function listProjectFiles(workDir) {
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const rel of PROJECT_FILE_WHITELIST) {
|
|
88
|
+
let abs;
|
|
89
|
+
try {
|
|
90
|
+
abs = resolveSafe(workDir, rel);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// workDir missing or path escape — surface as "not exists" rather than
|
|
94
|
+
// erroring the whole list call (e.g. a project whose workDir was deleted
|
|
95
|
+
// on disk should still be navigable).
|
|
96
|
+
out.push({ relPath: rel, exists: false, loadedByCli: true });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (!existsSync(abs)) {
|
|
100
|
+
out.push({ relPath: rel, exists: false, loadedByCli: true });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const st = statSync(abs);
|
|
105
|
+
if (!st.isFile()) {
|
|
106
|
+
out.push({ relPath: rel, exists: false, loadedByCli: true });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
out.push({
|
|
110
|
+
relPath: rel,
|
|
111
|
+
exists: true,
|
|
112
|
+
size: st.size,
|
|
113
|
+
mtime: st.mtime.getTime(),
|
|
114
|
+
loadedByCli: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
out.push({ relPath: rel, exists: false, loadedByCli: true });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Read a whitelisted file. Returns content="" + etag of empty string when
|
|
125
|
+
* the file does not exist, so the editor UI can populate a blank textarea
|
|
126
|
+
* with a valid etag for first-write conflict detection.
|
|
127
|
+
*/
|
|
128
|
+
export function readProjectFile(workDir, relPath) {
|
|
129
|
+
if (!isWhitelistedRelPath(relPath)) {
|
|
130
|
+
throw new ProjectFilesError(400, "relPath not in whitelist", { relPath });
|
|
131
|
+
}
|
|
132
|
+
const abs = resolveSafe(workDir, relPath);
|
|
133
|
+
if (!existsSync(abs)) {
|
|
134
|
+
return { relPath, content: "", mtime: 0, etag: computeEtag("") };
|
|
135
|
+
}
|
|
136
|
+
const st = statSync(abs);
|
|
137
|
+
if (!st.isFile()) {
|
|
138
|
+
throw new ProjectFilesError(400, "Path is not a regular file", { relPath });
|
|
139
|
+
}
|
|
140
|
+
const content = readFileSync(abs, "utf-8");
|
|
141
|
+
return {
|
|
142
|
+
relPath,
|
|
143
|
+
content,
|
|
144
|
+
mtime: st.mtime.getTime(),
|
|
145
|
+
etag: computeEtag(content),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Atomic write: validates JSON syntax for `.json` files, ensures parent
|
|
150
|
+
* `.claude/` exists, writes to a tmp sibling, then renames. Returns the
|
|
151
|
+
* post-write content snapshot. On etag mismatch throws 409 with the current
|
|
152
|
+
* server-side content so the UI can render a 3-way diff.
|
|
153
|
+
*/
|
|
154
|
+
export function writeProjectFile(workDir, relPath, newContent, opts = {}) {
|
|
155
|
+
if (!isWhitelistedRelPath(relPath)) {
|
|
156
|
+
throw new ProjectFilesError(400, "relPath not in whitelist", { relPath });
|
|
157
|
+
}
|
|
158
|
+
// Optimistic-concurrency check happens BEFORE any FS mutation.
|
|
159
|
+
if (opts.ifMatchEtag !== undefined) {
|
|
160
|
+
const current = readProjectFile(workDir, relPath);
|
|
161
|
+
if (current.etag !== opts.ifMatchEtag) {
|
|
162
|
+
throw new ProjectFilesError(409, "etag mismatch", {
|
|
163
|
+
relPath,
|
|
164
|
+
currentEtag: current.etag,
|
|
165
|
+
currentContent: current.content,
|
|
166
|
+
currentMtime: current.mtime,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Validate JSON before touching disk so we never leave broken settings.json.
|
|
171
|
+
if (relPath.endsWith(".json")) {
|
|
172
|
+
if (newContent.trim().length === 0) {
|
|
173
|
+
// Empty JSON → store as `{}` so the file remains valid for CLI consumers.
|
|
174
|
+
newContent = "{}\n";
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
try {
|
|
178
|
+
JSON.parse(newContent);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
throw new ProjectFilesError(400, "Invalid JSON", {
|
|
182
|
+
relPath,
|
|
183
|
+
parseError: err instanceof Error ? err.message : String(err),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (relPath.endsWith(".md")) {
|
|
189
|
+
// Ensure trailing newline — POSIX text-file convention; avoids editors
|
|
190
|
+
// appending a newline on next manual edit and producing spurious diffs.
|
|
191
|
+
if (newContent.length > 0 && !newContent.endsWith("\n")) {
|
|
192
|
+
newContent = newContent + "\n";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const abs = resolveSafe(workDir, relPath);
|
|
196
|
+
const parent = dirname(abs);
|
|
197
|
+
if (!existsSync(parent)) {
|
|
198
|
+
mkdirSync(parent, { recursive: true });
|
|
199
|
+
}
|
|
200
|
+
// tmp file lives in the same directory so rename() is atomic on the same FS.
|
|
201
|
+
// randomBytes avoids collisions if two writes race; the loser gets EEXIST on
|
|
202
|
+
// its own tmp (very unlikely) and retries via the API layer.
|
|
203
|
+
const tmp = join(parent, `.${randomBytes(6).toString("hex")}.tmp`);
|
|
204
|
+
writeFileSync(tmp, newContent, "utf-8");
|
|
205
|
+
renameSync(tmp, abs);
|
|
206
|
+
const st = statSync(abs);
|
|
207
|
+
return {
|
|
208
|
+
relPath,
|
|
209
|
+
content: newContent,
|
|
210
|
+
mtime: st.mtime.getTime(),
|
|
211
|
+
etag: computeEtag(newContent),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=project-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-files.js","sourceRoot":"","sources":["../src/project-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type TaskStatus = "pending" | "in_progress" | "completed" | "cancelled";
|
|
2
|
+
export interface TaskCounts {
|
|
3
|
+
pending: number;
|
|
4
|
+
in_progress: number;
|
|
5
|
+
completed: number;
|
|
6
|
+
cancelled: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ProjectSummary {
|
|
9
|
+
encodedCwd: string;
|
|
10
|
+
workDir: string;
|
|
11
|
+
createdAt: string | null;
|
|
12
|
+
swarmCount: number;
|
|
13
|
+
taskCount: number;
|
|
14
|
+
taskCounts: TaskCounts;
|
|
15
|
+
}
|
|
16
|
+
export interface SwarmSummary {
|
|
17
|
+
swarmId: string;
|
|
18
|
+
teamName: string;
|
|
19
|
+
status: string;
|
|
20
|
+
roleCount: number;
|
|
21
|
+
savedAt: number | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function listProjectDirs(home?: string): string[];
|
|
24
|
+
export declare function resolveWorkDir(home: string, encodedCwd: string): string | null;
|
|
25
|
+
export declare function loadProjectSummary(home: string, encodedCwd: string): ProjectSummary | null;
|
|
26
|
+
export declare function loadSwarmSummaries(home: string, workDir: string): SwarmSummary[];
|
|
27
|
+
export declare function listAllProjectSummaries(home?: string): ProjectSummary[];
|
|
28
|
+
//# sourceMappingURL=projects-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects-fs.d.ts","sourceRoot":"","sources":["../src/projects-fs.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC;AAC/E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACxB;AACD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAkBD,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAMvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY9E;AAiBD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAkB1F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAiBhF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAKvE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { TaskStore, projectRoot } from "@mclawnet/task";
|
|
5
|
+
import { loadSwarmSnapshot } from "./persistence.js";
|
|
6
|
+
function getHome(homeOverride) {
|
|
7
|
+
return homeOverride ?? process.env.CLAWNET_HOME ?? homedir();
|
|
8
|
+
}
|
|
9
|
+
function projectsDir(home) {
|
|
10
|
+
return join(home, ".clawnet", "projects");
|
|
11
|
+
}
|
|
12
|
+
function isSafeEncoded(encoded) {
|
|
13
|
+
if (!encoded)
|
|
14
|
+
return false;
|
|
15
|
+
if (encoded.includes("/") || encoded.includes("\\"))
|
|
16
|
+
return false;
|
|
17
|
+
if (encoded.includes("\0"))
|
|
18
|
+
return false;
|
|
19
|
+
if (encoded === "." || encoded === ".." || encoded.includes(".."))
|
|
20
|
+
return false;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
export function listProjectDirs(home) {
|
|
24
|
+
const dir = projectsDir(getHome(home));
|
|
25
|
+
if (!existsSync(dir))
|
|
26
|
+
return [];
|
|
27
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
28
|
+
.filter((e) => e.isDirectory())
|
|
29
|
+
.map((e) => e.name);
|
|
30
|
+
}
|
|
31
|
+
export function resolveWorkDir(home, encodedCwd) {
|
|
32
|
+
if (!isSafeEncoded(encodedCwd))
|
|
33
|
+
return null;
|
|
34
|
+
const dirs = listProjectDirs(home);
|
|
35
|
+
if (!dirs.includes(encodedCwd))
|
|
36
|
+
return null;
|
|
37
|
+
const metaPath = join(projectsDir(home), encodedCwd, "meta.json");
|
|
38
|
+
if (!existsSync(metaPath))
|
|
39
|
+
return null;
|
|
40
|
+
try {
|
|
41
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
42
|
+
return typeof meta.workDir === "string" ? meta.workDir : null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function readMetaCreatedAt(home, encodedCwd) {
|
|
49
|
+
const metaPath = join(projectsDir(home), encodedCwd, "meta.json");
|
|
50
|
+
if (!existsSync(metaPath))
|
|
51
|
+
return null;
|
|
52
|
+
try {
|
|
53
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
54
|
+
return meta.createdAt ?? null;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function emptyCounts() {
|
|
61
|
+
return { pending: 0, in_progress: 0, completed: 0, cancelled: 0 };
|
|
62
|
+
}
|
|
63
|
+
export function loadProjectSummary(home, encodedCwd) {
|
|
64
|
+
const workDir = resolveWorkDir(home, encodedCwd);
|
|
65
|
+
if (!workDir)
|
|
66
|
+
return null;
|
|
67
|
+
const store = new TaskStore({ workDir, home });
|
|
68
|
+
const tasks = store.list();
|
|
69
|
+
const counts = emptyCounts();
|
|
70
|
+
for (const t of tasks) {
|
|
71
|
+
if (t.status in counts)
|
|
72
|
+
counts[t.status] += 1;
|
|
73
|
+
}
|
|
74
|
+
const swarms = loadSwarmSummaries(home, workDir);
|
|
75
|
+
return {
|
|
76
|
+
encodedCwd,
|
|
77
|
+
workDir,
|
|
78
|
+
createdAt: readMetaCreatedAt(home, encodedCwd),
|
|
79
|
+
swarmCount: swarms.length,
|
|
80
|
+
taskCount: tasks.length,
|
|
81
|
+
taskCounts: counts,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function loadSwarmSummaries(home, workDir) {
|
|
85
|
+
const swarmsRoot = join(projectRoot(workDir, home), "swarms");
|
|
86
|
+
if (!existsSync(swarmsRoot))
|
|
87
|
+
return [];
|
|
88
|
+
const out = [];
|
|
89
|
+
for (const entry of readdirSync(swarmsRoot, { withFileTypes: true })) {
|
|
90
|
+
if (!entry.isDirectory())
|
|
91
|
+
continue;
|
|
92
|
+
const snap = loadSwarmSnapshot(workDir, entry.name);
|
|
93
|
+
if (!snap)
|
|
94
|
+
continue;
|
|
95
|
+
out.push({
|
|
96
|
+
swarmId: snap.id,
|
|
97
|
+
teamName: snap.teamName,
|
|
98
|
+
status: snap.status ?? "unknown",
|
|
99
|
+
roleCount: snap.roles?.length ?? 0,
|
|
100
|
+
savedAt: snap.savedAt ?? null,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
export function listAllProjectSummaries(home) {
|
|
106
|
+
const h = getHome(home);
|
|
107
|
+
return listProjectDirs(h)
|
|
108
|
+
.map((e) => loadProjectSummary(h, e))
|
|
109
|
+
.filter((s) => !!s);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=projects-fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects-fs.js","sourceRoot":"","sources":["../src/projects-fs.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mclawnet/swarm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"proper-lockfile": "^4.1.2",
|
|
21
21
|
"@mclawnet/logger": "0.1.6",
|
|
22
|
+
"@mclawnet/memory": "0.1.5",
|
|
22
23
|
"@mclawnet/skill-manager": "0.1.4",
|
|
23
|
-
"@mclawnet/
|
|
24
|
-
"@mclawnet/
|
|
25
|
-
"@mclawnet/memory": "0.1.5"
|
|
24
|
+
"@mclawnet/shared": "0.1.4",
|
|
25
|
+
"@mclawnet/task": "0.1.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/better-sqlite3": "^7",
|