@syncended/dsh-automations 0.1.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/LICENSE +21 -0
- package/README.md +128 -0
- package/cordis.patch.yml +5 -0
- package/dist/agent-executor.d.ts +15 -0
- package/dist/agent-executor.d.ts.map +1 -0
- package/dist/agent-executor.js +173 -0
- package/dist/agent-executor.js.map +1 -0
- package/dist/cron.d.ts +15 -0
- package/dist/cron.d.ts.map +1 -0
- package/dist/cron.js +74 -0
- package/dist/cron.js.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +203 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -0
- package/dist/project-policy.d.ts +9 -0
- package/dist/project-policy.d.ts.map +1 -0
- package/dist/project-policy.js +53 -0
- package/dist/project-policy.js.map +1 -0
- package/dist/scheduler.d.ts +58 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +518 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/state.d.ts +8 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +44 -0
- package/dist/state.js.map +1 -0
- package/dist/store.d.ts +21 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +317 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +13 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +159 -0
- package/dist/validation.js.map +1 -0
- package/docs/architecture.md +195 -0
- package/docs/http-api.md +40 -0
- package/docs/security.md +51 -0
- package/lib/client.js +1251 -0
- package/package.json +105 -0
package/dist/http.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { AutomationInputError, assertExpectedVersion, assertJsonObject, assertOnlyKeys } from './validation.js';
|
|
2
|
+
const MAX_BODY_BYTES = 1_048_576;
|
|
3
|
+
function sendJson(response, status, value) {
|
|
4
|
+
const body = `${JSON.stringify(value)}\n`;
|
|
5
|
+
response.writeHead(status, {
|
|
6
|
+
'content-type': 'application/json; charset=utf-8',
|
|
7
|
+
'content-length': Buffer.byteLength(body),
|
|
8
|
+
'cache-control': 'no-store',
|
|
9
|
+
'x-content-type-options': 'nosniff',
|
|
10
|
+
});
|
|
11
|
+
response.end(body);
|
|
12
|
+
}
|
|
13
|
+
function publicRun(run) {
|
|
14
|
+
const visible = { ...run };
|
|
15
|
+
delete visible.snapshot;
|
|
16
|
+
return visible;
|
|
17
|
+
}
|
|
18
|
+
function methodNotAllowed(response, allowed) {
|
|
19
|
+
response.setHeader('allow', allowed.join(', '));
|
|
20
|
+
sendJson(response, 405, {
|
|
21
|
+
error: {
|
|
22
|
+
code: 'METHOD_NOT_ALLOWED',
|
|
23
|
+
message: `Allowed method(s): ${allowed.join(', ')}`,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function mutationFence(request) {
|
|
28
|
+
if (request.headers['x-dsh-automation-client'] !== '1') {
|
|
29
|
+
throw new AutomationInputError('missing automation mutation header', 'MUTATION_FENCE', 403);
|
|
30
|
+
}
|
|
31
|
+
const contentType = request.headers['content-type'] ?? '';
|
|
32
|
+
if (!contentType.toLowerCase().startsWith('application/json')) {
|
|
33
|
+
throw new AutomationInputError('mutations require application/json', 'UNSUPPORTED_MEDIA_TYPE', 415);
|
|
34
|
+
}
|
|
35
|
+
const fetchSite = request.headers['sec-fetch-site'];
|
|
36
|
+
if (fetchSite !== undefined && fetchSite !== 'same-origin') {
|
|
37
|
+
throw new AutomationInputError('cross-origin automation mutation refused', 'CROSS_ORIGIN', 403);
|
|
38
|
+
}
|
|
39
|
+
const origin = request.headers.origin;
|
|
40
|
+
const host = request.headers.host;
|
|
41
|
+
if (origin !== undefined) {
|
|
42
|
+
let originHost;
|
|
43
|
+
try {
|
|
44
|
+
originHost = new URL(origin).host;
|
|
45
|
+
}
|
|
46
|
+
catch (cause) {
|
|
47
|
+
throw new AutomationInputError('invalid Origin header', 'CROSS_ORIGIN', 403, { cause });
|
|
48
|
+
}
|
|
49
|
+
if (host === undefined || originHost.toLowerCase() !== host.toLowerCase()) {
|
|
50
|
+
throw new AutomationInputError('Origin does not match the Harness host', 'CROSS_ORIGIN', 403);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function readJson(request) {
|
|
55
|
+
let size = 0;
|
|
56
|
+
const chunks = [];
|
|
57
|
+
for await (const chunk of request) {
|
|
58
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
59
|
+
size += buffer.length;
|
|
60
|
+
if (size > MAX_BODY_BYTES)
|
|
61
|
+
throw new AutomationInputError('request body is too large', 'BODY_TOO_LARGE', 413);
|
|
62
|
+
chunks.push(buffer);
|
|
63
|
+
}
|
|
64
|
+
if (chunks.length === 0)
|
|
65
|
+
return {};
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
68
|
+
}
|
|
69
|
+
catch (cause) {
|
|
70
|
+
throw new AutomationInputError('request body is not valid JSON', 'INVALID_JSON', 400, { cause });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function segment(value, label) {
|
|
74
|
+
try {
|
|
75
|
+
const decoded = decodeURIComponent(value);
|
|
76
|
+
if (decoded === '' || decoded.includes('/'))
|
|
77
|
+
throw new Error('invalid path segment');
|
|
78
|
+
return decoded;
|
|
79
|
+
}
|
|
80
|
+
catch (cause) {
|
|
81
|
+
throw new AutomationInputError(`${label} path segment is invalid`, 'INVALID_PATH', 400, { cause });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function parseLimit(url) {
|
|
85
|
+
const raw = url.searchParams.get('limit');
|
|
86
|
+
if (raw === null)
|
|
87
|
+
return 100;
|
|
88
|
+
const value = Number(raw);
|
|
89
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 1_000) {
|
|
90
|
+
throw new AutomationInputError('limit must be an integer between 1 and 1000');
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
export function createAutomationHttpHandler(service, prefix, logger) {
|
|
95
|
+
return async (request, response) => {
|
|
96
|
+
try {
|
|
97
|
+
const url = new URL(request.url ?? '/', 'http://dsh.local');
|
|
98
|
+
const relative = url.pathname.slice(prefix.length).replace(/^\/+|\/+$/g, '');
|
|
99
|
+
const parts = relative === '' ? [] : relative.split('/');
|
|
100
|
+
const method = request.method ?? 'GET';
|
|
101
|
+
if (parts.length === 0) {
|
|
102
|
+
if (method !== 'GET')
|
|
103
|
+
return methodNotAllowed(response, ['GET']);
|
|
104
|
+
const snapshot = service.snapshot(parseLimit(url));
|
|
105
|
+
return sendJson(response, 200, {
|
|
106
|
+
...snapshot,
|
|
107
|
+
// Executor snapshots remain durable server-side but need not duplicate prompts in API responses.
|
|
108
|
+
runs: snapshot.runs.map(publicRun),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (parts.length === 1 && parts[0] === 'meta') {
|
|
112
|
+
if (method !== 'GET')
|
|
113
|
+
return methodNotAllowed(response, ['GET']);
|
|
114
|
+
return sendJson(response, 200, await service.metadata());
|
|
115
|
+
}
|
|
116
|
+
if (parts.length === 1 && parts[0] === 'jobs') {
|
|
117
|
+
if (method !== 'POST')
|
|
118
|
+
return methodNotAllowed(response, ['POST']);
|
|
119
|
+
mutationFence(request);
|
|
120
|
+
const body = assertJsonObject(await readJson(request));
|
|
121
|
+
assertOnlyKeys(body, ['id', 'spec']);
|
|
122
|
+
if (body.id !== undefined && typeof body.id !== 'string') {
|
|
123
|
+
throw new AutomationInputError('id must be a string');
|
|
124
|
+
}
|
|
125
|
+
const job = await service.create({
|
|
126
|
+
spec: body.spec,
|
|
127
|
+
...(body.id === undefined ? {} : { id: body.id }),
|
|
128
|
+
});
|
|
129
|
+
return sendJson(response, 201, job);
|
|
130
|
+
}
|
|
131
|
+
if (parts.length >= 2 && parts[0] === 'jobs') {
|
|
132
|
+
const id = segment(parts[1], 'job id');
|
|
133
|
+
if (parts.length === 2 && method === 'PUT') {
|
|
134
|
+
mutationFence(request);
|
|
135
|
+
const body = assertJsonObject(await readJson(request));
|
|
136
|
+
assertOnlyKeys(body, ['expectedVersion', 'spec']);
|
|
137
|
+
const expectedVersion = assertExpectedVersion(body.expectedVersion);
|
|
138
|
+
const job = await service.update(id, {
|
|
139
|
+
spec: body.spec,
|
|
140
|
+
...(expectedVersion === undefined ? {} : { expectedVersion }),
|
|
141
|
+
});
|
|
142
|
+
return sendJson(response, 200, job);
|
|
143
|
+
}
|
|
144
|
+
if (parts.length === 2 && method === 'DELETE') {
|
|
145
|
+
mutationFence(request);
|
|
146
|
+
// DELETE still carries an explicit empty JSON object to stay behind the same mutation fence.
|
|
147
|
+
const body = assertJsonObject(await readJson(request));
|
|
148
|
+
assertOnlyKeys(body, []);
|
|
149
|
+
await service.remove(id);
|
|
150
|
+
response.writeHead(204, { 'cache-control': 'no-store' });
|
|
151
|
+
response.end();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (parts.length === 3 && parts[2] === 'run') {
|
|
155
|
+
if (method !== 'POST')
|
|
156
|
+
return methodNotAllowed(response, ['POST']);
|
|
157
|
+
mutationFence(request);
|
|
158
|
+
const body = assertJsonObject(await readJson(request));
|
|
159
|
+
assertOnlyKeys(body, []);
|
|
160
|
+
return sendJson(response, 202, publicRun(await service.trigger(id)));
|
|
161
|
+
}
|
|
162
|
+
if (parts.length === 3 && parts[2] === 'enabled') {
|
|
163
|
+
if (method !== 'POST')
|
|
164
|
+
return methodNotAllowed(response, ['POST']);
|
|
165
|
+
mutationFence(request);
|
|
166
|
+
const body = assertJsonObject(await readJson(request));
|
|
167
|
+
assertOnlyKeys(body, ['enabled', 'expectedVersion']);
|
|
168
|
+
if (typeof body.enabled !== 'boolean')
|
|
169
|
+
throw new AutomationInputError('enabled must be a boolean');
|
|
170
|
+
const expectedVersion = assertExpectedVersion(body.expectedVersion);
|
|
171
|
+
return sendJson(response, 200, await service.setEnabled(id, body.enabled, expectedVersion));
|
|
172
|
+
}
|
|
173
|
+
return sendJson(response, 404, { error: { code: 'NOT_FOUND', message: 'Automation API route not found.' } });
|
|
174
|
+
}
|
|
175
|
+
if (parts.length === 3 && parts[0] === 'runs' && parts[2] === 'cancel') {
|
|
176
|
+
if (method !== 'POST')
|
|
177
|
+
return methodNotAllowed(response, ['POST']);
|
|
178
|
+
mutationFence(request);
|
|
179
|
+
const body = assertJsonObject(await readJson(request));
|
|
180
|
+
assertOnlyKeys(body, ['reason']);
|
|
181
|
+
if (body.reason !== undefined && typeof body.reason !== 'string') {
|
|
182
|
+
throw new AutomationInputError('reason must be a string');
|
|
183
|
+
}
|
|
184
|
+
const runId = segment(parts[1], 'run id');
|
|
185
|
+
return sendJson(response, 202, publicRun(await service.cancel(runId, body.reason)));
|
|
186
|
+
}
|
|
187
|
+
return sendJson(response, 404, { error: { code: 'NOT_FOUND', message: 'Automation API route not found.' } });
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
const status = error instanceof AutomationInputError ? error.status : 500;
|
|
191
|
+
const code = error instanceof AutomationInputError ? error.code : 'INTERNAL_ERROR';
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
if (status >= 500)
|
|
194
|
+
logger.warn('automations: HTTP request failed: %s', message);
|
|
195
|
+
const publicMessage = status >= 500 ? 'Internal automation error.' : message;
|
|
196
|
+
if (!response.headersSent)
|
|
197
|
+
sendJson(response, status, { error: { code, message: publicMessage } });
|
|
198
|
+
else
|
|
199
|
+
response.destroy(error instanceof Error ? error : new Error(message));
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Context, Service } from '@deepseek-ai/cordis';
|
|
2
|
+
import z from '@deepseek-ai/schemastery';
|
|
3
|
+
import { type AutomationExecutor, type AutomationJob, type AutomationMeta, type AutomationRun, type AutomationServiceApi, type AutomationServiceConfig, type AutomationSnapshot, type CreateAutomationJobRequest, type UpdateAutomationJobRequest } from './types.js';
|
|
4
|
+
export * from './cron.js';
|
|
5
|
+
export * from './project-policy.js';
|
|
6
|
+
export * from './scheduler.js';
|
|
7
|
+
export * from './state.js';
|
|
8
|
+
export * from './store.js';
|
|
9
|
+
export * from './types.js';
|
|
10
|
+
export * from './validation.js';
|
|
11
|
+
export declare const name = "automations";
|
|
12
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
13
|
+
statePath: z<string, string>;
|
|
14
|
+
dshHome: z<string, string>;
|
|
15
|
+
maxConcurrentRuns: z<number, number>;
|
|
16
|
+
historyLimit: z<number, number>;
|
|
17
|
+
misfireGraceMs: z<number, number>;
|
|
18
|
+
maxOutputChars: z<number, number>;
|
|
19
|
+
allowedProjectRoots: z<string[], string[]>;
|
|
20
|
+
}>, Schemastery.ObjectT<{
|
|
21
|
+
statePath: z<string, string>;
|
|
22
|
+
dshHome: z<string, string>;
|
|
23
|
+
maxConcurrentRuns: z<number, number>;
|
|
24
|
+
historyLimit: z<number, number>;
|
|
25
|
+
misfireGraceMs: z<number, number>;
|
|
26
|
+
maxOutputChars: z<number, number>;
|
|
27
|
+
allowedProjectRoots: z<string[], string[]>;
|
|
28
|
+
}>>;
|
|
29
|
+
/** Durable cron automation service and executor registry. */
|
|
30
|
+
export declare class AutomationService extends Service implements AutomationServiceApi {
|
|
31
|
+
static Config: z<Schemastery.ObjectS<{
|
|
32
|
+
statePath: z<string, string>;
|
|
33
|
+
dshHome: z<string, string>;
|
|
34
|
+
maxConcurrentRuns: z<number, number>;
|
|
35
|
+
historyLimit: z<number, number>;
|
|
36
|
+
misfireGraceMs: z<number, number>;
|
|
37
|
+
maxOutputChars: z<number, number>;
|
|
38
|
+
allowedProjectRoots: z<string[], string[]>;
|
|
39
|
+
}>, Schemastery.ObjectT<{
|
|
40
|
+
statePath: z<string, string>;
|
|
41
|
+
dshHome: z<string, string>;
|
|
42
|
+
maxConcurrentRuns: z<number, number>;
|
|
43
|
+
historyLimit: z<number, number>;
|
|
44
|
+
misfireGraceMs: z<number, number>;
|
|
45
|
+
maxOutputChars: z<number, number>;
|
|
46
|
+
allowedProjectRoots: z<string[], string[]>;
|
|
47
|
+
}>>;
|
|
48
|
+
static inject: string[];
|
|
49
|
+
readonly statePath: string;
|
|
50
|
+
private readonly store;
|
|
51
|
+
private readonly scheduler;
|
|
52
|
+
private readonly configuredRoots;
|
|
53
|
+
private projectPolicy;
|
|
54
|
+
constructor(ctx: Context, config: AutomationServiceConfig);
|
|
55
|
+
[Service.init](): AsyncGenerator<() => Promise<void>, void, unknown>;
|
|
56
|
+
snapshot(limit?: number): AutomationSnapshot;
|
|
57
|
+
create(input: CreateAutomationJobRequest): Promise<AutomationJob>;
|
|
58
|
+
update(id: string, input: UpdateAutomationJobRequest): Promise<AutomationJob>;
|
|
59
|
+
remove(id: string): Promise<void>;
|
|
60
|
+
setEnabled(id: string, enabled: boolean, expectedVersion?: number): Promise<AutomationJob>;
|
|
61
|
+
trigger(id: string): Promise<AutomationRun>;
|
|
62
|
+
cancel(runId: string, reason?: string): Promise<AutomationRun>;
|
|
63
|
+
registerExecutor(executor: AutomationExecutor): () => void;
|
|
64
|
+
metadata(): Promise<AutomationMeta>;
|
|
65
|
+
private prepareSpec;
|
|
66
|
+
}
|
|
67
|
+
export default AutomationService;
|
|
68
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAcxC,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAElB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAChC,MAAM,YAAY,CAAA;AAMnB,cAAc,WAAW,CAAA;AACzB,cAAc,qBAAqB,CAAA;AACnC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAE/B,eAAO,MAAM,IAAI,gBAAgB,CAAA;AAejC,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;GAQjB,CAAA;AAEF,6DAA6D;AAC7D,qBAAa,iBAAkB,SAAQ,OAAQ,YAAW,oBAAoB;IAC5E,MAAM,CAAC,MAAM;;;;;;;;;;;;;;;;QAAS;IACtB,MAAM,CAAC,MAAM,WAQZ;IAED,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,aAAa,CAA2B;gBAEpC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,uBAAuB;IAclD,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC;IA+B3E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,kBAAkB;IAItC,MAAM,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAKnF,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1F,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI3C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAI9D,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAIpD,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;YAiC3B,WAAW;CAkD1B;AAED,eAAe,iBAAiB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
2
|
+
import { Context, Service } from '@deepseek-ai/cordis';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { expandHomePath, resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
5
|
+
import { HarnessAgentExecutor } from './agent-executor.js';
|
|
6
|
+
import { createAutomationHttpHandler } from './http.js';
|
|
7
|
+
import { ProjectPolicy } from './project-policy.js';
|
|
8
|
+
import { AutomationScheduler } from './scheduler.js';
|
|
9
|
+
import { AutomationStateStore } from './store.js';
|
|
10
|
+
import {} from './types.js';
|
|
11
|
+
import { AutomationInputError, normalizeJobSpec, } from './validation.js';
|
|
12
|
+
export * from './cron.js';
|
|
13
|
+
export * from './project-policy.js';
|
|
14
|
+
export * from './scheduler.js';
|
|
15
|
+
export * from './state.js';
|
|
16
|
+
export * from './store.js';
|
|
17
|
+
export * from './types.js';
|
|
18
|
+
export * from './validation.js';
|
|
19
|
+
export const name = 'automations';
|
|
20
|
+
const API_PREFIX = '/api/automations';
|
|
21
|
+
function resolvedStatePath(config) {
|
|
22
|
+
if (config.statePath !== undefined) {
|
|
23
|
+
const expanded = expandHomePath(config.statePath.trim());
|
|
24
|
+
if (expanded === '' || !isAbsolute(expanded)) {
|
|
25
|
+
throw new Error('automations: statePath must be an absolute path (a leading ~ is supported)');
|
|
26
|
+
}
|
|
27
|
+
return resolve(expanded);
|
|
28
|
+
}
|
|
29
|
+
return join(resolveDshHome(config.dshHome), 'automations', 'state.json');
|
|
30
|
+
}
|
|
31
|
+
export const Config = z.object({
|
|
32
|
+
statePath: z.string(),
|
|
33
|
+
dshHome: z.string(),
|
|
34
|
+
maxConcurrentRuns: z.number().min(1).max(32).default(2),
|
|
35
|
+
historyLimit: z.number().min(10).max(10_000).default(1_000),
|
|
36
|
+
misfireGraceMs: z.number().min(0).max(86_400_000).default(60_000),
|
|
37
|
+
maxOutputChars: z.number().min(1_024).max(1_048_576).default(65_536),
|
|
38
|
+
allowedProjectRoots: z.array(z.string()).default([]),
|
|
39
|
+
});
|
|
40
|
+
/** Durable cron automation service and executor registry. */
|
|
41
|
+
export class AutomationService extends Service {
|
|
42
|
+
static Config = Config;
|
|
43
|
+
static inject = [
|
|
44
|
+
'agents',
|
|
45
|
+
'sessions',
|
|
46
|
+
'agentDefaultModel',
|
|
47
|
+
'agentPresets',
|
|
48
|
+
'permissionPresets',
|
|
49
|
+
'llm',
|
|
50
|
+
'webServer',
|
|
51
|
+
];
|
|
52
|
+
statePath;
|
|
53
|
+
store;
|
|
54
|
+
scheduler;
|
|
55
|
+
configuredRoots;
|
|
56
|
+
projectPolicy;
|
|
57
|
+
constructor(ctx, config) {
|
|
58
|
+
super(ctx, 'automations');
|
|
59
|
+
this.statePath = resolvedStatePath(config);
|
|
60
|
+
this.configuredRoots = config.allowedProjectRoots ?? [];
|
|
61
|
+
this.store = new AutomationStateStore(this.statePath, config.historyLimit ?? 1_000);
|
|
62
|
+
this.scheduler = new AutomationScheduler({
|
|
63
|
+
store: this.store,
|
|
64
|
+
maxConcurrentRuns: config.maxConcurrentRuns ?? 2,
|
|
65
|
+
misfireGraceMs: config.misfireGraceMs ?? 60_000,
|
|
66
|
+
maxOutputChars: config.maxOutputChars ?? 65_536,
|
|
67
|
+
logger: ctx.logger,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async *[Service.init]() {
|
|
71
|
+
this.projectPolicy = await ProjectPolicy.create(this.configuredRoots);
|
|
72
|
+
await this.store.open(new Date());
|
|
73
|
+
const unregisterExecutor = this.scheduler.registerExecutor(new HarnessAgentExecutor(this.ctx, this.projectPolicy));
|
|
74
|
+
let unregisterRoute;
|
|
75
|
+
try {
|
|
76
|
+
await this.scheduler.start();
|
|
77
|
+
unregisterRoute = this.ctx.webServer.register({
|
|
78
|
+
kind: 'prefix',
|
|
79
|
+
path: API_PREFIX,
|
|
80
|
+
handler: createAutomationHttpHandler(this, API_PREFIX, this.ctx.logger),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
await this.scheduler.stop().catch(() => undefined);
|
|
85
|
+
unregisterExecutor();
|
|
86
|
+
await this.store.close().catch(() => undefined);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
yield async () => {
|
|
90
|
+
unregisterRoute?.();
|
|
91
|
+
try {
|
|
92
|
+
await this.scheduler.stop();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
unregisterExecutor();
|
|
96
|
+
await this.store.close();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
snapshot(limit) {
|
|
101
|
+
return this.scheduler.snapshot(limit);
|
|
102
|
+
}
|
|
103
|
+
async create(input) {
|
|
104
|
+
const spec = await this.prepareSpec(input.spec);
|
|
105
|
+
return this.scheduler.create(spec, input.id);
|
|
106
|
+
}
|
|
107
|
+
async update(id, input) {
|
|
108
|
+
const spec = await this.prepareSpec(input.spec);
|
|
109
|
+
return this.scheduler.update(id, spec, input.expectedVersion);
|
|
110
|
+
}
|
|
111
|
+
remove(id) {
|
|
112
|
+
return this.scheduler.remove(id);
|
|
113
|
+
}
|
|
114
|
+
setEnabled(id, enabled, expectedVersion) {
|
|
115
|
+
return this.scheduler.setEnabled(id, enabled, expectedVersion);
|
|
116
|
+
}
|
|
117
|
+
trigger(id) {
|
|
118
|
+
return this.scheduler.trigger(id);
|
|
119
|
+
}
|
|
120
|
+
cancel(runId, reason) {
|
|
121
|
+
return this.scheduler.cancel(runId, reason);
|
|
122
|
+
}
|
|
123
|
+
registerExecutor(executor) {
|
|
124
|
+
return this.scheduler.registerExecutor(executor);
|
|
125
|
+
}
|
|
126
|
+
async metadata() {
|
|
127
|
+
const selection = this.ctx.agentDefaultModel.currentSelection();
|
|
128
|
+
const providers = await Promise.all(this.ctx.llm.listProviders().map(async (provider) => {
|
|
129
|
+
try {
|
|
130
|
+
const models = await this.ctx.llm.listModels(provider.id);
|
|
131
|
+
return { id: provider.id, models: models.map((model) => model.id) };
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
this.ctx.logger.warn('automations: could not list models for provider %s', provider.id);
|
|
135
|
+
this.ctx.logger.warn(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
136
|
+
return { id: provider.id, models: [] };
|
|
137
|
+
}
|
|
138
|
+
}));
|
|
139
|
+
const agentPresets = (await this.ctx.agentPresets.list()).map((preset) => ({
|
|
140
|
+
id: preset.id,
|
|
141
|
+
name: preset.name ?? preset.id,
|
|
142
|
+
...(preset.broken === undefined ? {} : { broken: preset.broken }),
|
|
143
|
+
}));
|
|
144
|
+
return {
|
|
145
|
+
defaultModel: {
|
|
146
|
+
provider: selection.provider,
|
|
147
|
+
model: selection.model,
|
|
148
|
+
...(selection.reasoningEffort === undefined
|
|
149
|
+
? {}
|
|
150
|
+
: { reasoningEffort: String(selection.reasoningEffort) }),
|
|
151
|
+
},
|
|
152
|
+
providers,
|
|
153
|
+
permissionPresets: [...this.ctx.permissionPresets.names],
|
|
154
|
+
agentPresets,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
async prepareSpec(input) {
|
|
158
|
+
let spec;
|
|
159
|
+
try {
|
|
160
|
+
spec = normalizeJobSpec(input);
|
|
161
|
+
}
|
|
162
|
+
catch (cause) {
|
|
163
|
+
if (cause instanceof AutomationInputError)
|
|
164
|
+
throw cause;
|
|
165
|
+
throw new AutomationInputError(cause instanceof Error ? cause.message : String(cause), 'INVALID_INPUT', 400, {
|
|
166
|
+
cause,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const projectPolicy = this.projectPolicy;
|
|
170
|
+
if (projectPolicy === undefined)
|
|
171
|
+
throw new Error('automations: service is not initialized');
|
|
172
|
+
const cwd = await projectPolicy.authorize(spec.execution.cwd);
|
|
173
|
+
try {
|
|
174
|
+
this.ctx.permissionPresets.resolve(spec.execution.permissionPreset);
|
|
175
|
+
}
|
|
176
|
+
catch (cause) {
|
|
177
|
+
throw new AutomationInputError(`unknown permission preset "${spec.execution.permissionPreset}"`, 'UNKNOWN_PERMISSION_PRESET', 400, { cause });
|
|
178
|
+
}
|
|
179
|
+
let preset;
|
|
180
|
+
try {
|
|
181
|
+
preset = await this.ctx.agentPresets.resolve(spec.execution.agentPreset);
|
|
182
|
+
}
|
|
183
|
+
catch (cause) {
|
|
184
|
+
throw new AutomationInputError(spec.execution.agentPreset === undefined
|
|
185
|
+
? 'the default agent preset is unavailable'
|
|
186
|
+
: `unknown agent preset "${spec.execution.agentPreset}"`, 'UNKNOWN_AGENT_PRESET', 400, { cause });
|
|
187
|
+
}
|
|
188
|
+
if (preset.broken !== undefined) {
|
|
189
|
+
throw new AutomationInputError(`agent preset "${preset.id}" is broken: ${preset.broken}`, 'BROKEN_AGENT_PRESET');
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
...spec,
|
|
193
|
+
execution: {
|
|
194
|
+
...spec.execution,
|
|
195
|
+
cwd,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export default AutomationService;
|
|
201
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Host-side allowlist and symlink-canonicalization boundary for job workspaces. */
|
|
2
|
+
export declare class ProjectPolicy {
|
|
3
|
+
private readonly roots;
|
|
4
|
+
private constructor();
|
|
5
|
+
static create(configuredRoots: readonly string[]): Promise<ProjectPolicy>;
|
|
6
|
+
authorize(cwd: string): Promise<string>;
|
|
7
|
+
listRoots(): readonly string[];
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=project-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-policy.d.ts","sourceRoot":"","sources":["../src/project-policy.ts"],"names":[],"mappings":"AA0BA,oFAAoF;AACpF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAEhC,OAAO;WAIM,MAAM,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;IASzE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa7C,SAAS,IAAI,SAAS,MAAM,EAAE;CAG/B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
+
import { expandHomePath } from '@deepseek-ai/dsh-home-paths';
|
|
4
|
+
import { AutomationInputError } from './validation.js';
|
|
5
|
+
function isWithin(root, candidate) {
|
|
6
|
+
const path = relative(root, candidate);
|
|
7
|
+
return path === '' || (!path.startsWith('..') && !isAbsolute(path));
|
|
8
|
+
}
|
|
9
|
+
async function canonicalDirectory(path, label) {
|
|
10
|
+
let metadata;
|
|
11
|
+
let canonical;
|
|
12
|
+
try {
|
|
13
|
+
;
|
|
14
|
+
[metadata, canonical] = await Promise.all([stat(path), realpath(path)]);
|
|
15
|
+
}
|
|
16
|
+
catch (cause) {
|
|
17
|
+
throw new AutomationInputError(`${label} is not an accessible directory: ${path}`, 'PROJECT_UNAVAILABLE', 400, {
|
|
18
|
+
cause,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (!metadata.isDirectory()) {
|
|
22
|
+
throw new AutomationInputError(`${label} is not a directory: ${path}`, 'PROJECT_NOT_DIRECTORY');
|
|
23
|
+
}
|
|
24
|
+
return canonical;
|
|
25
|
+
}
|
|
26
|
+
/** Host-side allowlist and symlink-canonicalization boundary for job workspaces. */
|
|
27
|
+
export class ProjectPolicy {
|
|
28
|
+
roots;
|
|
29
|
+
constructor(roots) {
|
|
30
|
+
this.roots = roots;
|
|
31
|
+
}
|
|
32
|
+
static async create(configuredRoots) {
|
|
33
|
+
const roots = [];
|
|
34
|
+
for (const configured of configuredRoots) {
|
|
35
|
+
const path = resolve(expandHomePath(configured));
|
|
36
|
+
roots.push(await canonicalDirectory(path, 'allowed project root'));
|
|
37
|
+
}
|
|
38
|
+
return new ProjectPolicy([...new Set(roots)]);
|
|
39
|
+
}
|
|
40
|
+
async authorize(cwd) {
|
|
41
|
+
if (!isAbsolute(cwd))
|
|
42
|
+
throw new AutomationInputError('project cwd must be absolute');
|
|
43
|
+
const canonical = await canonicalDirectory(cwd, 'project cwd');
|
|
44
|
+
if (this.roots.length > 0 && !this.roots.some((root) => isWithin(root, canonical))) {
|
|
45
|
+
throw new AutomationInputError(`project cwd is outside allowedProjectRoots: ${canonical}`, 'PROJECT_NOT_ALLOWED', 403);
|
|
46
|
+
}
|
|
47
|
+
return canonical;
|
|
48
|
+
}
|
|
49
|
+
listRoots() {
|
|
50
|
+
return this.roots;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=project-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-policy.js","sourceRoot":"","sources":["../src/project-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAEtD,SAAS,QAAQ,CAAC,IAAY,EAAE,SAAiB;IAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IACtC,OAAO,IAAI,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;AACrE,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAa;IAC3D,IAAI,QAAQ,CAAA;IACZ,IAAI,SAAS,CAAA;IACb,IAAI,CAAC;QACH,CAAC;QAAA,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,oCAAoC,IAAI,EAAE,EAAE,qBAAqB,EAAE,GAAG,EAAE;YAC7G,KAAK;SACN,CAAC,CAAA;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,oBAAoB,CAAC,GAAG,KAAK,wBAAwB,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAA;IACjG,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,oFAAoF;AACpF,MAAM,OAAO,aAAa;IACP,KAAK,CAAU;IAEhC,YAAoB,KAAe;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,eAAkC;QACpD,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,KAAK,MAAM,UAAU,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;YAChD,KAAK,CAAC,IAAI,CAAC,MAAM,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAA;QACpE,CAAC;QACD,OAAO,IAAI,aAAa,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAC/C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,oBAAoB,CAAC,8BAA8B,CAAC,CAAA;QACpF,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;QAC9D,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;YACnF,MAAM,IAAI,oBAAoB,CAC5B,+CAA+C,SAAS,EAAE,EAC1D,qBAAqB,EACrB,GAAG,CACJ,CAAA;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AutomationStateStore } from './store.js';
|
|
2
|
+
import type { AutomationExecutor, AutomationJob, AutomationJobSpec, AutomationRun, AutomationSnapshot, SchedulerClock } from './types.js';
|
|
3
|
+
interface SchedulerLogger {
|
|
4
|
+
debug(message: string, ...args: unknown[]): void;
|
|
5
|
+
info(message: string, ...args: unknown[]): void;
|
|
6
|
+
warn(message: string, ...args: unknown[]): void;
|
|
7
|
+
error(message: string, ...args: unknown[]): void;
|
|
8
|
+
}
|
|
9
|
+
interface SchedulerOptions {
|
|
10
|
+
store: AutomationStateStore;
|
|
11
|
+
maxConcurrentRuns: number;
|
|
12
|
+
misfireGraceMs: number;
|
|
13
|
+
maxOutputChars: number;
|
|
14
|
+
logger: SchedulerLogger;
|
|
15
|
+
clock?: SchedulerClock;
|
|
16
|
+
}
|
|
17
|
+
export declare class AutomationScheduler {
|
|
18
|
+
private readonly store;
|
|
19
|
+
private readonly maxConcurrentRuns;
|
|
20
|
+
private readonly misfireGraceMs;
|
|
21
|
+
private readonly maxOutputChars;
|
|
22
|
+
private readonly logger;
|
|
23
|
+
private readonly clock;
|
|
24
|
+
private readonly executors;
|
|
25
|
+
private readonly active;
|
|
26
|
+
private timer;
|
|
27
|
+
private drivePromise;
|
|
28
|
+
private driveRequested;
|
|
29
|
+
private started;
|
|
30
|
+
private stopping;
|
|
31
|
+
constructor(options: SchedulerOptions);
|
|
32
|
+
registerExecutor(executor: AutomationExecutor): () => void;
|
|
33
|
+
start(): Promise<void>;
|
|
34
|
+
stop(): Promise<void>;
|
|
35
|
+
snapshot(limit?: number): AutomationSnapshot;
|
|
36
|
+
create(spec: AutomationJobSpec, requestedId?: string): Promise<AutomationJob>;
|
|
37
|
+
update(idValue: string, spec: AutomationJobSpec, expectedVersion?: number): Promise<AutomationJob>;
|
|
38
|
+
setEnabled(idValue: string, enabled: boolean, expectedVersion?: number): Promise<AutomationJob>;
|
|
39
|
+
remove(idValue: string): Promise<void>;
|
|
40
|
+
trigger(idValue: string): Promise<AutomationRun>;
|
|
41
|
+
cancel(runId: string, reason?: string): Promise<AutomationRun>;
|
|
42
|
+
private allocateJobId;
|
|
43
|
+
private reconcileJobs;
|
|
44
|
+
private requestDrive;
|
|
45
|
+
private driveLoop;
|
|
46
|
+
private claimDueRuns;
|
|
47
|
+
private claimDueRun;
|
|
48
|
+
private admitRun;
|
|
49
|
+
private admitSkippedRun;
|
|
50
|
+
private drainQueue;
|
|
51
|
+
private executeRun;
|
|
52
|
+
private finishRun;
|
|
53
|
+
private armNext;
|
|
54
|
+
private arm;
|
|
55
|
+
private clearTimer;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAOjD,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,aAAa,EAIb,kBAAkB,EAClB,cAAc,EACf,MAAM,YAAY,CAAA;AAWnB,UAAU,eAAe;IACvB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CACjD;AAED,UAAU,gBAAgB;IACxB,KAAK,EAAE,oBAAoB,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;IACvB,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB;AAyED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,KAAK,CAA2C;IACxD,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,OAAO,EAAE,gBAAgB;IASrC,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAcpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,QAAQ,CAAC,KAAK,SAAM,GAAG,kBAAkB;IASnC,MAAM,CAAC,IAAI,EAAE,iBAAiB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA6B7E,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA2BlG,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAO/F,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBtC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAYhD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAA2B,GAAG,OAAO,CAAC,aAAa,CAAC;IAoBtF,OAAO,CAAC,aAAa;YAUP,aAAa;IAsB3B,OAAO,CAAC,YAAY;YAUN,SAAS;YAgBT,YAAY;YAQZ,WAAW;IAyBzB,OAAO,CAAC,QAAQ;IA6BhB,OAAO,CAAC,eAAe;YA6BT,UAAU;YA+CV,UAAU;YA0CV,SAAS;IAUvB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,GAAG;IAUX,OAAO,CAAC,UAAU;CAKnB"}
|