@mclawnet/swarm 0.1.12 → 0.1.13
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__/inbox-store-notbefore.test.d.ts +2 -0
- package/dist/__tests__/inbox-store-notbefore.test.d.ts.map +1 -0
- package/dist/__tests__/inbox-store-notbefore.test.js +47 -0
- package/dist/__tests__/inbox-store-notbefore.test.js.map +1 -0
- package/dist/__tests__/inbox-store-swarmid-validation.test.d.ts +2 -0
- package/dist/__tests__/inbox-store-swarmid-validation.test.d.ts.map +1 -0
- package/dist/__tests__/inbox-store-swarmid-validation.test.js +32 -0
- package/dist/__tests__/inbox-store-swarmid-validation.test.js.map +1 -0
- package/dist/__tests__/role-loader-editor.test.js +40 -10
- package/dist/__tests__/role-loader-editor.test.js.map +1 -1
- package/dist/__tests__/template-loader-editor.test.js +28 -13
- package/dist/__tests__/template-loader-editor.test.js.map +1 -1
- package/dist/__tests__/wakeup-end-to-end.test.d.ts +2 -0
- package/dist/__tests__/wakeup-end-to-end.test.d.ts.map +1 -0
- package/dist/__tests__/wakeup-end-to-end.test.js +80 -0
- package/dist/__tests__/wakeup-end-to-end.test.js.map +1 -0
- package/dist/__tests__/wakeup-scheduler-restore-fires.test.d.ts +2 -0
- package/dist/__tests__/wakeup-scheduler-restore-fires.test.d.ts.map +1 -0
- package/dist/__tests__/wakeup-scheduler-restore-fires.test.js +33 -0
- package/dist/__tests__/wakeup-scheduler-restore-fires.test.js.map +1 -0
- package/dist/__tests__/wakeup-scheduler-restore.test.d.ts +2 -0
- package/dist/__tests__/wakeup-scheduler-restore.test.d.ts.map +1 -0
- package/dist/__tests__/wakeup-scheduler-restore.test.js +62 -0
- package/dist/__tests__/wakeup-scheduler-restore.test.js.map +1 -0
- package/dist/__tests__/wakeup-scheduler.test.d.ts +2 -0
- package/dist/__tests__/wakeup-scheduler.test.d.ts.map +1 -0
- package/dist/__tests__/wakeup-scheduler.test.js +65 -0
- package/dist/__tests__/wakeup-scheduler.test.js.map +1 -0
- package/dist/__tests__/watch-manager.test.d.ts +2 -0
- package/dist/__tests__/watch-manager.test.d.ts.map +1 -0
- package/dist/__tests__/watch-manager.test.js +203 -0
- package/dist/__tests__/watch-manager.test.js.map +1 -0
- package/dist/inbox-store.d.ts +7 -0
- package/dist/inbox-store.d.ts.map +1 -1
- package/dist/inbox-store.js +8 -1
- package/dist/inbox-store.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/roles/role-loader.d.ts +30 -5
- package/dist/roles/role-loader.d.ts.map +1 -1
- package/dist/roles/role-loader.js +40 -32
- package/dist/roles/role-loader.js.map +1 -1
- package/dist/templates/template-loader.d.ts +23 -9
- package/dist/templates/template-loader.d.ts.map +1 -1
- package/dist/templates/template-loader.js +33 -26
- package/dist/templates/template-loader.js.map +1 -1
- package/dist/wakeup-scheduler.d.ts +36 -0
- package/dist/wakeup-scheduler.d.ts.map +1 -0
- package/dist/wakeup-scheduler.js +107 -0
- package/dist/wakeup-scheduler.js.map +1 -0
- package/dist/watch-manager.d.ts +32 -0
- package/dist/watch-manager.d.ts.map +1 -0
- package/dist/watch-manager.js +153 -0
- package/dist/watch-manager.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { WakeupScheduler } from "../wakeup-scheduler.js";
|
|
3
|
+
describe("WakeupScheduler", () => {
|
|
4
|
+
it("calls relay.deliver after notBefore elapses", async () => {
|
|
5
|
+
vi.useFakeTimers();
|
|
6
|
+
const deliver = vi.fn().mockResolvedValue(undefined);
|
|
7
|
+
const sched = new WakeupScheduler({ deliver });
|
|
8
|
+
sched.register("s1", "w0", Date.now() + 5_000);
|
|
9
|
+
expect(deliver).not.toHaveBeenCalled();
|
|
10
|
+
await vi.advanceTimersByTimeAsync(5_001);
|
|
11
|
+
expect(deliver).toHaveBeenCalledWith("s1", "w0");
|
|
12
|
+
sched.dispose();
|
|
13
|
+
vi.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
it("dedupes register on identical key — second register does not cause extra delivery", async () => {
|
|
16
|
+
vi.useFakeTimers();
|
|
17
|
+
const deliver = vi.fn().mockResolvedValue(undefined);
|
|
18
|
+
const sched = new WakeupScheduler({ deliver });
|
|
19
|
+
const t = Date.now() + 10_000;
|
|
20
|
+
sched.register("s1", "w0", t);
|
|
21
|
+
sched.register("s1", "w0", t);
|
|
22
|
+
expect(sched.size()).toBe(1);
|
|
23
|
+
await vi.advanceTimersByTimeAsync(10_001);
|
|
24
|
+
expect(deliver).toHaveBeenCalledTimes(1);
|
|
25
|
+
sched.dispose();
|
|
26
|
+
vi.useRealTimers();
|
|
27
|
+
});
|
|
28
|
+
it("swallows deliver errors and continues (best-effort, no unhandled rejection)", async () => {
|
|
29
|
+
vi.useFakeTimers();
|
|
30
|
+
const deliver = vi.fn().mockRejectedValue(new Error("relay boom"));
|
|
31
|
+
const sched = new WakeupScheduler({ deliver });
|
|
32
|
+
sched.register("s1", "w0", Date.now() + 1_000);
|
|
33
|
+
await vi.advanceTimersByTimeAsync(1_001);
|
|
34
|
+
// Yield once more so the .catch handler attached to the rejected promise runs.
|
|
35
|
+
await Promise.resolve();
|
|
36
|
+
expect(deliver).toHaveBeenCalledOnce();
|
|
37
|
+
expect(sched.size()).toBe(0); // timer cleaned up despite rejection
|
|
38
|
+
sched.dispose();
|
|
39
|
+
vi.useRealTimers();
|
|
40
|
+
});
|
|
41
|
+
it("calls deliver immediately if notBefore is in the past", async () => {
|
|
42
|
+
vi.useFakeTimers();
|
|
43
|
+
const deliver = vi.fn().mockResolvedValue(undefined);
|
|
44
|
+
const sched = new WakeupScheduler({ deliver });
|
|
45
|
+
sched.register("s1", "w0", Date.now() - 1);
|
|
46
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
47
|
+
expect(deliver).toHaveBeenCalled();
|
|
48
|
+
sched.dispose();
|
|
49
|
+
vi.useRealTimers();
|
|
50
|
+
});
|
|
51
|
+
it("countForInstance returns active timer count for (s,i)", () => {
|
|
52
|
+
vi.useFakeTimers();
|
|
53
|
+
const sched = new WakeupScheduler({ deliver: vi.fn() });
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
expect(sched.countForInstance("s1", "q")).toBe(0);
|
|
56
|
+
sched.register("s1", "q", now + 1000);
|
|
57
|
+
sched.register("s1", "q", now + 2000);
|
|
58
|
+
sched.register("s1", "other", now + 3000);
|
|
59
|
+
expect(sched.countForInstance("s1", "q")).toBe(2);
|
|
60
|
+
expect(sched.countForInstance("s1", "other")).toBe(1);
|
|
61
|
+
sched.dispose();
|
|
62
|
+
vi.useRealTimers();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=wakeup-scheduler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wakeup-scheduler.test.js","sourceRoot":"","sources":["../../src/__tests__/wakeup-scheduler.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-manager.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/watch-manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { WatchManager } from "../watch-manager.js";
|
|
6
|
+
// Build a fake fs.watch that records listeners and exposes an emit() helper.
|
|
7
|
+
function makeFakeWatch() {
|
|
8
|
+
const listeners = [];
|
|
9
|
+
const closed = [];
|
|
10
|
+
const watchFn = ((_root, _opts, cb) => {
|
|
11
|
+
listeners.push(cb);
|
|
12
|
+
const idx = closed.push(false) - 1;
|
|
13
|
+
return {
|
|
14
|
+
close: () => { closed[idx] = true; },
|
|
15
|
+
on: () => { },
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
watchFn,
|
|
20
|
+
emit: (name) => listeners.forEach(l => l("change", name)),
|
|
21
|
+
closedCount: () => closed.filter(Boolean).length,
|
|
22
|
+
listenerCount: () => listeners.length,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
describe("WatchManager", () => {
|
|
26
|
+
it("triggers onTrigger when a matching file changes (after debounce)", async () => {
|
|
27
|
+
vi.useFakeTimers();
|
|
28
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
29
|
+
const onTrigger = vi.fn();
|
|
30
|
+
const fake = makeFakeWatch();
|
|
31
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
32
|
+
mgr.register({
|
|
33
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
34
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: ["node_modules/**"],
|
|
35
|
+
debounceMs: 200, onTrigger,
|
|
36
|
+
});
|
|
37
|
+
fake.emit("a.ts");
|
|
38
|
+
await vi.advanceTimersByTimeAsync(300);
|
|
39
|
+
expect(onTrigger).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(onTrigger).toHaveBeenCalledWith("a.ts");
|
|
41
|
+
mgr.dispose();
|
|
42
|
+
vi.useRealTimers();
|
|
43
|
+
});
|
|
44
|
+
it("ignores files matching ignoreGlobs", async () => {
|
|
45
|
+
vi.useFakeTimers();
|
|
46
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
47
|
+
mkdirSync(join(root, "node_modules"), { recursive: true });
|
|
48
|
+
const onTrigger = vi.fn();
|
|
49
|
+
const fake = makeFakeWatch();
|
|
50
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
51
|
+
mgr.register({
|
|
52
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
53
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: ["node_modules/**"],
|
|
54
|
+
debounceMs: 100, onTrigger,
|
|
55
|
+
});
|
|
56
|
+
fake.emit("node_modules/x.ts");
|
|
57
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
58
|
+
expect(onTrigger).not.toHaveBeenCalled();
|
|
59
|
+
mgr.dispose();
|
|
60
|
+
vi.useRealTimers();
|
|
61
|
+
});
|
|
62
|
+
it("drops events that don't match includeGlobs", async () => {
|
|
63
|
+
vi.useFakeTimers();
|
|
64
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
65
|
+
const onTrigger = vi.fn();
|
|
66
|
+
const fake = makeFakeWatch();
|
|
67
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
68
|
+
mgr.register({
|
|
69
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
70
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
|
|
71
|
+
debounceMs: 100, onTrigger,
|
|
72
|
+
});
|
|
73
|
+
fake.emit("README.md");
|
|
74
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
75
|
+
expect(onTrigger).not.toHaveBeenCalled();
|
|
76
|
+
mgr.dispose();
|
|
77
|
+
vi.useRealTimers();
|
|
78
|
+
});
|
|
79
|
+
it("debounces bursts into one trigger", async () => {
|
|
80
|
+
vi.useFakeTimers();
|
|
81
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
82
|
+
const onTrigger = vi.fn();
|
|
83
|
+
const fake = makeFakeWatch();
|
|
84
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
85
|
+
mgr.register({
|
|
86
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
87
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
|
|
88
|
+
debounceMs: 200, onTrigger,
|
|
89
|
+
});
|
|
90
|
+
for (let i = 0; i < 5; i++) {
|
|
91
|
+
fake.emit(`f${i}.ts`);
|
|
92
|
+
await vi.advanceTimersByTimeAsync(50);
|
|
93
|
+
}
|
|
94
|
+
await vi.advanceTimersByTimeAsync(300);
|
|
95
|
+
expect(onTrigger).toHaveBeenCalledTimes(1);
|
|
96
|
+
mgr.dispose();
|
|
97
|
+
vi.useRealTimers();
|
|
98
|
+
});
|
|
99
|
+
it("unwatchByInstance clears all watches for that instance", () => {
|
|
100
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
101
|
+
const fake = makeFakeWatch();
|
|
102
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
103
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
104
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w2", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
105
|
+
mgr.register({ swarmId: "s1", instanceId: "other", watchId: "w3", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
106
|
+
expect(mgr.size()).toBe(3);
|
|
107
|
+
mgr.unwatchByInstance("s1", "q");
|
|
108
|
+
expect(mgr.size()).toBe(1);
|
|
109
|
+
expect(fake.closedCount()).toBe(2);
|
|
110
|
+
mgr.dispose();
|
|
111
|
+
expect(fake.closedCount()).toBe(3);
|
|
112
|
+
});
|
|
113
|
+
it("skips register when projectRoot is missing", () => {
|
|
114
|
+
const mgr = new WatchManager();
|
|
115
|
+
mgr.register({
|
|
116
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
117
|
+
projectRoot: "/no/such/dir/xyz123", includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100,
|
|
118
|
+
onTrigger: () => { },
|
|
119
|
+
});
|
|
120
|
+
expect(mgr.size()).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
// M5 hotfix I3: server.ts's unregister wrapper uses countForInstance to
|
|
123
|
+
// prune its (s,i)->workDir registry once the last watch for an instance
|
|
124
|
+
// is gone (countForInstance(s,i) === 0).
|
|
125
|
+
it("countForInstance reflects last-removed transition for prune signalling", () => {
|
|
126
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
127
|
+
const fake = makeFakeWatch();
|
|
128
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
129
|
+
expect(mgr.countForInstance("s1", "q")).toBe(0);
|
|
130
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
131
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w2", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
132
|
+
expect(mgr.countForInstance("s1", "q")).toBeGreaterThan(0);
|
|
133
|
+
// unrelated instance must not bleed
|
|
134
|
+
expect(mgr.countForInstance("s1", "other")).toBe(0);
|
|
135
|
+
mgr.unregister("s1", "q", "w1");
|
|
136
|
+
expect(mgr.countForInstance("s1", "q")).toBe(1); // w2 still there
|
|
137
|
+
mgr.unregister("s1", "q", "w2");
|
|
138
|
+
expect(mgr.countForInstance("s1", "q")).toBe(0);
|
|
139
|
+
mgr.dispose();
|
|
140
|
+
});
|
|
141
|
+
it("countForInstance returns active watch count for (s,i)", () => {
|
|
142
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
143
|
+
const fake = makeFakeWatch();
|
|
144
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
145
|
+
expect(mgr.countForInstance("s1", "q")).toBe(0);
|
|
146
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "a", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
147
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "b", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
148
|
+
mgr.register({ swarmId: "s1", instanceId: "other", watchId: "c", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
149
|
+
expect(mgr.countForInstance("s1", "q")).toBe(2);
|
|
150
|
+
expect(mgr.countForInstance("s1", "other")).toBe(1);
|
|
151
|
+
mgr.dispose();
|
|
152
|
+
});
|
|
153
|
+
it("re-register replaces the existing handle and closes the old watcher", () => {
|
|
154
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
155
|
+
const fake = makeFakeWatch();
|
|
156
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
157
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
158
|
+
mgr.register({ swarmId: "s1", instanceId: "q", watchId: "w1", projectRoot: root, includeGlobs: ["**"], ignoreGlobs: [], debounceMs: 100, onTrigger: () => { } });
|
|
159
|
+
expect(mgr.size()).toBe(1);
|
|
160
|
+
expect(fake.listenerCount()).toBe(2);
|
|
161
|
+
expect(fake.closedCount()).toBe(1);
|
|
162
|
+
mgr.dispose();
|
|
163
|
+
});
|
|
164
|
+
// Race guard: an event arriving after dispose() must not schedule a new timer
|
|
165
|
+
// nor call onTrigger. fs.watch close is best-effort and the listener may still
|
|
166
|
+
// fire briefly after close on some platforms.
|
|
167
|
+
it("ignores events that arrive after dispose (race guard)", async () => {
|
|
168
|
+
vi.useFakeTimers();
|
|
169
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
170
|
+
const onTrigger = vi.fn();
|
|
171
|
+
const fake = makeFakeWatch();
|
|
172
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
173
|
+
mgr.register({
|
|
174
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
175
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
|
|
176
|
+
debounceMs: 100, onTrigger,
|
|
177
|
+
});
|
|
178
|
+
mgr.dispose();
|
|
179
|
+
fake.emit("late.ts");
|
|
180
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
181
|
+
expect(onTrigger).not.toHaveBeenCalled();
|
|
182
|
+
vi.useRealTimers();
|
|
183
|
+
});
|
|
184
|
+
it("ignores timer fire after unregister (race guard, in-flight timer)", async () => {
|
|
185
|
+
vi.useFakeTimers();
|
|
186
|
+
const root = mkdtempSync(join(tmpdir(), "wm-"));
|
|
187
|
+
const onTrigger = vi.fn();
|
|
188
|
+
const fake = makeFakeWatch();
|
|
189
|
+
const mgr = new WatchManager({ watchFn: fake.watchFn });
|
|
190
|
+
mgr.register({
|
|
191
|
+
swarmId: "s1", instanceId: "q", watchId: "w1",
|
|
192
|
+
projectRoot: root, includeGlobs: ["**/*.ts"], ignoreGlobs: [],
|
|
193
|
+
debounceMs: 200, onTrigger,
|
|
194
|
+
});
|
|
195
|
+
fake.emit("a.ts"); // schedules timer
|
|
196
|
+
await vi.advanceTimersByTimeAsync(100); // mid-debounce
|
|
197
|
+
mgr.unregister("s1", "q", "w1"); // clears timer + map entry
|
|
198
|
+
await vi.advanceTimersByTimeAsync(300);
|
|
199
|
+
expect(onTrigger).not.toHaveBeenCalled();
|
|
200
|
+
vi.useRealTimers();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=watch-manager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-manager.test.js","sourceRoot":"","sources":["../../src/__tests__/watch-manager.test.ts"],"names":[],"mappings":"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"}
|
package/dist/inbox-store.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface InboxMessage {
|
|
|
7
7
|
timestamp: number;
|
|
8
8
|
delivered: boolean;
|
|
9
9
|
deliveredAt?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Optional unix-ms timestamp. The message is "scheduled" — readUndelivered
|
|
12
|
+
* filters it out until Date.now() >= notBefore. Used by the wakeup_after
|
|
13
|
+
* MCP tool to let an agent self-defer work without external triggers.
|
|
14
|
+
*/
|
|
15
|
+
notBefore?: number;
|
|
10
16
|
}
|
|
11
17
|
export declare class InboxStore {
|
|
12
18
|
private workDir;
|
|
@@ -15,6 +21,7 @@ export declare class InboxStore {
|
|
|
15
21
|
constructor(workDir: string, swarmId: string, homeOverride?: string | undefined);
|
|
16
22
|
private home;
|
|
17
23
|
private inboxesDir;
|
|
24
|
+
private validateSwarmId;
|
|
18
25
|
private validateInstanceId;
|
|
19
26
|
inboxFile(instanceId: string): string;
|
|
20
27
|
readAll(instanceId: string): Promise<InboxMessage[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbox-store.d.ts","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"inbox-store.d.ts","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;IAQnB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,YAAY,CAAC;gBAFb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,MAAM,YAAA;IAG/B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,kBAAkB;IAM1B,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAK/B,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAUpD,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO5D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBtE"}
|
package/dist/inbox-store.js
CHANGED
|
@@ -26,8 +26,14 @@ export class InboxStore {
|
|
|
26
26
|
return this.homeOverride ?? process.env.CLAWNET_HOME ?? homedir();
|
|
27
27
|
}
|
|
28
28
|
inboxesDir() {
|
|
29
|
+
this.validateSwarmId(this.swarmId);
|
|
29
30
|
return join(projectRoot(this.workDir, this.home()), "swarms", this.swarmId, "inboxes");
|
|
30
31
|
}
|
|
32
|
+
validateSwarmId(id) {
|
|
33
|
+
if (!id || /[\\/]|\.\./.test(id) || id.includes("\0")) {
|
|
34
|
+
throw new Error(`InboxStore: invalid swarmId: ${JSON.stringify(id)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
31
37
|
validateInstanceId(id) {
|
|
32
38
|
if (!id || /[\\/]|\.\./.test(id) || id.includes("\0")) {
|
|
33
39
|
throw new Error(`InboxStore: invalid instanceId: ${JSON.stringify(id)}`);
|
|
@@ -66,7 +72,8 @@ export class InboxStore {
|
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
async readUndelivered(instanceId) {
|
|
69
|
-
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
return (await this.readAll(instanceId)).filter(m => !m.delivered && (m.notBefore === undefined || m.notBefore <= now));
|
|
70
77
|
}
|
|
71
78
|
async markDelivered(instanceId, ids) {
|
|
72
79
|
if (ids.length === 0)
|
package/dist/inbox-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbox-store.js","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,QAAQ,MAAM,iBAAiB,CAAC;AAEvC,MAAM,SAAS,GAAG;IAChB,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE;CACjD,CAAC;
|
|
1
|
+
{"version":3,"file":"inbox-store.js","sourceRoot":"","sources":["../src/inbox-store.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,9 +11,11 @@ export type { InboxMessage } from "./inbox-store.js";
|
|
|
11
11
|
export { pickStrongestStatus } from "./plan-sync.js";
|
|
12
12
|
export { InboxRelay } from "./inbox-relay.js";
|
|
13
13
|
export { InboxWatcher, type InboxRelayLike as InboxWatcherRelayLike } from "./inbox-watcher.js";
|
|
14
|
+
export { WakeupScheduler, type WakeupRelayLike } from "./wakeup-scheduler.js";
|
|
15
|
+
export { WatchManager, type WatchRegistration, type WatchManagerOptions } from "./watch-manager.js";
|
|
14
16
|
export type { SessionAdapter, HubAdapter, SwarmInstance, RoleInstance, SwarmAction, SwarmMessage, SwarmCreateOptions, RouteEndpoint, SwarmStatus, PlanStatus, } from "./types.js";
|
|
15
17
|
export type { RoleDefinition, DelegationMap, DelegationEntry } from "./roles/types.js";
|
|
16
|
-
export {
|
|
18
|
+
export { listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
|
|
17
19
|
export type { TemplateDefinition, TemplateRoleSpec } from "./templates/types.js";
|
|
18
20
|
export type { TemplateSource, TemplateListItem } from "./templates/template-loader.js";
|
|
19
21
|
export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
|
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,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACvE,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,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,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,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACvE,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,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,IAAI,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEpG,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,YAAY,EAClC,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjF,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvF,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,7 +8,9 @@ export { InboxStore } from "./inbox-store.js";
|
|
|
8
8
|
export { pickStrongestStatus } from "./plan-sync.js";
|
|
9
9
|
export { InboxRelay } from "./inbox-relay.js";
|
|
10
10
|
export { InboxWatcher } from "./inbox-watcher.js";
|
|
11
|
-
export {
|
|
11
|
+
export { WakeupScheduler } from "./wakeup-scheduler.js";
|
|
12
|
+
export { WatchManager } from "./watch-manager.js";
|
|
13
|
+
export { listTemplates, loadTemplate, listTemplatesWithSource, getTemplateSource, getTemplateRaw, saveTemplate, deleteTemplate, } from "./templates/template-loader.js";
|
|
12
14
|
export { listProjectDirs, resolveWorkDir, loadProjectSummary, loadSwarmSummaries, listAllProjectSummaries, } from "./projects-fs.js";
|
|
13
15
|
export { PROJECT_FILE_WHITELIST, isWhitelistedRelPath, listProjectFiles, readProjectFile, writeProjectFile, ProjectFilesError, } from "./project-files.js";
|
|
14
16
|
//# 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,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAEhC,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,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,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,EACrE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,GACrE,MAAM,wBAAwB,CAAC;AAEhC,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,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAgD,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,eAAe,EAAwB,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAoD,MAAM,oBAAoB,CAAC;AAiBpG,OAAO,EAAE,aAAa,EAAE,YAAY,EAClC,uBAAuB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,GACzF,MAAM,gCAAgC,CAAC;AAIxC,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"}
|
|
@@ -10,25 +10,50 @@ import type { RoleDefinition } from "./types.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function parseRoleFile(content: string): RoleDefinition;
|
|
12
12
|
/**
|
|
13
|
-
* Ensure user role directory exists
|
|
14
|
-
*
|
|
13
|
+
* Ensure user role directory exists.
|
|
14
|
+
*
|
|
15
|
+
* Presets live inside the npm package (read-only). User-authored roles live in
|
|
16
|
+
* `~/.clawnet/roles/<name>/CLAUDE.md`. We deliberately do NOT seed presets into
|
|
17
|
+
* the user dir: a preset and a user role with the same name is forbidden at
|
|
18
|
+
* save time, so the union over the two dirs is conflict-free.
|
|
15
19
|
*/
|
|
16
20
|
export declare function initRoles(): string;
|
|
17
21
|
/** Load a role definition by name. Reads from ~/.clawnet/roles/{name}/CLAUDE.md first, falls back to preset. */
|
|
18
22
|
export declare function loadRole(roleName: string): RoleDefinition;
|
|
19
23
|
/** List all available role names (from user dir + presets). */
|
|
20
24
|
export declare function listRoles(): string[];
|
|
21
|
-
export type RoleSource = "preset" | "user"
|
|
25
|
+
export type RoleSource = "preset" | "user";
|
|
22
26
|
export interface RoleListItem {
|
|
23
27
|
name: string;
|
|
24
28
|
source: RoleSource;
|
|
29
|
+
/**
|
|
30
|
+
* True when a user-authored role with this name hides a built-in preset of
|
|
31
|
+
* the same name (legacy state — saveRole now refuses such writes). Lets the
|
|
32
|
+
* UI warn the user they're masking a built-in.
|
|
33
|
+
*/
|
|
34
|
+
shadowsPreset?: boolean;
|
|
25
35
|
}
|
|
26
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* List all roles with source tag.
|
|
38
|
+
*
|
|
39
|
+
* Presets live in the npm package; user roles in `~/.clawnet/roles/`. Names
|
|
40
|
+
* must be globally unique going forward (enforced at save time). If a stale
|
|
41
|
+
* user dir collides with a preset (pre-refactor seeded copy, manual file),
|
|
42
|
+
* the user copy wins and the entry is flagged with `shadowsPreset` so the UI
|
|
43
|
+
* can surface the conflict.
|
|
44
|
+
*/
|
|
27
45
|
export declare function listRolesWithSource(): RoleListItem[];
|
|
28
46
|
export declare function getRoleSource(name: string): RoleSource | null;
|
|
29
47
|
/** Get raw markdown content. User file wins over preset. */
|
|
30
48
|
export declare function getRoleRaw(name: string): string | null;
|
|
31
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Save a user role.
|
|
51
|
+
*
|
|
52
|
+
* Validates by parsing first; throws on bad frontmatter or name mismatch.
|
|
53
|
+
* Refuses to write if the requested name collides with a built-in preset —
|
|
54
|
+
* presets are immutable, so "edit a preset" is implemented by Copy with a
|
|
55
|
+
* new name.
|
|
56
|
+
*/
|
|
32
57
|
export declare function saveRole(name: string, raw: string): RoleSource;
|
|
33
58
|
/**
|
|
34
59
|
* Delete a user role directory. Preset is never touched.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-loader.d.ts","sourceRoot":"","sources":["../../src/roles/role-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,YAAY,CAAC;AAiCjF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAyJ7D;AAgBD
|
|
1
|
+
{"version":3,"file":"role-loader.d.ts","sourceRoot":"","sources":["../../src/roles/role-loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkC,MAAM,YAAY,CAAC;AAiCjF;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAyJ7D;AAgBD;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAazD;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,IAAI,MAAM,EAAE,CAqBpC;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,EAAE,CA8BpD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAK7D;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOtD;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAoB9D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,CAWtE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,uqKA2CnC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,cAAc,EAC1B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,YAAY,GAC1B,MAAM,CA8CR"}
|
|
@@ -195,26 +195,16 @@ function pushDelegationEntry(map, key, entry) {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
* Ensure user role directory exists
|
|
199
|
-
*
|
|
198
|
+
* Ensure user role directory exists.
|
|
199
|
+
*
|
|
200
|
+
* Presets live inside the npm package (read-only). User-authored roles live in
|
|
201
|
+
* `~/.clawnet/roles/<name>/CLAUDE.md`. We deliberately do NOT seed presets into
|
|
202
|
+
* the user dir: a preset and a user role with the same name is forbidden at
|
|
203
|
+
* save time, so the union over the two dirs is conflict-free.
|
|
200
204
|
*/
|
|
201
205
|
export function initRoles() {
|
|
202
206
|
const USER_ROLES_DIR = userRolesDir();
|
|
203
207
|
mkdirSync(USER_ROLES_DIR, { recursive: true });
|
|
204
|
-
if (!existsSync(PRESET_DIR))
|
|
205
|
-
return USER_ROLES_DIR;
|
|
206
|
-
for (const file of readdirSync(PRESET_DIR)) {
|
|
207
|
-
if (!file.endsWith(".md"))
|
|
208
|
-
continue;
|
|
209
|
-
const roleName = file.replace(/\.md$/, "");
|
|
210
|
-
const userRoleDir = join(USER_ROLES_DIR, roleName);
|
|
211
|
-
const userFile = join(userRoleDir, "CLAUDE.md");
|
|
212
|
-
if (!existsSync(userFile)) {
|
|
213
|
-
mkdirSync(userRoleDir, { recursive: true });
|
|
214
|
-
const content = readFileSync(join(PRESET_DIR, file), "utf-8");
|
|
215
|
-
writeFileSync(userFile, content, "utf-8");
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
208
|
return USER_ROLES_DIR;
|
|
219
209
|
}
|
|
220
210
|
/** Load a role definition by name. Reads from ~/.clawnet/roles/{name}/CLAUDE.md first, falls back to preset. */
|
|
@@ -250,7 +240,15 @@ export function listRoles() {
|
|
|
250
240
|
}
|
|
251
241
|
return [...roles].sort();
|
|
252
242
|
}
|
|
253
|
-
/**
|
|
243
|
+
/**
|
|
244
|
+
* List all roles with source tag.
|
|
245
|
+
*
|
|
246
|
+
* Presets live in the npm package; user roles in `~/.clawnet/roles/`. Names
|
|
247
|
+
* must be globally unique going forward (enforced at save time). If a stale
|
|
248
|
+
* user dir collides with a preset (pre-refactor seeded copy, manual file),
|
|
249
|
+
* the user copy wins and the entry is flagged with `shadowsPreset` so the UI
|
|
250
|
+
* can surface the conflict.
|
|
251
|
+
*/
|
|
254
252
|
export function listRolesWithSource() {
|
|
255
253
|
const presetNames = existsSync(PRESET_DIR)
|
|
256
254
|
? readdirSync(PRESET_DIR)
|
|
@@ -266,25 +264,25 @@ export function listRolesWithSource() {
|
|
|
266
264
|
}
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
|
-
const presetSet = new Set(presetNames);
|
|
270
267
|
const userSet = new Set(userNames);
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
268
|
+
const presetSet = new Set(presetNames);
|
|
269
|
+
const items = [];
|
|
270
|
+
for (const name of userNames) {
|
|
271
|
+
items.push(presetSet.has(name)
|
|
272
|
+
? { name, source: "user", shadowsPreset: true }
|
|
273
|
+
: { name, source: "user" });
|
|
274
|
+
}
|
|
275
|
+
for (const name of presetNames) {
|
|
276
|
+
if (!userSet.has(name))
|
|
277
|
+
items.push({ name, source: "preset" });
|
|
278
|
+
}
|
|
279
|
+
return items.sort((a, b) => a.name.localeCompare(b.name));
|
|
278
280
|
}
|
|
279
281
|
export function getRoleSource(name) {
|
|
280
282
|
validateName(name);
|
|
281
|
-
|
|
282
|
-
const inPreset = existsSync(join(PRESET_DIR, `${name}.md`));
|
|
283
|
-
if (inUser && inPreset)
|
|
284
|
-
return "override";
|
|
285
|
-
if (inUser)
|
|
283
|
+
if (existsSync(join(userRolesDir(), name, "CLAUDE.md")))
|
|
286
284
|
return "user";
|
|
287
|
-
if (
|
|
285
|
+
if (existsSync(join(PRESET_DIR, `${name}.md`)))
|
|
288
286
|
return "preset";
|
|
289
287
|
return null;
|
|
290
288
|
}
|
|
@@ -299,9 +297,19 @@ export function getRoleRaw(name) {
|
|
|
299
297
|
return readFileSync(presetFile, "utf-8");
|
|
300
298
|
return null;
|
|
301
299
|
}
|
|
302
|
-
/**
|
|
300
|
+
/**
|
|
301
|
+
* Save a user role.
|
|
302
|
+
*
|
|
303
|
+
* Validates by parsing first; throws on bad frontmatter or name mismatch.
|
|
304
|
+
* Refuses to write if the requested name collides with a built-in preset —
|
|
305
|
+
* presets are immutable, so "edit a preset" is implemented by Copy with a
|
|
306
|
+
* new name.
|
|
307
|
+
*/
|
|
303
308
|
export function saveRole(name, raw) {
|
|
304
309
|
validateName(name);
|
|
310
|
+
if (existsSync(join(PRESET_DIR, `${name}.md`))) {
|
|
311
|
+
throw new Error(`Cannot save role '${name}': name conflicts with a built-in preset. Choose another name.`);
|
|
312
|
+
}
|
|
305
313
|
const parsed = parseRoleFile(raw);
|
|
306
314
|
if (parsed.name && parsed.name !== name && parsed.name !== "unknown") {
|
|
307
315
|
throw new Error(`Frontmatter name '${parsed.name}' does not match requested '${name}'`);
|