@pasko70/pibo 1.7.11 → 1.8.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/dist/core/context-guard.js +172 -5
- package/dist/core/provider-telemetry.js +97 -4
- package/dist/core/routed-session.js +110 -16
- package/dist/core/runtime-telemetry.js +254 -94
- package/dist/core/runtime.js +72 -11
- package/dist/core/session-errors.js +16 -0
- package/dist/core/session-router.js +114 -21
- package/dist/data/telemetry.js +115 -26
- package/dist/gateway/cli.js +35 -16
- package/dist/gateway/pidfile.js +90 -44
- package/dist/gateway/server.js +22 -16
- package/dist/gateway/web.js +29 -23
- package/dist/ralph/service.js +60 -20
- package/dist/ralph/store.js +1 -1
- package/package.json +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.7.11.vsix +0 -0
package/dist/gateway/pidfile.js
CHANGED
|
@@ -1,84 +1,130 @@
|
|
|
1
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { getPiboHome, piboHomePath } from "../core/pibo-home.js";
|
|
4
|
+
function gatewayPidPath() {
|
|
5
|
+
return piboHomePath("gateway.pid");
|
|
5
6
|
}
|
|
6
7
|
function fallbackGatewayPidPath() {
|
|
7
8
|
return piboHomePath("gateway-fallback.pid");
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
function legacyGatewayPidPaths() {
|
|
11
|
+
try {
|
|
12
|
+
const home = getPiboHome();
|
|
13
|
+
return readdirSync(home)
|
|
14
|
+
.filter((name) => /^gateway-\d+\.pid$/.test(name))
|
|
15
|
+
.map((name) => join(home, name));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function storedPid(path) {
|
|
10
22
|
try {
|
|
11
|
-
const path = gatewayPidPath(port);
|
|
12
23
|
if (!existsSync(path))
|
|
13
24
|
return undefined;
|
|
14
25
|
const pid = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
return Number.isNaN(pid) ? undefined : pid;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function livePid(path) {
|
|
33
|
+
const pid = storedPid(path);
|
|
34
|
+
if (pid === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
try {
|
|
37
|
+
process.kill(pid, 0);
|
|
38
|
+
return pid;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error.code === "EPERM")
|
|
19
42
|
return pid;
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function claimPidFile(path, label) {
|
|
47
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
48
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
49
|
+
try {
|
|
50
|
+
writeFileSync(path, String(process.pid), { encoding: "utf-8", flag: "wx" });
|
|
51
|
+
return;
|
|
20
52
|
}
|
|
21
|
-
catch {
|
|
22
|
-
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code !== "EEXIST")
|
|
55
|
+
throw error;
|
|
56
|
+
const existingPid = livePid(path);
|
|
57
|
+
if (existingPid === process.pid)
|
|
58
|
+
return;
|
|
59
|
+
if (existingPid !== undefined)
|
|
60
|
+
throw new Error(`${label} already running (PID ${existingPid})`);
|
|
61
|
+
try {
|
|
62
|
+
unlinkSync(path);
|
|
63
|
+
}
|
|
64
|
+
catch (unlinkError) {
|
|
65
|
+
if (unlinkError.code !== "ENOENT")
|
|
66
|
+
throw unlinkError;
|
|
67
|
+
}
|
|
23
68
|
}
|
|
24
69
|
}
|
|
70
|
+
throw new Error(`Unable to claim ${label.toLowerCase()} PID file`);
|
|
71
|
+
}
|
|
72
|
+
function clearPidFileIfOwned(path) {
|
|
73
|
+
try {
|
|
74
|
+
if (storedPid(path) === process.pid)
|
|
75
|
+
unlinkSync(path);
|
|
76
|
+
}
|
|
25
77
|
catch {
|
|
26
|
-
|
|
78
|
+
// ignore
|
|
27
79
|
}
|
|
28
80
|
}
|
|
29
|
-
export function
|
|
81
|
+
export function readPidFile() {
|
|
82
|
+
return livePid(gatewayPidPath()) ?? legacyGatewayPidPaths().map(livePid).find((pid) => pid !== undefined);
|
|
83
|
+
}
|
|
84
|
+
export function clearPidFile() {
|
|
30
85
|
try {
|
|
31
|
-
const path = gatewayPidPath(
|
|
32
|
-
if (existsSync(path))
|
|
86
|
+
const path = gatewayPidPath();
|
|
87
|
+
if (existsSync(path))
|
|
33
88
|
unlinkSync(path);
|
|
34
|
-
}
|
|
35
89
|
}
|
|
36
90
|
catch {
|
|
37
91
|
// ignore
|
|
38
92
|
}
|
|
39
93
|
}
|
|
40
|
-
export function
|
|
41
|
-
|
|
94
|
+
export function releaseGatewayPid() {
|
|
95
|
+
clearPidFileIfOwned(gatewayPidPath());
|
|
96
|
+
}
|
|
97
|
+
export function writeGatewayPid() {
|
|
98
|
+
const existingPid = readPidFile();
|
|
42
99
|
if (existingPid !== undefined && existingPid !== process.pid) {
|
|
43
100
|
throw new Error(`Gateway already running (PID ${existingPid})`);
|
|
44
101
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const path = fallbackGatewayPidPath();
|
|
50
|
-
if (!existsSync(path))
|
|
51
|
-
return undefined;
|
|
52
|
-
const pid = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
53
|
-
if (Number.isNaN(pid))
|
|
54
|
-
return undefined;
|
|
102
|
+
for (const path of legacyGatewayPidPaths()) {
|
|
103
|
+
if (livePid(path) !== undefined)
|
|
104
|
+
continue;
|
|
55
105
|
try {
|
|
56
|
-
|
|
57
|
-
return pid;
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return undefined;
|
|
106
|
+
unlinkSync(path);
|
|
61
107
|
}
|
|
108
|
+
catch { }
|
|
62
109
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
110
|
+
claimPidFile(gatewayPidPath(), "Gateway");
|
|
111
|
+
}
|
|
112
|
+
export function readFallbackPidFile() {
|
|
113
|
+
return livePid(fallbackGatewayPidPath());
|
|
66
114
|
}
|
|
67
115
|
export function clearFallbackPidFile() {
|
|
68
116
|
try {
|
|
69
117
|
const path = fallbackGatewayPidPath();
|
|
70
|
-
if (existsSync(path))
|
|
118
|
+
if (existsSync(path))
|
|
71
119
|
unlinkSync(path);
|
|
72
|
-
}
|
|
73
120
|
}
|
|
74
121
|
catch {
|
|
75
122
|
// ignore
|
|
76
123
|
}
|
|
77
124
|
}
|
|
125
|
+
export function releaseFallbackGatewayPid() {
|
|
126
|
+
clearPidFileIfOwned(fallbackGatewayPidPath());
|
|
127
|
+
}
|
|
78
128
|
export function writeFallbackGatewayPid() {
|
|
79
|
-
|
|
80
|
-
if (existingPid !== undefined && existingPid !== process.pid) {
|
|
81
|
-
throw new Error(`Fallback gateway already running (PID ${existingPid})`);
|
|
82
|
-
}
|
|
83
|
-
writeFileSync(fallbackGatewayPidPath(), String(process.pid), "utf-8");
|
|
129
|
+
claimPidFile(fallbackGatewayPidPath(), "Fallback gateway");
|
|
84
130
|
}
|
package/dist/gateway/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createDefaultPiboPluginRegistry, createPiboProfileFromRegistryOrDefault
|
|
|
3
3
|
import { PiboSessionRouter } from "../core/session-router.js";
|
|
4
4
|
import { loadPiboModelDefaults, selectRequestedModelProfile } from "../core/model-defaults.js";
|
|
5
5
|
import { DEFAULT_GATEWAY_HOST, DEFAULT_GATEWAY_PORT, encodeFrame, errorResponse, isGatewayRequestFrame, isGatewaySubscribeFrame, } from "./protocol.js";
|
|
6
|
-
import {
|
|
6
|
+
import { releaseFallbackGatewayPid, releaseGatewayPid, writeFallbackGatewayPid, writeGatewayPid } from "./pidfile.js";
|
|
7
7
|
const DEFAULT_MAX_BACKPRESSURE_FRAMES = 1_000;
|
|
8
8
|
const DEFAULT_MAX_BACKPRESSURE_BYTES = 4 * 1024 * 1024;
|
|
9
9
|
function parseJsonLine(line) {
|
|
@@ -342,31 +342,37 @@ export class PiboGatewayServer {
|
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
export async function runGatewayServer(options = {}) {
|
|
345
|
-
const
|
|
346
|
-
await server.start();
|
|
345
|
+
const fallbackMode = process.env.PIBO_FALLBACK_MODE === "1";
|
|
347
346
|
try {
|
|
348
|
-
if (
|
|
347
|
+
if (fallbackMode)
|
|
349
348
|
writeFallbackGatewayPid();
|
|
350
|
-
|
|
351
|
-
else {
|
|
349
|
+
else
|
|
352
350
|
writeGatewayPid();
|
|
353
|
-
}
|
|
354
351
|
}
|
|
355
|
-
catch (
|
|
356
|
-
console.error(
|
|
357
|
-
|
|
358
|
-
|
|
352
|
+
catch (error) {
|
|
353
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
354
|
+
process.exitCode = 1;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const releasePid = fallbackMode ? releaseFallbackGatewayPid : releaseGatewayPid;
|
|
358
|
+
let server;
|
|
359
|
+
try {
|
|
360
|
+
server = new PiboGatewayServer(options);
|
|
361
|
+
await server.start();
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
releasePid();
|
|
365
|
+
throw error;
|
|
359
366
|
}
|
|
360
367
|
const host = options.host ?? DEFAULT_GATEWAY_HOST;
|
|
361
368
|
const port = options.port ?? DEFAULT_GATEWAY_PORT;
|
|
362
369
|
console.error(`pibo gateway listening on ${host}:${port}`);
|
|
363
370
|
const stop = async () => {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
clearFallbackPidFile();
|
|
371
|
+
try {
|
|
372
|
+
await server.stop();
|
|
367
373
|
}
|
|
368
|
-
|
|
369
|
-
|
|
374
|
+
finally {
|
|
375
|
+
releasePid();
|
|
370
376
|
}
|
|
371
377
|
};
|
|
372
378
|
process.once("SIGINT", () => {
|
package/dist/gateway/web.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createPiboWebHostPlugin } from "../plugins/web.js";
|
|
|
13
13
|
import { DEFAULT_WEB_CHANNEL_HOST, DEFAULT_WEB_CHANNEL_PORT } from "../web/channel.js";
|
|
14
14
|
import { loadPiboConfig } from "../config/config.js";
|
|
15
15
|
import { PiboGatewayServer } from "./server.js";
|
|
16
|
-
import {
|
|
16
|
+
import { releaseFallbackGatewayPid, releaseGatewayPid, writeFallbackGatewayPid, writeGatewayPid } from "./pidfile.js";
|
|
17
17
|
const PUBLIC_WEB_CHANNEL_HOST = "0.0.0.0";
|
|
18
18
|
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
19
19
|
function isComputeWorkerRuntime() {
|
|
@@ -175,37 +175,43 @@ function createChatAppURL(options, host, port) {
|
|
|
175
175
|
return `http://${host}:${port}/apps/chat`;
|
|
176
176
|
}
|
|
177
177
|
export async function runWebGatewayServer(options = {}) {
|
|
178
|
-
const
|
|
179
|
-
const pluginRegistry = resolvedOptions.pluginRegistry ?? createWebPiboPluginRegistry(resolvedOptions);
|
|
180
|
-
const pidFilePort = resolvedOptions.port;
|
|
181
|
-
const server = new PiboGatewayServer({
|
|
182
|
-
...resolvedOptions,
|
|
183
|
-
pluginRegistry,
|
|
184
|
-
});
|
|
185
|
-
await server.start();
|
|
178
|
+
const fallbackMode = process.env.PIBO_FALLBACK_MODE === "1";
|
|
186
179
|
try {
|
|
187
|
-
if (
|
|
180
|
+
if (fallbackMode)
|
|
188
181
|
writeFallbackGatewayPid();
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
writeGatewayPid(pidFilePort);
|
|
192
|
-
}
|
|
182
|
+
else
|
|
183
|
+
writeGatewayPid();
|
|
193
184
|
}
|
|
194
|
-
catch (
|
|
195
|
-
console.error(
|
|
196
|
-
|
|
197
|
-
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
187
|
+
process.exitCode = 1;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const releasePid = fallbackMode ? releaseFallbackGatewayPid : releaseGatewayPid;
|
|
191
|
+
let resolvedOptions;
|
|
192
|
+
let server;
|
|
193
|
+
try {
|
|
194
|
+
resolvedOptions = resolveWebGatewayServerOptions(options);
|
|
195
|
+
const pluginRegistry = resolvedOptions.pluginRegistry ?? createWebPiboPluginRegistry(resolvedOptions);
|
|
196
|
+
server = new PiboGatewayServer({
|
|
197
|
+
...resolvedOptions,
|
|
198
|
+
pluginRegistry,
|
|
199
|
+
});
|
|
200
|
+
await server.start();
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
releasePid();
|
|
204
|
+
throw error;
|
|
198
205
|
}
|
|
199
206
|
const host = resolvedOptions.web?.host ?? DEFAULT_WEB_CHANNEL_HOST;
|
|
200
207
|
const port = resolvedOptions.web?.port ?? DEFAULT_WEB_CHANNEL_PORT;
|
|
201
208
|
console.error(`pibo chat app available at ${createChatAppURL(resolvedOptions, host, port)}`);
|
|
202
209
|
const stop = async () => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
clearFallbackPidFile();
|
|
210
|
+
try {
|
|
211
|
+
await server.stop();
|
|
206
212
|
}
|
|
207
|
-
|
|
208
|
-
|
|
213
|
+
finally {
|
|
214
|
+
releasePid();
|
|
209
215
|
}
|
|
210
216
|
};
|
|
211
217
|
process.once("SIGINT", () => {
|
package/dist/ralph/service.js
CHANGED
|
@@ -10,6 +10,14 @@ import { createDefaultPiboRalphStore } from './store.js';
|
|
|
10
10
|
import { createBuiltInRalphStopConditions, evaluateRalphStopPolicy } from './stopping.js';
|
|
11
11
|
const CHAT_WEB_CHANNEL = 'pibo.chat-web';
|
|
12
12
|
function errorMessage(error) { return error instanceof Error ? error.message : String(error); }
|
|
13
|
+
class RalphRunTimeoutError extends Error {
|
|
14
|
+
abortFailed;
|
|
15
|
+
constructor(message, abortFailed = false) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.abortFailed = abortFailed;
|
|
18
|
+
this.name = 'RalphRunTimeoutError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
function isUnknownProfileErrorMessage(message) { return /^Unknown profile "[^"]+"/.test(message); }
|
|
14
22
|
function buildRalphPrompt(job) { return ['You are running a continuous Pibo Ralph job.', `Job: ${job.name}`, `Target: ${job.target.kind}`, '', 'Complete the task below. Return the result in this session. When this session finishes, Ralph may start a fresh session with the same task unless a configured stop condition is satisfied.', 'Important: if this job uses a promise-complete stop condition, do not quote, negate, explain, or mention its literal completion marker unless the task is fully complete and you intend to stop the job.', '', 'Task:', job.prompt].join('\n'); }
|
|
15
23
|
function isJsonObject(value) { return !!value && typeof value === 'object' && !Array.isArray(value); }
|
|
@@ -137,9 +145,10 @@ export class PiboRalphService {
|
|
|
137
145
|
const cancelled = this.cancelledRuns.delete(run.id);
|
|
138
146
|
const message = errorMessage(error);
|
|
139
147
|
const fatalProfileError = !cancelled && isUnknownProfileErrorMessage(message);
|
|
148
|
+
const timeoutAbortFailed = error instanceof RalphRunTimeoutError && error.abortFailed;
|
|
140
149
|
const outcome = { status: cancelled ? 'cancelled' : 'error', error: cancelled ? undefined : message };
|
|
141
150
|
const { evaluation, conditionStates } = await this.evaluateStopPolicy(this.store.getJob(job.id) ?? job, 'after-run', run, outcome);
|
|
142
|
-
this.store.completeRun({ jobId: job.id, runId: run.id, status: outcome.status, error: outcome.error, reason: cancelled ? 'cancelled' : fatalProfileError ? 'unknown-profile' : evaluation.reason, stopAfterRun: fatalProfileError || evaluation.finalAction !== 'continue', stopEvaluation: evaluation, conditionStates });
|
|
151
|
+
this.store.completeRun({ jobId: job.id, runId: run.id, status: outcome.status, error: outcome.error, reason: cancelled ? 'cancelled' : fatalProfileError ? 'unknown-profile' : timeoutAbortFailed ? 'timeout-abort-failed' : evaluation.reason, stopAfterRun: fatalProfileError || timeoutAbortFailed || evaluation.finalAction !== 'continue', stopEvaluation: evaluation, conditionStates });
|
|
143
152
|
await this.cleanupRunResources(job, run);
|
|
144
153
|
if (!cancelled)
|
|
145
154
|
console.error(`[ralph] job ${job.id} failed`, error);
|
|
@@ -256,24 +265,55 @@ export class PiboRalphService {
|
|
|
256
265
|
} const room = this.roomService.ensureDefaultRoom({ name: 'Shared Chat' }); return { roomId: room.id, workspace: room.workspace ?? getDefaultPiboWorkspace() }; }
|
|
257
266
|
async emitMessageAndWait(piboSessionId, text, options = {}) {
|
|
258
267
|
const eventId = `ralph_msg_${randomUUID()}`;
|
|
259
|
-
return await new Promise((resolve, reject) => {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
return await new Promise((resolve, reject) => {
|
|
269
|
+
let settled = false;
|
|
270
|
+
let deltaAnswer = '';
|
|
271
|
+
let finalAnswer = '';
|
|
272
|
+
let lastSessionError;
|
|
273
|
+
let timingOut = false;
|
|
274
|
+
let unsubscribe;
|
|
275
|
+
let timeout;
|
|
276
|
+
const finish = (error) => {
|
|
277
|
+
if (settled)
|
|
278
|
+
return;
|
|
279
|
+
settled = true;
|
|
280
|
+
if (timeout)
|
|
281
|
+
clearTimeout(timeout);
|
|
282
|
+
unsubscribe?.();
|
|
283
|
+
if (error)
|
|
284
|
+
reject(error);
|
|
285
|
+
else
|
|
286
|
+
resolve(finalAnswer || deltaAnswer);
|
|
287
|
+
};
|
|
288
|
+
if (this.runTimeoutMs !== undefined) {
|
|
289
|
+
timeout = setTimeout(() => {
|
|
290
|
+
timingOut = true;
|
|
291
|
+
const message = lastSessionError ? `Ralph run timed out after session error: ${lastSessionError}` : 'Ralph run timed out';
|
|
292
|
+
void this.options.context.emit({ type: 'execution', piboSessionId, action: 'abort', id: `ralph_timeout_${randomUUID()}` })
|
|
293
|
+
.then(() => finish(new RalphRunTimeoutError(message)), (abortError) => finish(new RalphRunTimeoutError(`${message}; session abort failed: ${errorMessage(abortError)}`, true)));
|
|
294
|
+
}, this.runTimeoutMs);
|
|
295
|
+
}
|
|
296
|
+
unsubscribe = this.options.context.subscribe((event) => {
|
|
297
|
+
if (timingOut || event.piboSessionId !== piboSessionId)
|
|
298
|
+
return;
|
|
299
|
+
if ('eventId' in event && event.eventId !== eventId)
|
|
300
|
+
return;
|
|
301
|
+
if (event.type === 'assistant_delta')
|
|
302
|
+
deltaAnswer += event.text;
|
|
303
|
+
if (event.type === 'assistant_message') {
|
|
304
|
+
finalAnswer = event.text;
|
|
305
|
+
lastSessionError = undefined;
|
|
306
|
+
}
|
|
307
|
+
if (event.type === 'message_finished')
|
|
308
|
+
finish(lastSessionError ? new Error(lastSessionError) : undefined);
|
|
309
|
+
if (event.type === 'session_error') {
|
|
310
|
+
lastSessionError = event.error;
|
|
311
|
+
const providerAttempt = event.errorDetails?.origin === 'provider' && Boolean(event.errorDetails.api || event.errorDetails.provider || event.errorDetails.model);
|
|
312
|
+
if (!providerAttempt || options.isCancelled?.())
|
|
313
|
+
finish(new Error(event.error));
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
this.options.context.emit({ type: 'message', piboSessionId, id: eventId, source: 'service', text }).catch((error) => finish(error instanceof Error ? error : new Error(String(error))));
|
|
317
|
+
});
|
|
278
318
|
}
|
|
279
319
|
}
|
package/dist/ralph/store.js
CHANGED
|
@@ -328,7 +328,7 @@ export class PiboRalphStore {
|
|
|
328
328
|
if (!job)
|
|
329
329
|
return;
|
|
330
330
|
const timestamp = nowIso(now);
|
|
331
|
-
const state = { ...job.state,
|
|
331
|
+
const state = { ...job.state, conditionStates: input.conditionStates ?? job.state.conditionStates, lastStopEvaluation: input.evaluation };
|
|
332
332
|
this.db.prepare('UPDATE pibo_ralph_jobs SET enabled = ?, state_json = ?, updated_at = ? WHERE id = ?').run(input.disable ? 0 : job.enabled ? 1 : 0, JSON.stringify(state), timestamp, job.id);
|
|
333
333
|
}
|
|
334
334
|
completeRun(input, now = new Date()) {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|