@yhyzgn/tikeo 0.1.911
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 +9 -0
- package/dist/client.d.ts +56 -0
- package/dist/client.js +218 -0
- package/dist/config.d.ts +50 -0
- package/dist/config.js +89 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/management.d.ts +45 -0
- package/dist/management.js +63 -0
- package/dist/proto/worker.proto +188 -0
- package/dist/script/index.d.ts +86 -0
- package/dist/script/index.js +316 -0
- package/dist/script/runtimeDirs.d.ts +24 -0
- package/dist/script/runtimeDirs.js +81 -0
- package/dist/script/sandboxTools.d.ts +22 -0
- package/dist/script/sandboxTools.js +133 -0
- package/dist/task.d.ts +18 -0
- package/dist/task.js +18 -0
- package/package.json +44 -0
- package/src/proto/worker.proto +188 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @yhyzgn/tikeo
|
|
2
|
+
|
|
3
|
+
Node.js SDK aligned with the Rust, Go, Java, and Python Worker SDKs.
|
|
4
|
+
|
|
5
|
+
- Worker Tunnel client with structured capabilities.
|
|
6
|
+
- Task processors with precise task-scoped logs.
|
|
7
|
+
- Management API client using `x-tikeo-api-key`.
|
|
8
|
+
- SRT/Deno/container/local script runners and fail-closed unavailable handlers.
|
|
9
|
+
- Default `auto`: `srt` for native scripts, `deno` for JavaScript/TypeScript.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { WorkerCapabilities } from "./config.js";
|
|
2
|
+
import { WorkerConfig } from "./config.js";
|
|
3
|
+
import type { ScriptRunnerRegistry } from "./script/index.js";
|
|
4
|
+
import { type TaskOutcome, type TaskProcessor } from "./task.js";
|
|
5
|
+
export interface Registration {
|
|
6
|
+
clientInstanceId: string;
|
|
7
|
+
namespace: string;
|
|
8
|
+
app: string;
|
|
9
|
+
name: string;
|
|
10
|
+
region: string;
|
|
11
|
+
version: string;
|
|
12
|
+
cluster: string;
|
|
13
|
+
capabilities: string[];
|
|
14
|
+
labels: Record<string, string>;
|
|
15
|
+
structured: WorkerCapabilities;
|
|
16
|
+
}
|
|
17
|
+
export interface Heartbeat {
|
|
18
|
+
workerId: string;
|
|
19
|
+
sequence: number;
|
|
20
|
+
generation: number;
|
|
21
|
+
fencingToken: string;
|
|
22
|
+
sentAt: Date;
|
|
23
|
+
}
|
|
24
|
+
export declare class Client {
|
|
25
|
+
private config;
|
|
26
|
+
private seq;
|
|
27
|
+
private open;
|
|
28
|
+
constructor(config: WorkerConfig);
|
|
29
|
+
registration(): Registration;
|
|
30
|
+
startDryRun(processor: TaskProcessor): void;
|
|
31
|
+
nextHeartbeat(workerId: string, fencingToken: string, generation: number): Heartbeat;
|
|
32
|
+
close(): void;
|
|
33
|
+
connectGrpc(): any;
|
|
34
|
+
connect(): Promise<Session>;
|
|
35
|
+
private registerMessage;
|
|
36
|
+
}
|
|
37
|
+
export declare class Session {
|
|
38
|
+
private call;
|
|
39
|
+
workerId: string;
|
|
40
|
+
leaseSeconds: number;
|
|
41
|
+
generation: number;
|
|
42
|
+
private fencingToken;
|
|
43
|
+
private heartbeatEveryMs;
|
|
44
|
+
private sequence;
|
|
45
|
+
private logSequence;
|
|
46
|
+
constructor(call: any, workerId: string, leaseSeconds: number, generation: number, fencingToken: string, heartbeatEveryMs: number);
|
|
47
|
+
sendHeartbeat(): number;
|
|
48
|
+
startHeartbeat(): () => void;
|
|
49
|
+
emitTaskLog(instanceId: string, assignmentToken: string, level: string, message: string): number;
|
|
50
|
+
processNext(processor: TaskProcessor, scripts?: ScriptRunnerRegistry): Promise<TaskOutcome>;
|
|
51
|
+
close(): void;
|
|
52
|
+
private emitTaskLogSafely;
|
|
53
|
+
}
|
|
54
|
+
export declare function processDispatchTask(processor: TaskProcessor, scripts: ScriptRunnerRegistry | undefined, task: any, log: (level: string, message: string) => void): Promise<TaskOutcome>;
|
|
55
|
+
export declare function printTaskLogLocally(level: string, message: string): void;
|
|
56
|
+
export declare function grpcTarget(endpoint: string): string;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import * as grpc from "@grpc/grpc-js";
|
|
4
|
+
import * as protoLoader from "@grpc/proto-loader";
|
|
5
|
+
import { failed, TaskContext } from "./task.js";
|
|
6
|
+
export class Client {
|
|
7
|
+
config;
|
|
8
|
+
seq = 0;
|
|
9
|
+
open = false;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
config.validate();
|
|
13
|
+
config.normalize();
|
|
14
|
+
}
|
|
15
|
+
registration() {
|
|
16
|
+
return {
|
|
17
|
+
clientInstanceId: this.config.clientInstanceId,
|
|
18
|
+
namespace: this.config.namespace,
|
|
19
|
+
app: this.config.app,
|
|
20
|
+
name: this.config.name,
|
|
21
|
+
region: this.config.region,
|
|
22
|
+
version: this.config.version,
|
|
23
|
+
cluster: this.config.cluster,
|
|
24
|
+
capabilities: [...this.config.capabilities],
|
|
25
|
+
labels: { ...this.config.labels },
|
|
26
|
+
structured: JSON.parse(JSON.stringify(this.config.structured)),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
startDryRun(processor) {
|
|
30
|
+
if (!processor)
|
|
31
|
+
throw new Error("tikeo task processor is required");
|
|
32
|
+
this.open = true;
|
|
33
|
+
}
|
|
34
|
+
nextHeartbeat(workerId, fencingToken, generation) {
|
|
35
|
+
if (!this.open)
|
|
36
|
+
throw new Error("tikeo worker client is not started");
|
|
37
|
+
if (!workerId)
|
|
38
|
+
throw new Error("tikeo worker id is required");
|
|
39
|
+
this.seq += 1;
|
|
40
|
+
return { workerId, sequence: this.seq, generation, fencingToken, sentAt: new Date() };
|
|
41
|
+
}
|
|
42
|
+
close() { this.open = false; }
|
|
43
|
+
connectGrpc() {
|
|
44
|
+
const proto = loadProto();
|
|
45
|
+
return new proto.tikeo.worker.v1.WorkerTunnelService(grpcTarget(this.config.endpoint), grpc.credentials.createInsecure());
|
|
46
|
+
}
|
|
47
|
+
async connect() {
|
|
48
|
+
const client = this.connectGrpc();
|
|
49
|
+
const call = client.OpenTunnel();
|
|
50
|
+
call.write({ register: this.registerMessage() });
|
|
51
|
+
const ack = await nextStreamMessage(call);
|
|
52
|
+
const registered = ack?.registered;
|
|
53
|
+
if (!registered?.workerId)
|
|
54
|
+
throw new Error("tikeo worker expected registration ack");
|
|
55
|
+
return new Session(call, registered.workerId, Number(registered.leaseSeconds ?? 0), Number(registered.generation ?? 0), registered.fencingToken ?? "", this.config.heartbeatEveryMs);
|
|
56
|
+
}
|
|
57
|
+
registerMessage() {
|
|
58
|
+
return {
|
|
59
|
+
clientInstanceId: this.config.clientInstanceId,
|
|
60
|
+
app: this.config.app,
|
|
61
|
+
namespace: this.config.namespace,
|
|
62
|
+
cluster: this.config.cluster,
|
|
63
|
+
region: this.config.region,
|
|
64
|
+
capabilities: [...this.config.capabilities],
|
|
65
|
+
labels: { ...this.config.labels },
|
|
66
|
+
structuredCapabilities: {
|
|
67
|
+
tags: [...this.config.structured.tags],
|
|
68
|
+
sdkProcessors: this.config.structured.sdkProcessors.map((name) => ({ name })),
|
|
69
|
+
scriptRunners: this.config.structured.scriptRunners.map((runner) => ({ language: runner.language, sandboxBackend: runner.sandboxBackend })),
|
|
70
|
+
pluginProcessors: this.config.structured.pluginProcessors.map((plugin) => ({ type: plugin.type, processorNames: [...plugin.processorNames] })),
|
|
71
|
+
},
|
|
72
|
+
election: { enabled: true, priority: 100 },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export class Session {
|
|
77
|
+
call;
|
|
78
|
+
workerId;
|
|
79
|
+
leaseSeconds;
|
|
80
|
+
generation;
|
|
81
|
+
fencingToken;
|
|
82
|
+
heartbeatEveryMs;
|
|
83
|
+
sequence = 0;
|
|
84
|
+
logSequence = 0;
|
|
85
|
+
constructor(call, workerId, leaseSeconds, generation, fencingToken, heartbeatEveryMs) {
|
|
86
|
+
this.call = call;
|
|
87
|
+
this.workerId = workerId;
|
|
88
|
+
this.leaseSeconds = leaseSeconds;
|
|
89
|
+
this.generation = generation;
|
|
90
|
+
this.fencingToken = fencingToken;
|
|
91
|
+
this.heartbeatEveryMs = heartbeatEveryMs;
|
|
92
|
+
}
|
|
93
|
+
sendHeartbeat() {
|
|
94
|
+
this.sequence += 1;
|
|
95
|
+
this.call.write({ heartbeat: { workerId: this.workerId, sequence: this.sequence, generation: this.generation, fencingToken: this.fencingToken } });
|
|
96
|
+
return this.sequence;
|
|
97
|
+
}
|
|
98
|
+
startHeartbeat() {
|
|
99
|
+
this.sendHeartbeat();
|
|
100
|
+
const id = setInterval(() => this.sendHeartbeat(), this.heartbeatEveryMs);
|
|
101
|
+
return () => clearInterval(id);
|
|
102
|
+
}
|
|
103
|
+
emitTaskLog(instanceId, assignmentToken, level, message) {
|
|
104
|
+
this.logSequence += 1;
|
|
105
|
+
this.call.write({ taskLog: { workerId: this.workerId, instanceId, level: level || "info", message, sequence: this.logSequence, assignmentToken } });
|
|
106
|
+
return this.logSequence;
|
|
107
|
+
}
|
|
108
|
+
async processNext(processor, scripts) {
|
|
109
|
+
while (true) {
|
|
110
|
+
const message = await nextStreamMessage(this.call);
|
|
111
|
+
const task = message?.dispatchTask ?? message?.dispatch_task;
|
|
112
|
+
if (!task?.instanceId && !task?.instance_id)
|
|
113
|
+
continue;
|
|
114
|
+
const normalized = normalizeDispatchTask(task);
|
|
115
|
+
this.emitTaskLogSafely(normalized, "info", `received task ${normalized.instanceId} processor=${normalized.processorName}`);
|
|
116
|
+
const outcome = await processDispatchTask(processor, scripts, normalized, (level, msg) => this.emitTaskLogSafely(normalized, level, msg));
|
|
117
|
+
const level = outcome.success ? "info" : "error";
|
|
118
|
+
this.emitTaskLogSafely(normalized, level, `completed task ${normalized.instanceId} success=${outcome.success} message=${outcome.message}`);
|
|
119
|
+
this.call.write({ taskResult: { workerId: this.workerId, instanceId: normalized.instanceId, success: outcome.success, message: outcome.message, assignmentToken: normalized.assignmentToken } });
|
|
120
|
+
return outcome;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
close() {
|
|
124
|
+
this.call.write({ unregister: { workerId: this.workerId, generation: this.generation, fencingToken: this.fencingToken } });
|
|
125
|
+
this.call.end();
|
|
126
|
+
}
|
|
127
|
+
emitTaskLogSafely(task, level, message) {
|
|
128
|
+
printTaskLogLocally(level, message);
|
|
129
|
+
this.emitTaskLog(task.instanceId, task.assignmentToken, level, message);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export async function processDispatchTask(processor, scripts, task, log) {
|
|
133
|
+
try {
|
|
134
|
+
const script = task.processorBinding?.script;
|
|
135
|
+
if (script) {
|
|
136
|
+
const runner = scripts?.get(script.language);
|
|
137
|
+
if (!runner)
|
|
138
|
+
return failed(`script runner is not registered for language: ${script.language}`);
|
|
139
|
+
return await runner.run({
|
|
140
|
+
scriptId: script.scriptId,
|
|
141
|
+
versionId: script.versionId,
|
|
142
|
+
versionNumber: Number(script.versionNumber),
|
|
143
|
+
language: script.language,
|
|
144
|
+
content: script.content,
|
|
145
|
+
contentSha256: script.contentSha256,
|
|
146
|
+
timeoutMs: Number(script.timeoutMs || 30_000),
|
|
147
|
+
maxOutputBytes: Number(script.maxOutputBytes || 1024 * 1024),
|
|
148
|
+
allowNetwork: Boolean(script.allowNetwork),
|
|
149
|
+
allowedEnvVars: script.allowedEnvVars ?? [],
|
|
150
|
+
readOnlyPaths: script.readOnlyPaths ?? [],
|
|
151
|
+
writablePaths: script.writablePaths ?? [],
|
|
152
|
+
secretRefs: script.secretRefs ?? [],
|
|
153
|
+
allowedNetworkHosts: script.allowedNetworkHosts ?? [],
|
|
154
|
+
sandboxBackend: script.sandboxBackend ?? "",
|
|
155
|
+
instanceId: task.instanceId,
|
|
156
|
+
jobId: task.jobId,
|
|
157
|
+
log,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return await processor(new TaskContext(task.instanceId, task.jobId, task.processorName || task.jobId, task.payload ?? new Uint8Array(), log));
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return failed(error.message);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
export function printTaskLogLocally(level, message) {
|
|
167
|
+
const line = `[tikeo-worker] ${message}`;
|
|
168
|
+
if (level.toLowerCase() === "error")
|
|
169
|
+
console.error(line);
|
|
170
|
+
else
|
|
171
|
+
console.log(line);
|
|
172
|
+
}
|
|
173
|
+
export function grpcTarget(endpoint) {
|
|
174
|
+
const value = endpoint.trim();
|
|
175
|
+
if (!value)
|
|
176
|
+
throw new Error("tikeo worker endpoint is required");
|
|
177
|
+
try {
|
|
178
|
+
const url = new URL(value);
|
|
179
|
+
if (url.protocol === "http:" || url.protocol === "https:")
|
|
180
|
+
return url.host;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
function loadProto() {
|
|
188
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
189
|
+
const protoPath = join(here, "proto", "worker.proto");
|
|
190
|
+
const definition = protoLoader.loadSync(protoPath, { keepCase: false, longs: Number, enums: String, defaults: true, oneofs: true });
|
|
191
|
+
return grpc.loadPackageDefinition(definition);
|
|
192
|
+
}
|
|
193
|
+
function nextStreamMessage(call) {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const cleanup = (fn, value) => {
|
|
196
|
+
call.off?.("data", onData);
|
|
197
|
+
call.off?.("error", onError);
|
|
198
|
+
call.off?.("end", onEnd);
|
|
199
|
+
fn(value);
|
|
200
|
+
};
|
|
201
|
+
const onData = (message) => cleanup(resolve, message);
|
|
202
|
+
const onError = (error) => cleanup(reject, error);
|
|
203
|
+
const onEnd = () => cleanup(reject, new Error("worker tunnel closed"));
|
|
204
|
+
call.once("data", onData);
|
|
205
|
+
call.once("error", onError);
|
|
206
|
+
call.once("end", onEnd);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function normalizeDispatchTask(task) {
|
|
210
|
+
return {
|
|
211
|
+
instanceId: task.instanceId ?? task.instance_id,
|
|
212
|
+
jobId: task.jobId ?? task.job_id,
|
|
213
|
+
payload: task.payload ?? new Uint8Array(),
|
|
214
|
+
processorName: task.processorName ?? task.processor_name ?? task.jobId ?? task.job_id,
|
|
215
|
+
processorBinding: task.processorBinding ?? task.processor_binding,
|
|
216
|
+
assignmentToken: task.assignmentToken ?? task.assignment_token ?? "",
|
|
217
|
+
};
|
|
218
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface ScriptRunnerCapability {
|
|
2
|
+
language: string;
|
|
3
|
+
sandboxBackend: string;
|
|
4
|
+
}
|
|
5
|
+
export interface PluginProcessorCapability {
|
|
6
|
+
type: string;
|
|
7
|
+
processorNames: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface WorkerCapabilities {
|
|
10
|
+
tags: string[];
|
|
11
|
+
sdkProcessors: string[];
|
|
12
|
+
scriptRunners: ScriptRunnerCapability[];
|
|
13
|
+
pluginProcessors: PluginProcessorCapability[];
|
|
14
|
+
}
|
|
15
|
+
export interface WorkerConfigInput {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
clientInstanceId: string;
|
|
18
|
+
namespace?: string;
|
|
19
|
+
app?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
region?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
cluster?: string;
|
|
24
|
+
capabilities?: string[];
|
|
25
|
+
labels?: Record<string, string>;
|
|
26
|
+
structured?: Partial<WorkerCapabilities>;
|
|
27
|
+
heartbeatEveryMs?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare class WorkerConfig {
|
|
30
|
+
endpoint: string;
|
|
31
|
+
clientInstanceId: string;
|
|
32
|
+
namespace: string;
|
|
33
|
+
app: string;
|
|
34
|
+
name: string;
|
|
35
|
+
region: string;
|
|
36
|
+
version: string;
|
|
37
|
+
cluster: string;
|
|
38
|
+
capabilities: string[];
|
|
39
|
+
labels: Record<string, string>;
|
|
40
|
+
structured: WorkerCapabilities;
|
|
41
|
+
heartbeatEveryMs: number;
|
|
42
|
+
constructor(input: WorkerConfigInput);
|
|
43
|
+
addTag(tag: string): void;
|
|
44
|
+
addSDKProcessor(name: string): void;
|
|
45
|
+
addScriptRunner(language: string, sandboxBackend: string): void;
|
|
46
|
+
addPluginProcessor(type: string, processorName: string): void;
|
|
47
|
+
validate(): void;
|
|
48
|
+
normalize(): void;
|
|
49
|
+
}
|
|
50
|
+
export declare function localConfig(endpoint: string, clientInstanceId: string): WorkerConfig;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export class WorkerConfig {
|
|
2
|
+
endpoint;
|
|
3
|
+
clientInstanceId;
|
|
4
|
+
namespace;
|
|
5
|
+
app;
|
|
6
|
+
name;
|
|
7
|
+
region;
|
|
8
|
+
version;
|
|
9
|
+
cluster;
|
|
10
|
+
capabilities;
|
|
11
|
+
labels;
|
|
12
|
+
structured;
|
|
13
|
+
heartbeatEveryMs;
|
|
14
|
+
constructor(input) {
|
|
15
|
+
this.endpoint = input.endpoint;
|
|
16
|
+
this.clientInstanceId = input.clientInstanceId;
|
|
17
|
+
this.namespace = input.namespace ?? "default";
|
|
18
|
+
this.app = input.app ?? "default";
|
|
19
|
+
this.name = input.name || input.clientInstanceId;
|
|
20
|
+
this.region = input.region ?? "local";
|
|
21
|
+
this.version = input.version ?? "dev";
|
|
22
|
+
this.cluster = input.cluster ?? "local";
|
|
23
|
+
this.capabilities = [...(input.capabilities ?? [])];
|
|
24
|
+
this.labels = { ...(input.labels ?? {}) };
|
|
25
|
+
this.structured = {
|
|
26
|
+
tags: [...(input.structured?.tags ?? [])],
|
|
27
|
+
sdkProcessors: [...(input.structured?.sdkProcessors ?? [])],
|
|
28
|
+
scriptRunners: [...(input.structured?.scriptRunners ?? [])],
|
|
29
|
+
pluginProcessors: [...(input.structured?.pluginProcessors ?? [])],
|
|
30
|
+
};
|
|
31
|
+
this.heartbeatEveryMs = input.heartbeatEveryMs ?? 10_000;
|
|
32
|
+
}
|
|
33
|
+
addTag(tag) { appendUnique(this.structured.tags, tag); }
|
|
34
|
+
addSDKProcessor(name) { appendUnique(this.structured.sdkProcessors, name); }
|
|
35
|
+
addScriptRunner(language, sandboxBackend) {
|
|
36
|
+
const item = language.trim();
|
|
37
|
+
if (!item || this.structured.scriptRunners.some((runner) => runner.language === item))
|
|
38
|
+
return;
|
|
39
|
+
this.structured.scriptRunners.push({ language: item, sandboxBackend: sandboxBackend.trim() });
|
|
40
|
+
}
|
|
41
|
+
addPluginProcessor(type, processorName) {
|
|
42
|
+
const processorType = type.trim();
|
|
43
|
+
const name = processorName.trim();
|
|
44
|
+
if (!processorType || !name)
|
|
45
|
+
return;
|
|
46
|
+
const existing = this.structured.pluginProcessors.find((plugin) => plugin.type === processorType);
|
|
47
|
+
if (existing)
|
|
48
|
+
appendUnique(existing.processorNames, name);
|
|
49
|
+
else
|
|
50
|
+
this.structured.pluginProcessors.push({ type: processorType, processorNames: [name] });
|
|
51
|
+
}
|
|
52
|
+
validate() {
|
|
53
|
+
const required = {
|
|
54
|
+
"tikeo worker endpoint": this.endpoint,
|
|
55
|
+
"tikeo client instance id": this.clientInstanceId,
|
|
56
|
+
"tikeo worker namespace": this.namespace,
|
|
57
|
+
"tikeo worker app": this.app,
|
|
58
|
+
"tikeo worker name": this.name,
|
|
59
|
+
"tikeo worker cluster": this.cluster,
|
|
60
|
+
};
|
|
61
|
+
for (const [label, value] of Object.entries(required)) {
|
|
62
|
+
if (!value.trim())
|
|
63
|
+
throw new Error(`${label} is required`);
|
|
64
|
+
}
|
|
65
|
+
if (this.heartbeatEveryMs <= 0)
|
|
66
|
+
throw new Error("tikeo heartbeat interval must be positive");
|
|
67
|
+
}
|
|
68
|
+
normalize() {
|
|
69
|
+
this.capabilities = normalized(this.capabilities);
|
|
70
|
+
this.structured.tags = normalized(this.structured.tags);
|
|
71
|
+
this.structured.sdkProcessors = normalized(this.structured.sdkProcessors);
|
|
72
|
+
for (const plugin of this.structured.pluginProcessors)
|
|
73
|
+
plugin.processorNames = normalized(plugin.processorNames);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function localConfig(endpoint, clientInstanceId) {
|
|
77
|
+
return new WorkerConfig({ endpoint, clientInstanceId });
|
|
78
|
+
}
|
|
79
|
+
function appendUnique(values, value) {
|
|
80
|
+
const item = value.trim();
|
|
81
|
+
if (item && !values.includes(item))
|
|
82
|
+
values.push(item);
|
|
83
|
+
}
|
|
84
|
+
function normalized(values) {
|
|
85
|
+
const out = [];
|
|
86
|
+
for (const value of values)
|
|
87
|
+
appendUnique(out, value);
|
|
88
|
+
return out;
|
|
89
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const API_KEY_HEADER = "x-tikeo-api-key";
|
|
2
|
+
export interface JobRetryPolicy {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
maxAttempts: number;
|
|
5
|
+
initialDelaySeconds: number;
|
|
6
|
+
backoffMultiplier: number;
|
|
7
|
+
maxDelaySeconds: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function defaultJobRetryPolicy(): JobRetryPolicy;
|
|
10
|
+
export interface JobDefinition {
|
|
11
|
+
id: string;
|
|
12
|
+
namespace: string;
|
|
13
|
+
app: string;
|
|
14
|
+
name: string;
|
|
15
|
+
scheduleType: string;
|
|
16
|
+
scheduleExpr?: string | null;
|
|
17
|
+
processorName?: string | null;
|
|
18
|
+
processorType?: string | null;
|
|
19
|
+
scriptId?: string | null;
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
retryPolicy?: JobRetryPolicy | null;
|
|
22
|
+
}
|
|
23
|
+
export interface CreateJobRequest {
|
|
24
|
+
name: string;
|
|
25
|
+
scheduleType: string;
|
|
26
|
+
scheduleExpr?: string | null;
|
|
27
|
+
processorName?: string | null;
|
|
28
|
+
processorType?: string | null;
|
|
29
|
+
scriptId?: string | null;
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
retryPolicy?: JobRetryPolicy | null;
|
|
32
|
+
}
|
|
33
|
+
export declare function apiJob(name: string, processorName: string): CreateJobRequest;
|
|
34
|
+
export declare function pluginApiJob(name: string, processorType: string, processorName: string): CreateJobRequest;
|
|
35
|
+
export declare function scriptApiJob(name: string, scriptId: string): CreateJobRequest;
|
|
36
|
+
export declare class ManagementClient {
|
|
37
|
+
private apiKey;
|
|
38
|
+
private namespace;
|
|
39
|
+
private app;
|
|
40
|
+
private endpoint;
|
|
41
|
+
constructor(endpoint: string, apiKey: string, namespace?: string, app?: string);
|
|
42
|
+
listJobs(): Promise<JobDefinition[]>;
|
|
43
|
+
createJob(request: CreateJobRequest): Promise<JobDefinition>;
|
|
44
|
+
private send;
|
|
45
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const API_KEY_HEADER = "x-tikeo-api-key";
|
|
2
|
+
export function defaultJobRetryPolicy() {
|
|
3
|
+
return { enabled: true, maxAttempts: 3, initialDelaySeconds: 5, backoffMultiplier: 2, maxDelaySeconds: 60 };
|
|
4
|
+
}
|
|
5
|
+
export function apiJob(name, processorName) {
|
|
6
|
+
return { name, scheduleType: "api", processorName, enabled: true, retryPolicy: defaultJobRetryPolicy() };
|
|
7
|
+
}
|
|
8
|
+
export function pluginApiJob(name, processorType, processorName) {
|
|
9
|
+
return { name, scheduleType: "api", processorType, processorName, enabled: true, retryPolicy: defaultJobRetryPolicy() };
|
|
10
|
+
}
|
|
11
|
+
export function scriptApiJob(name, scriptId) {
|
|
12
|
+
return { name, scheduleType: "api", scriptId, enabled: true, retryPolicy: defaultJobRetryPolicy() };
|
|
13
|
+
}
|
|
14
|
+
export class ManagementClient {
|
|
15
|
+
apiKey;
|
|
16
|
+
namespace;
|
|
17
|
+
app;
|
|
18
|
+
endpoint;
|
|
19
|
+
constructor(endpoint, apiKey, namespace = "default", app = "default") {
|
|
20
|
+
this.apiKey = apiKey;
|
|
21
|
+
this.namespace = namespace;
|
|
22
|
+
this.app = app;
|
|
23
|
+
this.endpoint = endpoint.trim().replace(/\/$/, "");
|
|
24
|
+
this.namespace = namespace.trim() || "default";
|
|
25
|
+
this.app = app.trim() || "default";
|
|
26
|
+
}
|
|
27
|
+
async listJobs() {
|
|
28
|
+
const data = await this.send("GET", "/jobs");
|
|
29
|
+
const items = Array.isArray(data?.items) ? data.items : [];
|
|
30
|
+
return items.filter((job) => job.namespace === this.namespace && job.app === this.app);
|
|
31
|
+
}
|
|
32
|
+
async createJob(request) {
|
|
33
|
+
const payload = {
|
|
34
|
+
namespace: this.namespace,
|
|
35
|
+
app: this.app,
|
|
36
|
+
name: request.name,
|
|
37
|
+
scheduleType: request.scheduleType,
|
|
38
|
+
scheduleExpr: request.scheduleExpr,
|
|
39
|
+
processorName: request.processorName,
|
|
40
|
+
processorType: request.processorType,
|
|
41
|
+
scriptId: request.scriptId,
|
|
42
|
+
enabled: request.enabled,
|
|
43
|
+
retryPolicy: request.retryPolicy,
|
|
44
|
+
};
|
|
45
|
+
for (const key of Object.keys(payload))
|
|
46
|
+
if (payload[key] === undefined || payload[key] === null)
|
|
47
|
+
delete payload[key];
|
|
48
|
+
return this.send("POST", "/jobs", payload);
|
|
49
|
+
}
|
|
50
|
+
async send(method, path, body) {
|
|
51
|
+
const response = await fetch(`${this.endpoint}/api/v1${path}`, {
|
|
52
|
+
method,
|
|
53
|
+
headers: { accept: "application/json", [API_KEY_HEADER]: this.apiKey, ...(body ? { "content-type": "application/json" } : {}) },
|
|
54
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
55
|
+
});
|
|
56
|
+
const envelope = await response.json();
|
|
57
|
+
if (!response.ok || envelope.code !== 0)
|
|
58
|
+
throw new Error(`tikeo management request failed: status=${response.status} message=${envelope.message ?? ""}`);
|
|
59
|
+
if (envelope.data === undefined || envelope.data === null)
|
|
60
|
+
throw new Error("tikeo management response data was null");
|
|
61
|
+
return envelope.data;
|
|
62
|
+
}
|
|
63
|
+
}
|