@shareai-lab/kode-sdk 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +312 -0
- package/dist/core/agent.d.ts +85 -0
- package/dist/core/agent.js +687 -0
- package/dist/core/events.d.ts +19 -0
- package/dist/core/events.js +121 -0
- package/dist/core/hooks.d.ts +23 -0
- package/dist/core/hooks.js +71 -0
- package/dist/core/pool.d.ts +33 -0
- package/dist/core/pool.js +91 -0
- package/dist/core/room.d.ts +15 -0
- package/dist/core/room.js +57 -0
- package/dist/core/scheduler.d.ts +26 -0
- package/dist/core/scheduler.js +184 -0
- package/dist/core/types.d.ts +192 -0
- package/dist/core/types.js +13 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +57 -0
- package/dist/infra/provider.d.ts +58 -0
- package/dist/infra/provider.js +118 -0
- package/dist/infra/sandbox.d.ts +39 -0
- package/dist/infra/sandbox.js +77 -0
- package/dist/infra/store.d.ts +32 -0
- package/dist/infra/store.js +132 -0
- package/dist/tools/bash.d.ts +63 -0
- package/dist/tools/bash.js +99 -0
- package/dist/tools/builtin.d.ts +15 -0
- package/dist/tools/builtin.js +96 -0
- package/dist/tools/fs.d.ts +96 -0
- package/dist/tools/fs.js +96 -0
- package/dist/tools/task.d.ts +38 -0
- package/dist/tools/task.js +45 -0
- package/dist/utils/session-id.d.ts +21 -0
- package/dist/utils/session-id.js +64 -0
- package/package.json +47 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventBus = void 0;
|
|
4
|
+
const types_1 = require("../core/types");
|
|
5
|
+
const events_1 = require("events");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
class EventBus extends events_1.EventEmitter {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.cursor = 0;
|
|
11
|
+
this.timeline = [];
|
|
12
|
+
this.subscribers = new Set();
|
|
13
|
+
}
|
|
14
|
+
setStore(store, sessionId) {
|
|
15
|
+
this.store = store;
|
|
16
|
+
this.sessionId = sessionId;
|
|
17
|
+
}
|
|
18
|
+
emitEvent(event) {
|
|
19
|
+
const cursor = this.cursor++;
|
|
20
|
+
const eventId = (0, crypto_1.randomUUID)();
|
|
21
|
+
const timestamp = Date.now();
|
|
22
|
+
const fullEvent = { ...event, cursor, eventId, timestamp };
|
|
23
|
+
const timeline = { cursor, event: fullEvent };
|
|
24
|
+
this.timeline.push(timeline);
|
|
25
|
+
// Memory management: keep only last 10k events in memory
|
|
26
|
+
if (this.timeline.length > 10000) {
|
|
27
|
+
this.timeline = this.timeline.slice(-5000);
|
|
28
|
+
}
|
|
29
|
+
// Persist to store if configured
|
|
30
|
+
if (this.store && this.sessionId) {
|
|
31
|
+
this.store.appendEvent(this.sessionId, timeline).catch((err) => {
|
|
32
|
+
// Log error but don't block event emission
|
|
33
|
+
console.error('Failed to persist event:', err);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// Notify all subscribers
|
|
37
|
+
for (const subscriber of this.subscribers) {
|
|
38
|
+
if (subscriber.accepts(fullEvent.type)) {
|
|
39
|
+
subscriber.push(fullEvent);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Emit control plane events
|
|
43
|
+
this.emit(event.type, fullEvent);
|
|
44
|
+
return cursor;
|
|
45
|
+
}
|
|
46
|
+
subscribe(opts) {
|
|
47
|
+
const subscriber = new EventSubscriber(opts?.kinds || types_1.MINIMAL_EVENT_KINDS);
|
|
48
|
+
this.subscribers.add(subscriber);
|
|
49
|
+
// Replay past events if since is specified
|
|
50
|
+
if (opts?.since !== undefined) {
|
|
51
|
+
const past = this.timeline.filter((t) => t.cursor >= opts.since);
|
|
52
|
+
for (const t of past) {
|
|
53
|
+
if (subscriber.accepts(t.event.type)) {
|
|
54
|
+
subscriber.push(t.event);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
[Symbol.asyncIterator]: () => ({
|
|
60
|
+
next: async () => {
|
|
61
|
+
const event = await subscriber.next();
|
|
62
|
+
if (!event) {
|
|
63
|
+
this.subscribers.delete(subscriber);
|
|
64
|
+
return { done: true, value: undefined };
|
|
65
|
+
}
|
|
66
|
+
return { done: false, value: event };
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
getTimeline(since) {
|
|
72
|
+
return since !== undefined ? this.timeline.filter((t) => t.cursor >= since) : this.timeline;
|
|
73
|
+
}
|
|
74
|
+
getCursor() {
|
|
75
|
+
return this.cursor;
|
|
76
|
+
}
|
|
77
|
+
reset() {
|
|
78
|
+
this.cursor = 0;
|
|
79
|
+
this.timeline = [];
|
|
80
|
+
this.subscribers.clear();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.EventBus = EventBus;
|
|
84
|
+
class EventSubscriber {
|
|
85
|
+
constructor(kinds) {
|
|
86
|
+
this.kinds = kinds;
|
|
87
|
+
this.queue = [];
|
|
88
|
+
this.waiting = null;
|
|
89
|
+
this.closed = false;
|
|
90
|
+
}
|
|
91
|
+
accepts(kind) {
|
|
92
|
+
return this.kinds.includes(kind);
|
|
93
|
+
}
|
|
94
|
+
push(event) {
|
|
95
|
+
if (this.closed)
|
|
96
|
+
return;
|
|
97
|
+
if (this.waiting) {
|
|
98
|
+
this.waiting(event);
|
|
99
|
+
this.waiting = null;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
this.queue.push(event);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async next() {
|
|
106
|
+
if (this.closed)
|
|
107
|
+
return null;
|
|
108
|
+
if (this.queue.length > 0)
|
|
109
|
+
return this.queue.shift();
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
this.waiting = resolve;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
close() {
|
|
115
|
+
this.closed = true;
|
|
116
|
+
if (this.waiting) {
|
|
117
|
+
this.waiting(null);
|
|
118
|
+
this.waiting = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ToolCall, ToolOutcome, HookDecision, PostHookResult, ToolContext } from '../core/types';
|
|
2
|
+
import { ProviderResponse } from '../infra/provider';
|
|
3
|
+
export interface Hooks {
|
|
4
|
+
preToolUse?: (call: ToolCall, ctx: ToolContext) => HookDecision | Promise<HookDecision>;
|
|
5
|
+
postToolUse?: (outcome: ToolOutcome, ctx: ToolContext) => PostHookResult | Promise<PostHookResult>;
|
|
6
|
+
preModel?: (request: any) => void | Promise<void>;
|
|
7
|
+
postModel?: (response: ProviderResponse) => void | Promise<void>;
|
|
8
|
+
messagesChanged?: (snapshot: any) => void | Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface RegisteredHook {
|
|
11
|
+
origin: 'agent' | 'toolTune';
|
|
12
|
+
names: Array<'preToolUse' | 'postToolUse' | 'preModel' | 'postModel'>;
|
|
13
|
+
}
|
|
14
|
+
export declare class HookManager {
|
|
15
|
+
private hooks;
|
|
16
|
+
register(hooks: Hooks, origin?: 'agent' | 'toolTune'): void;
|
|
17
|
+
getRegistered(): ReadonlyArray<RegisteredHook>;
|
|
18
|
+
runPreToolUse(call: ToolCall, ctx: ToolContext): Promise<HookDecision>;
|
|
19
|
+
runPostToolUse(outcome: ToolOutcome, ctx: ToolContext): Promise<ToolOutcome>;
|
|
20
|
+
runPreModel(request: any): Promise<void>;
|
|
21
|
+
runPostModel(response: ProviderResponse): Promise<void>;
|
|
22
|
+
runMessagesChanged(snapshot: any): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HookManager = void 0;
|
|
4
|
+
class HookManager {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.hooks = [];
|
|
7
|
+
}
|
|
8
|
+
register(hooks, origin = 'agent') {
|
|
9
|
+
this.hooks.push({ hooks, origin });
|
|
10
|
+
}
|
|
11
|
+
getRegistered() {
|
|
12
|
+
return this.hooks.map(({ hooks, origin }) => ({
|
|
13
|
+
origin,
|
|
14
|
+
names: [
|
|
15
|
+
hooks.preToolUse && 'preToolUse',
|
|
16
|
+
hooks.postToolUse && 'postToolUse',
|
|
17
|
+
hooks.preModel && 'preModel',
|
|
18
|
+
hooks.postModel && 'postModel',
|
|
19
|
+
].filter(Boolean),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
async runPreToolUse(call, ctx) {
|
|
23
|
+
for (const { hooks } of this.hooks) {
|
|
24
|
+
if (hooks.preToolUse) {
|
|
25
|
+
const result = await hooks.preToolUse(call, ctx);
|
|
26
|
+
if (result)
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
async runPostToolUse(outcome, ctx) {
|
|
33
|
+
let current = outcome;
|
|
34
|
+
for (const { hooks } of this.hooks) {
|
|
35
|
+
if (hooks.postToolUse) {
|
|
36
|
+
const result = await hooks.postToolUse(current, ctx);
|
|
37
|
+
if (result) {
|
|
38
|
+
if ('replace' in result) {
|
|
39
|
+
current = result.replace;
|
|
40
|
+
}
|
|
41
|
+
else if ('update' in result) {
|
|
42
|
+
current = { ...current, ...result.update };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
async runPreModel(request) {
|
|
50
|
+
for (const { hooks } of this.hooks) {
|
|
51
|
+
if (hooks.preModel) {
|
|
52
|
+
await hooks.preModel(request);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async runPostModel(response) {
|
|
57
|
+
for (const { hooks } of this.hooks) {
|
|
58
|
+
if (hooks.postModel) {
|
|
59
|
+
await hooks.postModel(response);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async runMessagesChanged(snapshot) {
|
|
64
|
+
for (const { hooks } of this.hooks) {
|
|
65
|
+
if (hooks.messagesChanged) {
|
|
66
|
+
await hooks.messagesChanged(snapshot);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.HookManager = HookManager;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Agent, AgentOptions } from '../core/agent';
|
|
2
|
+
import { Store } from '../infra/store';
|
|
3
|
+
import { AgentTemplate } from '../tools/task';
|
|
4
|
+
import { AgentStatus, SnapshotId } from '../core/types';
|
|
5
|
+
export interface AgentPoolOptions {
|
|
6
|
+
store: Store;
|
|
7
|
+
maxAgents?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare class AgentPool {
|
|
10
|
+
private agents;
|
|
11
|
+
private store;
|
|
12
|
+
private maxAgents;
|
|
13
|
+
constructor(opts: AgentPoolOptions);
|
|
14
|
+
create(sessionId: string, templateOrOpts: AgentTemplate | AgentOptions, overrides?: Partial<AgentOptions>): Agent;
|
|
15
|
+
get(sessionId: string): Agent | undefined;
|
|
16
|
+
list(opts?: {
|
|
17
|
+
prefix?: string;
|
|
18
|
+
}): string[];
|
|
19
|
+
status(sessionId: string): Promise<AgentStatus | undefined>;
|
|
20
|
+
fork(sessionId: string, snapshotSel?: SnapshotId | {
|
|
21
|
+
at?: string;
|
|
22
|
+
}): Promise<Agent>;
|
|
23
|
+
resume(sessionId: string, opts: Omit<AgentOptions, 'sessionId' | 'store'> & {
|
|
24
|
+
autoRun?: boolean;
|
|
25
|
+
strategy?: 'crash' | 'manual';
|
|
26
|
+
}): Promise<Agent>;
|
|
27
|
+
resumeAll(configFactory: (sessionId: string) => Omit<AgentOptions, 'sessionId' | 'store'>, opts?: {
|
|
28
|
+
autoRun?: boolean;
|
|
29
|
+
strategy?: 'crash' | 'manual';
|
|
30
|
+
}): Promise<Agent[]>;
|
|
31
|
+
delete(sessionId: string): Promise<void>;
|
|
32
|
+
size(): number;
|
|
33
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentPool = void 0;
|
|
4
|
+
const agent_1 = require("../core/agent");
|
|
5
|
+
class AgentPool {
|
|
6
|
+
constructor(opts) {
|
|
7
|
+
this.agents = new Map();
|
|
8
|
+
this.store = opts.store;
|
|
9
|
+
this.maxAgents = opts.maxAgents || 50;
|
|
10
|
+
}
|
|
11
|
+
create(sessionId, templateOrOpts, overrides) {
|
|
12
|
+
if (this.agents.has(sessionId)) {
|
|
13
|
+
throw new Error(`Agent already exists: ${sessionId}`);
|
|
14
|
+
}
|
|
15
|
+
if (this.agents.size >= this.maxAgents) {
|
|
16
|
+
throw new Error(`Pool is full (max ${this.maxAgents} agents)`);
|
|
17
|
+
}
|
|
18
|
+
const agent = new agent_1.Agent(templateOrOpts, overrides);
|
|
19
|
+
this.agents.set(sessionId, agent);
|
|
20
|
+
return agent;
|
|
21
|
+
}
|
|
22
|
+
get(sessionId) {
|
|
23
|
+
return this.agents.get(sessionId);
|
|
24
|
+
}
|
|
25
|
+
list(opts) {
|
|
26
|
+
const ids = Array.from(this.agents.keys());
|
|
27
|
+
return opts?.prefix ? ids.filter((id) => id.startsWith(opts.prefix)) : ids;
|
|
28
|
+
}
|
|
29
|
+
async status(sessionId) {
|
|
30
|
+
const agent = this.agents.get(sessionId);
|
|
31
|
+
return agent ? await agent.status() : undefined;
|
|
32
|
+
}
|
|
33
|
+
async fork(sessionId, snapshotSel) {
|
|
34
|
+
const agent = this.agents.get(sessionId);
|
|
35
|
+
if (!agent) {
|
|
36
|
+
throw new Error(`Agent not found: ${sessionId}`);
|
|
37
|
+
}
|
|
38
|
+
return agent.fork(snapshotSel);
|
|
39
|
+
}
|
|
40
|
+
async resume(sessionId, opts) {
|
|
41
|
+
// 1. Check if already in pool
|
|
42
|
+
if (this.agents.has(sessionId)) {
|
|
43
|
+
return this.agents.get(sessionId);
|
|
44
|
+
}
|
|
45
|
+
// 2. Check pool capacity
|
|
46
|
+
if (this.agents.size >= this.maxAgents) {
|
|
47
|
+
throw new Error(`Pool is full (max ${this.maxAgents} agents)`);
|
|
48
|
+
}
|
|
49
|
+
// 3. Verify session exists
|
|
50
|
+
const exists = await this.store.exists(sessionId);
|
|
51
|
+
if (!exists) {
|
|
52
|
+
throw new Error(`Session not found in store: ${sessionId}`);
|
|
53
|
+
}
|
|
54
|
+
// 4. Use Agent.resume() to restore
|
|
55
|
+
const agent = await agent_1.Agent.resume(sessionId, {
|
|
56
|
+
...opts,
|
|
57
|
+
sessionId,
|
|
58
|
+
store: this.store,
|
|
59
|
+
});
|
|
60
|
+
// 5. Add to pool
|
|
61
|
+
this.agents.set(sessionId, agent);
|
|
62
|
+
return agent;
|
|
63
|
+
}
|
|
64
|
+
async resumeAll(configFactory, opts) {
|
|
65
|
+
const sessionIds = await this.store.list();
|
|
66
|
+
const resumed = [];
|
|
67
|
+
for (const sessionId of sessionIds) {
|
|
68
|
+
if (this.agents.size >= this.maxAgents)
|
|
69
|
+
break;
|
|
70
|
+
if (this.agents.has(sessionId))
|
|
71
|
+
continue;
|
|
72
|
+
try {
|
|
73
|
+
const config = configFactory(sessionId);
|
|
74
|
+
const agent = await this.resume(sessionId, { ...config, ...opts });
|
|
75
|
+
resumed.push(agent);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error(`Failed to resume ${sessionId}:`, error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return resumed;
|
|
82
|
+
}
|
|
83
|
+
async delete(sessionId) {
|
|
84
|
+
this.agents.delete(sessionId);
|
|
85
|
+
await this.store.delete(sessionId);
|
|
86
|
+
}
|
|
87
|
+
size() {
|
|
88
|
+
return this.agents.size;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.AgentPool = AgentPool;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AgentPool } from '../core/pool';
|
|
2
|
+
export interface RoomMember {
|
|
3
|
+
name: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class Room {
|
|
7
|
+
private pool;
|
|
8
|
+
private members;
|
|
9
|
+
constructor(pool: AgentPool);
|
|
10
|
+
join(name: string, sessionId: string): void;
|
|
11
|
+
leave(name: string): void;
|
|
12
|
+
say(from: string, text: string): Promise<void>;
|
|
13
|
+
getMembers(): RoomMember[];
|
|
14
|
+
private extractMentions;
|
|
15
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Room = void 0;
|
|
4
|
+
class Room {
|
|
5
|
+
constructor(pool) {
|
|
6
|
+
this.pool = pool;
|
|
7
|
+
this.members = new Map();
|
|
8
|
+
}
|
|
9
|
+
join(name, sessionId) {
|
|
10
|
+
if (this.members.has(name)) {
|
|
11
|
+
throw new Error(`Member already exists: ${name}`);
|
|
12
|
+
}
|
|
13
|
+
this.members.set(name, sessionId);
|
|
14
|
+
}
|
|
15
|
+
leave(name) {
|
|
16
|
+
this.members.delete(name);
|
|
17
|
+
}
|
|
18
|
+
async say(from, text) {
|
|
19
|
+
const mentions = this.extractMentions(text);
|
|
20
|
+
if (mentions.length > 0) {
|
|
21
|
+
// Directed message
|
|
22
|
+
for (const mention of mentions) {
|
|
23
|
+
const sessionId = this.members.get(mention);
|
|
24
|
+
if (sessionId) {
|
|
25
|
+
const agent = this.pool.get(sessionId);
|
|
26
|
+
if (agent) {
|
|
27
|
+
await agent.send(`[from:${from}] ${text}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Broadcast to all except sender
|
|
34
|
+
for (const [name, sessionId] of this.members) {
|
|
35
|
+
if (name !== from) {
|
|
36
|
+
const agent = this.pool.get(sessionId);
|
|
37
|
+
if (agent) {
|
|
38
|
+
await agent.send(`[from:${from}] ${text}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getMembers() {
|
|
45
|
+
return Array.from(this.members.entries()).map(([name, sessionId]) => ({ name, sessionId }));
|
|
46
|
+
}
|
|
47
|
+
extractMentions(text) {
|
|
48
|
+
const regex = /@(\w+)/g;
|
|
49
|
+
const mentions = [];
|
|
50
|
+
let match;
|
|
51
|
+
while ((match = regex.exec(text)) !== null) {
|
|
52
|
+
mentions.push(match[1]);
|
|
53
|
+
}
|
|
54
|
+
return mentions;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.Room = Room;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type TimeInterval = string;
|
|
2
|
+
type ScheduleCallback = (ctx?: any) => void | Promise<void>;
|
|
3
|
+
export declare class Scheduler {
|
|
4
|
+
private tasks;
|
|
5
|
+
private stepCounters;
|
|
6
|
+
private timers;
|
|
7
|
+
every(interval: TimeInterval, callback: ScheduleCallback): this;
|
|
8
|
+
everySteps(steps: number, callback: ScheduleCallback, targetId?: string): this;
|
|
9
|
+
daily(time: string, callback: ScheduleCallback): this;
|
|
10
|
+
weekly(dayTime: string, callback: ScheduleCallback): this;
|
|
11
|
+
notifyStep(targetId?: string): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
private parseInterval;
|
|
14
|
+
private scheduleDailyTask;
|
|
15
|
+
private scheduleWeeklyTask;
|
|
16
|
+
}
|
|
17
|
+
export declare class AgentSchedulerHandle {
|
|
18
|
+
private scheduler;
|
|
19
|
+
private agentId?;
|
|
20
|
+
constructor(scheduler: Scheduler, agentId?: string | undefined);
|
|
21
|
+
every(interval: TimeInterval, callback: ScheduleCallback): this;
|
|
22
|
+
everySteps(steps: number, callback: ScheduleCallback): this;
|
|
23
|
+
daily(time: string, callback: ScheduleCallback): this;
|
|
24
|
+
weekly(dayTime: string, callback: ScheduleCallback): this;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentSchedulerHandle = exports.Scheduler = void 0;
|
|
4
|
+
class Scheduler {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.tasks = new Map();
|
|
7
|
+
this.stepCounters = new Map();
|
|
8
|
+
this.timers = [];
|
|
9
|
+
}
|
|
10
|
+
every(interval, callback) {
|
|
11
|
+
const ms = this.parseInterval(interval);
|
|
12
|
+
const id = `time-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
13
|
+
const task = {
|
|
14
|
+
id,
|
|
15
|
+
type: 'time',
|
|
16
|
+
spec: interval,
|
|
17
|
+
callback,
|
|
18
|
+
enabled: true,
|
|
19
|
+
};
|
|
20
|
+
this.tasks.set(id, task);
|
|
21
|
+
const timer = setInterval(() => {
|
|
22
|
+
if (task.enabled) {
|
|
23
|
+
task.callback({ count: 0, type: 'time', id });
|
|
24
|
+
task.lastRun = Date.now();
|
|
25
|
+
}
|
|
26
|
+
}, ms);
|
|
27
|
+
this.timers.push(timer);
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
everySteps(steps, callback, targetId) {
|
|
31
|
+
const id = targetId || `step-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
32
|
+
const task = {
|
|
33
|
+
id,
|
|
34
|
+
type: 'step',
|
|
35
|
+
spec: steps,
|
|
36
|
+
callback,
|
|
37
|
+
enabled: true,
|
|
38
|
+
};
|
|
39
|
+
this.tasks.set(id, task);
|
|
40
|
+
this.stepCounters.set(id, 0);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
daily(time, callback) {
|
|
44
|
+
const id = `daily-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
45
|
+
const task = {
|
|
46
|
+
id,
|
|
47
|
+
type: 'daily',
|
|
48
|
+
spec: time,
|
|
49
|
+
callback,
|
|
50
|
+
enabled: true,
|
|
51
|
+
};
|
|
52
|
+
this.tasks.set(id, task);
|
|
53
|
+
this.scheduleDailyTask(task);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
weekly(dayTime, callback) {
|
|
57
|
+
const id = `weekly-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
58
|
+
const task = {
|
|
59
|
+
id,
|
|
60
|
+
type: 'weekly',
|
|
61
|
+
spec: dayTime,
|
|
62
|
+
callback,
|
|
63
|
+
enabled: true,
|
|
64
|
+
};
|
|
65
|
+
this.tasks.set(id, task);
|
|
66
|
+
this.scheduleWeeklyTask(task);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
notifyStep(targetId) {
|
|
70
|
+
for (const [id, task] of this.tasks) {
|
|
71
|
+
if (task.type === 'step' && (!targetId || id === targetId)) {
|
|
72
|
+
const count = (this.stepCounters.get(id) || 0) + 1;
|
|
73
|
+
this.stepCounters.set(id, count);
|
|
74
|
+
if (count >= task.spec) {
|
|
75
|
+
task.callback({ count, type: 'step', id });
|
|
76
|
+
this.stepCounters.set(id, 0);
|
|
77
|
+
task.lastRun = Date.now();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
stop() {
|
|
83
|
+
for (const timer of this.timers) {
|
|
84
|
+
clearInterval(timer);
|
|
85
|
+
}
|
|
86
|
+
this.timers = [];
|
|
87
|
+
this.tasks.clear();
|
|
88
|
+
this.stepCounters.clear();
|
|
89
|
+
}
|
|
90
|
+
parseInterval(interval) {
|
|
91
|
+
const match = interval.match(/^(\d+)(s|m|h|d)$/);
|
|
92
|
+
if (!match)
|
|
93
|
+
throw new Error(`Invalid interval: ${interval}`);
|
|
94
|
+
const value = parseInt(match[1], 10);
|
|
95
|
+
const unit = match[2];
|
|
96
|
+
switch (unit) {
|
|
97
|
+
case 's':
|
|
98
|
+
return value * 1000;
|
|
99
|
+
case 'm':
|
|
100
|
+
return value * 60 * 1000;
|
|
101
|
+
case 'h':
|
|
102
|
+
return value * 60 * 60 * 1000;
|
|
103
|
+
case 'd':
|
|
104
|
+
return value * 24 * 60 * 60 * 1000;
|
|
105
|
+
default:
|
|
106
|
+
throw new Error(`Unknown unit: ${unit}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
scheduleDailyTask(task) {
|
|
110
|
+
const [hours, minutes] = task.spec.split(':').map(Number);
|
|
111
|
+
const now = new Date();
|
|
112
|
+
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes, 0);
|
|
113
|
+
if (target.getTime() <= now.getTime()) {
|
|
114
|
+
target.setDate(target.getDate() + 1);
|
|
115
|
+
}
|
|
116
|
+
const delay = target.getTime() - now.getTime();
|
|
117
|
+
const timeout = setTimeout(() => {
|
|
118
|
+
if (task.enabled) {
|
|
119
|
+
task.callback({ time: task.spec, type: 'daily', id: task.id });
|
|
120
|
+
task.lastRun = Date.now();
|
|
121
|
+
}
|
|
122
|
+
this.scheduleDailyTask(task);
|
|
123
|
+
}, delay);
|
|
124
|
+
this.timers.push(timeout);
|
|
125
|
+
}
|
|
126
|
+
scheduleWeeklyTask(task) {
|
|
127
|
+
const [day, time] = task.spec.split(' ');
|
|
128
|
+
const [hours, minutes] = time.split(':').map(Number);
|
|
129
|
+
const dayMap = {
|
|
130
|
+
Sun: 0,
|
|
131
|
+
Mon: 1,
|
|
132
|
+
Tue: 2,
|
|
133
|
+
Wed: 3,
|
|
134
|
+
Thu: 4,
|
|
135
|
+
Fri: 5,
|
|
136
|
+
Sat: 6,
|
|
137
|
+
};
|
|
138
|
+
const targetDay = dayMap[day];
|
|
139
|
+
if (targetDay === undefined)
|
|
140
|
+
throw new Error(`Invalid day: ${day}`);
|
|
141
|
+
const now = new Date();
|
|
142
|
+
const target = new Date(now);
|
|
143
|
+
target.setHours(hours, minutes, 0, 0);
|
|
144
|
+
const currentDay = now.getDay();
|
|
145
|
+
let daysUntilTarget = targetDay - currentDay;
|
|
146
|
+
if (daysUntilTarget < 0 || (daysUntilTarget === 0 && target.getTime() <= now.getTime())) {
|
|
147
|
+
daysUntilTarget += 7;
|
|
148
|
+
}
|
|
149
|
+
target.setDate(target.getDate() + daysUntilTarget);
|
|
150
|
+
const delay = target.getTime() - now.getTime();
|
|
151
|
+
const timeout = setTimeout(() => {
|
|
152
|
+
if (task.enabled) {
|
|
153
|
+
task.callback({ time: task.spec, type: 'weekly', id: task.id });
|
|
154
|
+
task.lastRun = Date.now();
|
|
155
|
+
}
|
|
156
|
+
this.scheduleWeeklyTask(task);
|
|
157
|
+
}, delay);
|
|
158
|
+
this.timers.push(timeout);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.Scheduler = Scheduler;
|
|
162
|
+
class AgentSchedulerHandle {
|
|
163
|
+
constructor(scheduler, agentId) {
|
|
164
|
+
this.scheduler = scheduler;
|
|
165
|
+
this.agentId = agentId;
|
|
166
|
+
}
|
|
167
|
+
every(interval, callback) {
|
|
168
|
+
this.scheduler.every(interval, callback);
|
|
169
|
+
return this;
|
|
170
|
+
}
|
|
171
|
+
everySteps(steps, callback) {
|
|
172
|
+
this.scheduler.everySteps(steps, callback, this.agentId);
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
daily(time, callback) {
|
|
176
|
+
this.scheduler.daily(time, callback);
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
weekly(dayTime, callback) {
|
|
180
|
+
this.scheduler.weekly(dayTime, callback);
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.AgentSchedulerHandle = AgentSchedulerHandle;
|