multiclaws 0.4.41 → 0.4.43
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 +2 -0
- package/dist/gateway/handlers.d.ts +4 -4
- package/dist/gateway/handlers.js +239 -239
- package/dist/index.d.ts +8 -8
- package/dist/index.js +710 -710
- package/dist/infra/frp.d.ts +55 -55
- package/dist/infra/frp.js +398 -398
- package/dist/infra/gateway-client.d.ts +27 -27
- package/dist/infra/gateway-client.js +136 -136
- package/dist/infra/json-store.d.ts +4 -4
- package/dist/infra/json-store.js +57 -57
- package/dist/infra/logger.d.ts +14 -14
- package/dist/infra/logger.js +25 -25
- package/dist/infra/rate-limiter.d.ts +19 -19
- package/dist/infra/rate-limiter.js +69 -69
- package/dist/infra/tailscale.d.ts +19 -19
- package/dist/infra/tailscale.js +120 -120
- package/dist/infra/telemetry.d.ts +3 -3
- package/dist/infra/telemetry.js +17 -17
- package/dist/infra/version.d.ts +1 -1
- package/dist/infra/version.js +19 -19
- package/dist/service/a2a-adapter.d.ts +80 -80
- package/dist/service/a2a-adapter.js +505 -505
- package/dist/service/agent-profile.d.ts +17 -17
- package/dist/service/agent-profile.js +58 -58
- package/dist/service/agent-registry.d.ts +29 -29
- package/dist/service/agent-registry.js +131 -131
- package/dist/service/multiclaws-service.d.ts +150 -150
- package/dist/service/multiclaws-service.js +1137 -1137
- package/dist/service/session-store.d.ts +46 -46
- package/dist/service/session-store.js +143 -143
- package/dist/task/tracker.d.ts +46 -46
- package/dist/task/tracker.js +191 -191
- package/dist/team/team-store.d.ts +42 -42
- package/dist/team/team-store.js +195 -195
- package/dist/types/openclaw.d.ts +109 -109
- package/dist/types/openclaw.js +2 -2
- package/package.json +1 -1
- package/skills/meeting-scheduler/SKILL.md +112 -105
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import type { BasicLogger } from "../infra/logger";
|
|
2
|
-
export type SessionStatus = "active" | "input-required" | "completed" | "failed" | "canceled";
|
|
3
|
-
export type SessionMessage = {
|
|
4
|
-
role: "user" | "agent";
|
|
5
|
-
content: string;
|
|
6
|
-
timestampMs: number;
|
|
7
|
-
taskId?: string;
|
|
8
|
-
};
|
|
9
|
-
export type ConversationSession = {
|
|
10
|
-
sessionId: string;
|
|
11
|
-
agentUrl: string;
|
|
12
|
-
agentName: string;
|
|
13
|
-
contextId: string;
|
|
14
|
-
currentTaskId?: string;
|
|
15
|
-
status: SessionStatus;
|
|
16
|
-
messages: SessionMessage[];
|
|
17
|
-
createdAtMs: number;
|
|
18
|
-
updatedAtMs: number;
|
|
19
|
-
error?: string;
|
|
20
|
-
};
|
|
21
|
-
export declare class SessionStore {
|
|
22
|
-
private readonly filePath;
|
|
23
|
-
private readonly ttlMs;
|
|
24
|
-
private readonly logger?;
|
|
25
|
-
private store;
|
|
26
|
-
private persistPending;
|
|
27
|
-
constructor(opts: {
|
|
28
|
-
filePath: string;
|
|
29
|
-
ttlMs?: number;
|
|
30
|
-
logger?: BasicLogger;
|
|
31
|
-
});
|
|
32
|
-
create(params: {
|
|
33
|
-
agentUrl: string;
|
|
34
|
-
agentName: string;
|
|
35
|
-
contextId: string;
|
|
36
|
-
}): ConversationSession;
|
|
37
|
-
get(sessionId: string): ConversationSession | null;
|
|
38
|
-
list(): ConversationSession[];
|
|
39
|
-
update(sessionId: string, patch: Partial<Omit<ConversationSession, "sessionId" | "createdAtMs">>): ConversationSession | null;
|
|
40
|
-
appendMessage(sessionId: string, msg: SessionMessage): ConversationSession | null;
|
|
41
|
-
private loadSync;
|
|
42
|
-
private schedulePersist;
|
|
43
|
-
private persistAsync;
|
|
44
|
-
private prune;
|
|
45
|
-
private evictOldest;
|
|
46
|
-
}
|
|
1
|
+
import type { BasicLogger } from "../infra/logger";
|
|
2
|
+
export type SessionStatus = "active" | "input-required" | "completed" | "failed" | "canceled";
|
|
3
|
+
export type SessionMessage = {
|
|
4
|
+
role: "user" | "agent";
|
|
5
|
+
content: string;
|
|
6
|
+
timestampMs: number;
|
|
7
|
+
taskId?: string;
|
|
8
|
+
};
|
|
9
|
+
export type ConversationSession = {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
agentUrl: string;
|
|
12
|
+
agentName: string;
|
|
13
|
+
contextId: string;
|
|
14
|
+
currentTaskId?: string;
|
|
15
|
+
status: SessionStatus;
|
|
16
|
+
messages: SessionMessage[];
|
|
17
|
+
createdAtMs: number;
|
|
18
|
+
updatedAtMs: number;
|
|
19
|
+
error?: string;
|
|
20
|
+
};
|
|
21
|
+
export declare class SessionStore {
|
|
22
|
+
private readonly filePath;
|
|
23
|
+
private readonly ttlMs;
|
|
24
|
+
private readonly logger?;
|
|
25
|
+
private store;
|
|
26
|
+
private persistPending;
|
|
27
|
+
constructor(opts: {
|
|
28
|
+
filePath: string;
|
|
29
|
+
ttlMs?: number;
|
|
30
|
+
logger?: BasicLogger;
|
|
31
|
+
});
|
|
32
|
+
create(params: {
|
|
33
|
+
agentUrl: string;
|
|
34
|
+
agentName: string;
|
|
35
|
+
contextId: string;
|
|
36
|
+
}): ConversationSession;
|
|
37
|
+
get(sessionId: string): ConversationSession | null;
|
|
38
|
+
list(): ConversationSession[];
|
|
39
|
+
update(sessionId: string, patch: Partial<Omit<ConversationSession, "sessionId" | "createdAtMs">>): ConversationSession | null;
|
|
40
|
+
appendMessage(sessionId: string, msg: SessionMessage): ConversationSession | null;
|
|
41
|
+
private loadSync;
|
|
42
|
+
private schedulePersist;
|
|
43
|
+
private persistAsync;
|
|
44
|
+
private prune;
|
|
45
|
+
private evictOldest;
|
|
46
|
+
}
|
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.SessionStore = void 0;
|
|
7
|
-
const node_crypto_1 = require("node:crypto");
|
|
8
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
-
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
-
const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
12
|
-
const MAX_SESSIONS = 1_000;
|
|
13
|
-
const MAX_MESSAGES_PER_SESSION = 200;
|
|
14
|
-
function emptyStore() {
|
|
15
|
-
return { version: 1, sessions: [] };
|
|
16
|
-
}
|
|
17
|
-
function normalizeStore(raw) {
|
|
18
|
-
if (raw.version !== 1 || !Array.isArray(raw.sessions)) {
|
|
19
|
-
return emptyStore();
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
version: 1,
|
|
23
|
-
sessions: raw.sessions.filter((s) => s &&
|
|
24
|
-
typeof s.sessionId === "string" &&
|
|
25
|
-
typeof s.agentUrl === "string" &&
|
|
26
|
-
typeof s.status === "string" &&
|
|
27
|
-
typeof s.createdAtMs === "number" &&
|
|
28
|
-
Array.isArray(s.messages)),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
class SessionStore {
|
|
32
|
-
filePath;
|
|
33
|
-
ttlMs;
|
|
34
|
-
logger;
|
|
35
|
-
store;
|
|
36
|
-
persistPending = false;
|
|
37
|
-
constructor(opts) {
|
|
38
|
-
this.filePath = opts.filePath;
|
|
39
|
-
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
40
|
-
this.logger = opts.logger;
|
|
41
|
-
this.store = this.loadSync();
|
|
42
|
-
}
|
|
43
|
-
create(params) {
|
|
44
|
-
this.prune();
|
|
45
|
-
const now = Date.now();
|
|
46
|
-
const session = {
|
|
47
|
-
sessionId: (0, node_crypto_1.randomUUID)(),
|
|
48
|
-
agentUrl: params.agentUrl,
|
|
49
|
-
agentName: params.agentName,
|
|
50
|
-
contextId: params.contextId,
|
|
51
|
-
status: "active",
|
|
52
|
-
messages: [],
|
|
53
|
-
createdAtMs: now,
|
|
54
|
-
updatedAtMs: now,
|
|
55
|
-
};
|
|
56
|
-
if (this.store.sessions.length >= MAX_SESSIONS) {
|
|
57
|
-
this.evictOldest();
|
|
58
|
-
}
|
|
59
|
-
this.store.sessions.push(session);
|
|
60
|
-
this.schedulePersist();
|
|
61
|
-
return session;
|
|
62
|
-
}
|
|
63
|
-
get(sessionId) {
|
|
64
|
-
return this.store.sessions.find((s) => s.sessionId === sessionId) ?? null;
|
|
65
|
-
}
|
|
66
|
-
list() {
|
|
67
|
-
return [...this.store.sessions].sort((a, b) => b.updatedAtMs - a.updatedAtMs);
|
|
68
|
-
}
|
|
69
|
-
update(sessionId, patch) {
|
|
70
|
-
const idx = this.store.sessions.findIndex((s) => s.sessionId === sessionId);
|
|
71
|
-
if (idx < 0)
|
|
72
|
-
return null;
|
|
73
|
-
const next = {
|
|
74
|
-
...this.store.sessions[idx],
|
|
75
|
-
...patch,
|
|
76
|
-
updatedAtMs: Date.now(),
|
|
77
|
-
};
|
|
78
|
-
this.store.sessions[idx] = next;
|
|
79
|
-
this.schedulePersist();
|
|
80
|
-
return next;
|
|
81
|
-
}
|
|
82
|
-
appendMessage(sessionId, msg) {
|
|
83
|
-
const session = this.get(sessionId);
|
|
84
|
-
if (!session)
|
|
85
|
-
return null;
|
|
86
|
-
let messages = [...session.messages, msg];
|
|
87
|
-
// Truncate old messages, keeping the most recent ones
|
|
88
|
-
if (messages.length > MAX_MESSAGES_PER_SESSION) {
|
|
89
|
-
messages = messages.slice(-MAX_MESSAGES_PER_SESSION);
|
|
90
|
-
}
|
|
91
|
-
return this.update(sessionId, { messages });
|
|
92
|
-
}
|
|
93
|
-
loadSync() {
|
|
94
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(this.filePath), { recursive: true });
|
|
95
|
-
try {
|
|
96
|
-
const raw = JSON.parse(node_fs_1.default.readFileSync(this.filePath, "utf8"));
|
|
97
|
-
return normalizeStore(raw);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
const store = emptyStore();
|
|
101
|
-
node_fs_1.default.writeFileSync(this.filePath, JSON.stringify(store, null, 2), "utf8");
|
|
102
|
-
return store;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
schedulePersist() {
|
|
106
|
-
if (this.persistPending)
|
|
107
|
-
return;
|
|
108
|
-
this.persistPending = true;
|
|
109
|
-
queueMicrotask(() => {
|
|
110
|
-
this.persistPending = false;
|
|
111
|
-
void this.persistAsync();
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
async persistAsync() {
|
|
115
|
-
try {
|
|
116
|
-
await promises_1.default.mkdir(node_path_1.default.dirname(this.filePath), { recursive: true });
|
|
117
|
-
const tmp = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
118
|
-
await promises_1.default.writeFile(tmp, JSON.stringify(this.store, null, 2), "utf8");
|
|
119
|
-
await promises_1.default.rename(tmp, this.filePath);
|
|
120
|
-
}
|
|
121
|
-
catch (err) {
|
|
122
|
-
// best-effort
|
|
123
|
-
this.logger?.warn?.(`[session-store] persistAsync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
prune() {
|
|
127
|
-
const cutoff = Date.now() - this.ttlMs;
|
|
128
|
-
this.store.sessions = this.store.sessions.filter((s) => {
|
|
129
|
-
if (s.updatedAtMs >= cutoff)
|
|
130
|
-
return true;
|
|
131
|
-
return s.status !== "completed" && s.status !== "failed" && s.status !== "canceled";
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
evictOldest() {
|
|
135
|
-
const removable = [...this.store.sessions]
|
|
136
|
-
.filter((s) => s.status === "completed" || s.status === "failed" || s.status === "canceled")
|
|
137
|
-
.sort((a, b) => a.updatedAtMs - b.updatedAtMs)
|
|
138
|
-
.slice(0, Math.max(1, Math.floor(MAX_SESSIONS / 4)));
|
|
139
|
-
const ids = new Set(removable.map((s) => s.sessionId));
|
|
140
|
-
this.store.sessions = this.store.sessions.filter((s) => !ids.has(s.sessionId));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
exports.SessionStore = SessionStore;
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SessionStore = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
12
|
+
const MAX_SESSIONS = 1_000;
|
|
13
|
+
const MAX_MESSAGES_PER_SESSION = 200;
|
|
14
|
+
function emptyStore() {
|
|
15
|
+
return { version: 1, sessions: [] };
|
|
16
|
+
}
|
|
17
|
+
function normalizeStore(raw) {
|
|
18
|
+
if (raw.version !== 1 || !Array.isArray(raw.sessions)) {
|
|
19
|
+
return emptyStore();
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
sessions: raw.sessions.filter((s) => s &&
|
|
24
|
+
typeof s.sessionId === "string" &&
|
|
25
|
+
typeof s.agentUrl === "string" &&
|
|
26
|
+
typeof s.status === "string" &&
|
|
27
|
+
typeof s.createdAtMs === "number" &&
|
|
28
|
+
Array.isArray(s.messages)),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
class SessionStore {
|
|
32
|
+
filePath;
|
|
33
|
+
ttlMs;
|
|
34
|
+
logger;
|
|
35
|
+
store;
|
|
36
|
+
persistPending = false;
|
|
37
|
+
constructor(opts) {
|
|
38
|
+
this.filePath = opts.filePath;
|
|
39
|
+
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
40
|
+
this.logger = opts.logger;
|
|
41
|
+
this.store = this.loadSync();
|
|
42
|
+
}
|
|
43
|
+
create(params) {
|
|
44
|
+
this.prune();
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const session = {
|
|
47
|
+
sessionId: (0, node_crypto_1.randomUUID)(),
|
|
48
|
+
agentUrl: params.agentUrl,
|
|
49
|
+
agentName: params.agentName,
|
|
50
|
+
contextId: params.contextId,
|
|
51
|
+
status: "active",
|
|
52
|
+
messages: [],
|
|
53
|
+
createdAtMs: now,
|
|
54
|
+
updatedAtMs: now,
|
|
55
|
+
};
|
|
56
|
+
if (this.store.sessions.length >= MAX_SESSIONS) {
|
|
57
|
+
this.evictOldest();
|
|
58
|
+
}
|
|
59
|
+
this.store.sessions.push(session);
|
|
60
|
+
this.schedulePersist();
|
|
61
|
+
return session;
|
|
62
|
+
}
|
|
63
|
+
get(sessionId) {
|
|
64
|
+
return this.store.sessions.find((s) => s.sessionId === sessionId) ?? null;
|
|
65
|
+
}
|
|
66
|
+
list() {
|
|
67
|
+
return [...this.store.sessions].sort((a, b) => b.updatedAtMs - a.updatedAtMs);
|
|
68
|
+
}
|
|
69
|
+
update(sessionId, patch) {
|
|
70
|
+
const idx = this.store.sessions.findIndex((s) => s.sessionId === sessionId);
|
|
71
|
+
if (idx < 0)
|
|
72
|
+
return null;
|
|
73
|
+
const next = {
|
|
74
|
+
...this.store.sessions[idx],
|
|
75
|
+
...patch,
|
|
76
|
+
updatedAtMs: Date.now(),
|
|
77
|
+
};
|
|
78
|
+
this.store.sessions[idx] = next;
|
|
79
|
+
this.schedulePersist();
|
|
80
|
+
return next;
|
|
81
|
+
}
|
|
82
|
+
appendMessage(sessionId, msg) {
|
|
83
|
+
const session = this.get(sessionId);
|
|
84
|
+
if (!session)
|
|
85
|
+
return null;
|
|
86
|
+
let messages = [...session.messages, msg];
|
|
87
|
+
// Truncate old messages, keeping the most recent ones
|
|
88
|
+
if (messages.length > MAX_MESSAGES_PER_SESSION) {
|
|
89
|
+
messages = messages.slice(-MAX_MESSAGES_PER_SESSION);
|
|
90
|
+
}
|
|
91
|
+
return this.update(sessionId, { messages });
|
|
92
|
+
}
|
|
93
|
+
loadSync() {
|
|
94
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(this.filePath), { recursive: true });
|
|
95
|
+
try {
|
|
96
|
+
const raw = JSON.parse(node_fs_1.default.readFileSync(this.filePath, "utf8"));
|
|
97
|
+
return normalizeStore(raw);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
const store = emptyStore();
|
|
101
|
+
node_fs_1.default.writeFileSync(this.filePath, JSON.stringify(store, null, 2), "utf8");
|
|
102
|
+
return store;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
schedulePersist() {
|
|
106
|
+
if (this.persistPending)
|
|
107
|
+
return;
|
|
108
|
+
this.persistPending = true;
|
|
109
|
+
queueMicrotask(() => {
|
|
110
|
+
this.persistPending = false;
|
|
111
|
+
void this.persistAsync();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async persistAsync() {
|
|
115
|
+
try {
|
|
116
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(this.filePath), { recursive: true });
|
|
117
|
+
const tmp = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
118
|
+
await promises_1.default.writeFile(tmp, JSON.stringify(this.store, null, 2), "utf8");
|
|
119
|
+
await promises_1.default.rename(tmp, this.filePath);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
// best-effort
|
|
123
|
+
this.logger?.warn?.(`[session-store] persistAsync failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
prune() {
|
|
127
|
+
const cutoff = Date.now() - this.ttlMs;
|
|
128
|
+
this.store.sessions = this.store.sessions.filter((s) => {
|
|
129
|
+
if (s.updatedAtMs >= cutoff)
|
|
130
|
+
return true;
|
|
131
|
+
return s.status !== "completed" && s.status !== "failed" && s.status !== "canceled";
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
evictOldest() {
|
|
135
|
+
const removable = [...this.store.sessions]
|
|
136
|
+
.filter((s) => s.status === "completed" || s.status === "failed" || s.status === "canceled")
|
|
137
|
+
.sort((a, b) => a.updatedAtMs - b.updatedAtMs)
|
|
138
|
+
.slice(0, Math.max(1, Math.floor(MAX_SESSIONS / 4)));
|
|
139
|
+
const ids = new Set(removable.map((s) => s.sessionId));
|
|
140
|
+
this.store.sessions = this.store.sessions.filter((s) => !ids.has(s.sessionId));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.SessionStore = SessionStore;
|
package/dist/task/tracker.d.ts
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import type { BasicLogger } from "../infra/logger";
|
|
2
|
-
export type TaskStatus = "queued" | "running" | "completed" | "failed";
|
|
3
|
-
export type TaskRecord = {
|
|
4
|
-
taskId: string;
|
|
5
|
-
fromPeerId: string;
|
|
6
|
-
toPeerId: string;
|
|
7
|
-
task: string;
|
|
8
|
-
context?: string;
|
|
9
|
-
status: TaskStatus;
|
|
10
|
-
createdAtMs: number;
|
|
11
|
-
updatedAtMs: number;
|
|
12
|
-
result?: string;
|
|
13
|
-
error?: string;
|
|
14
|
-
};
|
|
15
|
-
export declare class TaskTracker {
|
|
16
|
-
private readonly filePath;
|
|
17
|
-
private readonly ttlMs;
|
|
18
|
-
private readonly maxTasks;
|
|
19
|
-
private readonly store;
|
|
20
|
-
private readonly logger?;
|
|
21
|
-
private pruneTimer;
|
|
22
|
-
private persistPending;
|
|
23
|
-
constructor(opts?: {
|
|
24
|
-
ttlMs?: number;
|
|
25
|
-
maxTasks?: number;
|
|
26
|
-
filePath?: string;
|
|
27
|
-
logger?: BasicLogger;
|
|
28
|
-
});
|
|
29
|
-
create(params: {
|
|
30
|
-
fromPeerId: string;
|
|
31
|
-
toPeerId: string;
|
|
32
|
-
task: string;
|
|
33
|
-
context?: string;
|
|
34
|
-
}): TaskRecord;
|
|
35
|
-
update(taskId: string, patch: Partial<Omit<TaskRecord, "taskId" | "createdAtMs">>): TaskRecord | null;
|
|
36
|
-
get(taskId: string): TaskRecord | null;
|
|
37
|
-
list(): TaskRecord[];
|
|
38
|
-
destroy(): void;
|
|
39
|
-
/** Sync load at startup — runs once before the event loop is busy. */
|
|
40
|
-
private loadStoreSync;
|
|
41
|
-
/** Coalesce rapid writes into a single async flush. */
|
|
42
|
-
private schedulePersist;
|
|
43
|
-
private persistAsync;
|
|
44
|
-
private prune;
|
|
45
|
-
private evictOldest;
|
|
46
|
-
}
|
|
1
|
+
import type { BasicLogger } from "../infra/logger";
|
|
2
|
+
export type TaskStatus = "queued" | "running" | "completed" | "failed";
|
|
3
|
+
export type TaskRecord = {
|
|
4
|
+
taskId: string;
|
|
5
|
+
fromPeerId: string;
|
|
6
|
+
toPeerId: string;
|
|
7
|
+
task: string;
|
|
8
|
+
context?: string;
|
|
9
|
+
status: TaskStatus;
|
|
10
|
+
createdAtMs: number;
|
|
11
|
+
updatedAtMs: number;
|
|
12
|
+
result?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare class TaskTracker {
|
|
16
|
+
private readonly filePath;
|
|
17
|
+
private readonly ttlMs;
|
|
18
|
+
private readonly maxTasks;
|
|
19
|
+
private readonly store;
|
|
20
|
+
private readonly logger?;
|
|
21
|
+
private pruneTimer;
|
|
22
|
+
private persistPending;
|
|
23
|
+
constructor(opts?: {
|
|
24
|
+
ttlMs?: number;
|
|
25
|
+
maxTasks?: number;
|
|
26
|
+
filePath?: string;
|
|
27
|
+
logger?: BasicLogger;
|
|
28
|
+
});
|
|
29
|
+
create(params: {
|
|
30
|
+
fromPeerId: string;
|
|
31
|
+
toPeerId: string;
|
|
32
|
+
task: string;
|
|
33
|
+
context?: string;
|
|
34
|
+
}): TaskRecord;
|
|
35
|
+
update(taskId: string, patch: Partial<Omit<TaskRecord, "taskId" | "createdAtMs">>): TaskRecord | null;
|
|
36
|
+
get(taskId: string): TaskRecord | null;
|
|
37
|
+
list(): TaskRecord[];
|
|
38
|
+
destroy(): void;
|
|
39
|
+
/** Sync load at startup — runs once before the event loop is busy. */
|
|
40
|
+
private loadStoreSync;
|
|
41
|
+
/** Coalesce rapid writes into a single async flush. */
|
|
42
|
+
private schedulePersist;
|
|
43
|
+
private persistAsync;
|
|
44
|
+
private prune;
|
|
45
|
+
private evictOldest;
|
|
46
|
+
}
|