@monotykamary/localterm-server 1.21.0 → 1.23.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/automation-scheduler.d.ts.map +1 -1
- package/dist/automation-scheduler.js +5 -1
- package/dist/automation-scheduler.js.map +1 -1
- package/dist/automation-store.d.ts.map +1 -1
- package/dist/automation-store.js +29 -12
- package/dist/automation-store.js.map +1 -1
- package/dist/caffeinate-manager.d.ts +42 -0
- package/dist/caffeinate-manager.d.ts.map +1 -0
- package/dist/caffeinate-manager.js +158 -0
- package/dist/caffeinate-manager.js.map +1 -0
- package/dist/caffeinate-preferences-store.d.ts +13 -0
- package/dist/caffeinate-preferences-store.d.ts.map +1 -0
- package/dist/caffeinate-preferences-store.js +98 -0
- package/dist/caffeinate-preferences-store.js.map +1 -0
- package/dist/caffeinate-process-match.d.ts +10 -0
- package/dist/caffeinate-process-match.d.ts.map +1 -0
- package/dist/caffeinate-process-match.js +76 -0
- package/dist/caffeinate-process-match.js.map +1 -0
- package/dist/constants.d.ts +7 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +27 -3
- package/dist/constants.js.map +1 -1
- package/dist/folder-watch-manager.d.ts +33 -0
- package/dist/folder-watch-manager.d.ts.map +1 -0
- package/dist/folder-watch-manager.js +129 -0
- package/dist/folder-watch-manager.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -23
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +3 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -2
- package/dist/protocol.js.map +1 -1
- package/dist/schemas.d.ts +612 -185
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +112 -25
- package/dist/schemas.js.map +1 -1
- package/dist/session-registry.d.ts +1 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +6 -0
- package/dist/session-registry.js.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/compile-schedule.d.ts +4 -1
- package/dist/utils/compile-schedule.d.ts.map +1 -1
- package/dist/utils/compile-schedule.js +14 -0
- package/dist/utils/compile-schedule.js.map +1 -1
- package/dist/utils/compute-next-automation-run-at.d.ts.map +1 -1
- package/dist/utils/compute-next-automation-run-at.js +4 -1
- package/dist/utils/compute-next-automation-run-at.js.map +1 -1
- package/dist/utils/reconcile-downtime.d.ts.map +1 -1
- package/dist/utils/reconcile-downtime.js +4 -1
- package/dist/utils/reconcile-downtime.js.map +1 -1
- package/package.json +1 -1
package/dist/constants.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export declare const COLORTERM_VALUE = "truecolor";
|
|
|
9
9
|
export declare const LOCALTERM_VALUE = "1";
|
|
10
10
|
export declare const CAFFEINATE_BINARY = "caffeinate";
|
|
11
11
|
export declare const CAFFEINATE_ARGS: readonly string[];
|
|
12
|
+
export declare const CAFFEINATE_AUTO_DEFAULT_COMMANDS: readonly string[];
|
|
13
|
+
export declare const CAFFEINATE_PREFERENCES_FILE_VERSION = 1;
|
|
14
|
+
export declare const CAFFEINATE_AUTO_POKE_DEBOUNCE_MS = 150;
|
|
15
|
+
export declare const MAX_CAFFEINATE_COMMANDS = 50;
|
|
16
|
+
export declare const MAX_CAFFEINATE_COMMAND_LENGTH = 128;
|
|
12
17
|
export declare const TITLE_MAX_PATH_SEGMENTS = 1;
|
|
13
18
|
/**
|
|
14
19
|
* Strip terminal-emulator identity env vars inherited from the daemon's parent.
|
|
@@ -57,7 +62,7 @@ export declare const MAX_AUTOMATIONS = 100;
|
|
|
57
62
|
export declare const MAX_AUTOMATION_NAME_LENGTH = 120;
|
|
58
63
|
export declare const MAX_AUTOMATION_COMMAND_LENGTH = 4096;
|
|
59
64
|
export declare const MAX_CRON_EXPRESSION_LENGTH = 256;
|
|
60
|
-
export declare const AUTOMATIONS_FILE_VERSION =
|
|
65
|
+
export declare const AUTOMATIONS_FILE_VERSION = 3;
|
|
61
66
|
export declare const AUTOMATION_RUN_LIMIT_MAX = 100000;
|
|
62
67
|
export declare const AUTOMATION_RUN_HISTORY_CAP = 50;
|
|
63
68
|
export declare const AUTOMATION_DOWNTIME_RECONCILE_CAP = 10;
|
|
@@ -67,6 +72,7 @@ export declare const AUTOMATION_RECONCILE_LOOKBACK_MS: number;
|
|
|
67
72
|
export declare const DAEMON_HEARTBEAT_FILE_VERSION = 1;
|
|
68
73
|
export declare const AUTOMATION_TICK_ALIGNMENT_DELAY_MS = 50;
|
|
69
74
|
export declare const AUTOMATION_PENDING_RUN_EXPIRY_MS: number;
|
|
75
|
+
export declare const AUTOMATION_WATCH_DEBOUNCE_MS = 500;
|
|
70
76
|
export declare const CRON_NEXT_OCCURRENCE_SCAN_LIMIT_DAYS = 1466;
|
|
71
77
|
export declare const MAX_AUTOMATION_EXIT_CODE_DIGITS = 4;
|
|
72
78
|
export declare const WS_READY_STATE_OPEN = 1;
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AACvD,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,eAAO,MAAM,eAAe,cAAc,CAAC;AAC3C,eAAO,MAAM,eAAe,MAAM,CAAC;AAInC,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAC9C,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
package/dist/constants.js
CHANGED
|
@@ -11,6 +11,23 @@ export const LOCALTERM_VALUE = "1";
|
|
|
11
11
|
// sleep — held for as long as the spawned process lives.
|
|
12
12
|
export const CAFFEINATE_BINARY = "caffeinate";
|
|
13
13
|
export const CAFFEINATE_ARGS = ["-dims"];
|
|
14
|
+
// Keep-awake "automatic" mode recognizes these commands out of the box and
|
|
15
|
+
// caffeinates whenever one is running in any localterm session. Fixed — the
|
|
16
|
+
// user can add their own on top but cannot remove these.
|
|
17
|
+
export const CAFFEINATE_AUTO_DEFAULT_COMMANDS = [
|
|
18
|
+
"claude",
|
|
19
|
+
"codex",
|
|
20
|
+
"opencode",
|
|
21
|
+
"pi",
|
|
22
|
+
];
|
|
23
|
+
export const CAFFEINATE_PREFERENCES_FILE_VERSION = 1;
|
|
24
|
+
// Automatic detection is event-driven (no timer): a `ps` snapshot is taken only
|
|
25
|
+
// in response to a foreground change or a session connect/disconnect. This
|
|
26
|
+
// debounce window coalesces a burst of such events into a single snapshot; it
|
|
27
|
+
// fires once and does not repeat.
|
|
28
|
+
export const CAFFEINATE_AUTO_POKE_DEBOUNCE_MS = 150;
|
|
29
|
+
export const MAX_CAFFEINATE_COMMANDS = 50;
|
|
30
|
+
export const MAX_CAFFEINATE_COMMAND_LENGTH = 128;
|
|
14
31
|
export const TITLE_MAX_PATH_SEGMENTS = 1;
|
|
15
32
|
/**
|
|
16
33
|
* Strip terminal-emulator identity env vars inherited from the daemon's parent.
|
|
@@ -129,9 +146,11 @@ export const MAX_AUTOMATION_COMMAND_LENGTH = 4096;
|
|
|
129
146
|
export const MAX_CRON_EXPRESSION_LENGTH = 256;
|
|
130
147
|
// v1 stored a raw cron string + a single lastRun. v2 stores a structured
|
|
131
148
|
// schedule (with a derived cron computed on the fly), a run-count limit, a
|
|
132
|
-
// lifecycle, and a capped run-history array.
|
|
133
|
-
//
|
|
134
|
-
|
|
149
|
+
// lifecycle, and a capped run-history array. v3 wraps the schedule in a
|
|
150
|
+
// top-level `trigger` union so an automation can fire on a schedule OR when a
|
|
151
|
+
// folder changes. AutomationStore.load() migrates v1/v2 -> v3 in place on first
|
|
152
|
+
// boot so existing automations are never lost.
|
|
153
|
+
export const AUTOMATIONS_FILE_VERSION = 3;
|
|
135
154
|
// Largest "stop after N runs" budget. Generous — a limit is opt-in; the common
|
|
136
155
|
// case is "forever".
|
|
137
156
|
export const AUTOMATION_RUN_LIMIT_MAX = 100_000;
|
|
@@ -159,6 +178,11 @@ export const AUTOMATION_TICK_ALIGNMENT_DELAY_MS = 50;
|
|
|
159
178
|
// A launched run that no browser tab claims within this window is marked
|
|
160
179
|
// "missed" (browser closed, headless host, open() failed silently).
|
|
161
180
|
export const AUTOMATION_PENDING_RUN_EXPIRY_MS = 5 * 60 * 1000;
|
|
181
|
+
// Quiet period after the last filesystem event before a folder-watch trigger
|
|
182
|
+
// fires. Coalesces an event storm (one editor save emits several events; a
|
|
183
|
+
// build emits thousands) into a single run. Trailing-edge: the timer resets on
|
|
184
|
+
// every event and fires once the directory settles.
|
|
185
|
+
export const AUTOMATION_WATCH_DEBOUNCE_MS = 500;
|
|
162
186
|
// Covers schedules that only fire on Feb 29 (the rarest valid cron target).
|
|
163
187
|
export const CRON_NEXT_OCCURRENCE_SCAN_LIMIT_DAYS = 1466;
|
|
164
188
|
export const MAX_AUTOMATION_EXIT_CODE_DIGITS = 4;
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { Automation } from "./types.js";
|
|
3
|
+
interface FolderWatchManagerEvents {
|
|
4
|
+
due: [automation: Automation];
|
|
5
|
+
}
|
|
6
|
+
interface WatchHandle {
|
|
7
|
+
close: () => void;
|
|
8
|
+
unref?: () => void;
|
|
9
|
+
}
|
|
10
|
+
type WatchFn = (target: string, options: {
|
|
11
|
+
recursive: boolean;
|
|
12
|
+
}, listener: () => void) => WatchHandle;
|
|
13
|
+
interface FolderWatchManagerOptions {
|
|
14
|
+
debounceMs: number;
|
|
15
|
+
isRunInFlight: (automationId: string) => boolean;
|
|
16
|
+
getAutomation: (automationId: string) => Automation | null;
|
|
17
|
+
watch?: WatchFn;
|
|
18
|
+
}
|
|
19
|
+
export declare class FolderWatchManager extends EventEmitter<FolderWatchManagerEvents> {
|
|
20
|
+
private readonly options;
|
|
21
|
+
private readonly entries;
|
|
22
|
+
private readonly watch;
|
|
23
|
+
private disposed;
|
|
24
|
+
constructor(options: FolderWatchManagerOptions);
|
|
25
|
+
sync(automations: Automation[]): void;
|
|
26
|
+
dispose(): void;
|
|
27
|
+
private startEntry;
|
|
28
|
+
private onFsEvent;
|
|
29
|
+
private fire;
|
|
30
|
+
private stopEntry;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=folder-watch-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-watch-manager.d.ts","sourceRoot":"","sources":["../src/folder-watch-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,UAAU,wBAAwB;IAChC,GAAG,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;CAC/B;AAGD,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB;AAID,KAAK,OAAO,GAAG,CACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,EAC/B,QAAQ,EAAE,MAAM,IAAI,KACjB,WAAW,CAAC;AAEjB,UAAU,yBAAyB;IAIjC,UAAU,EAAE,MAAM,CAAC;IAInB,aAAa,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;IAIjD,aAAa,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;IAG3D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAkBD,qBAAa,kBAAmB,SAAQ,YAAY,CAAC,wBAAwB,CAAC;IAKhE,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,QAAQ,CAAS;gBAEI,OAAO,EAAE,yBAAyB;IAS/D,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,IAAI;IAoBrC,OAAO,IAAI,IAAI;IAMf,OAAO,CAAC,UAAU;IAsBlB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,SAAS;CAgBlB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
const signatureOf = (automation) => automation.trigger.kind === "watch"
|
|
4
|
+
? `${automation.trigger.recursive}:${automation.cwd}`
|
|
5
|
+
: automation.cwd;
|
|
6
|
+
// Event-driven folder triggers for automations: one native fs.watch per "watch"
|
|
7
|
+
// automation, on its cwd. No polling — mirrors GitDiffWatcher. A burst of events
|
|
8
|
+
// is coalesced by a trailing debounce, and a launch is suppressed while a prior
|
|
9
|
+
// run is still in-flight (at most one run per automation at a time).
|
|
10
|
+
export class FolderWatchManager extends EventEmitter {
|
|
11
|
+
options;
|
|
12
|
+
entries = new Map();
|
|
13
|
+
watch;
|
|
14
|
+
disposed = false;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super();
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.watch =
|
|
19
|
+
options.watch ??
|
|
20
|
+
((target, watchOptions, listener) => fs.watch(target, watchOptions, () => listener()));
|
|
21
|
+
}
|
|
22
|
+
// Reconcile the live watchers with the desired set (enabled + active + watch).
|
|
23
|
+
// Idempotent and cheap, so it can be called after any automation mutation.
|
|
24
|
+
sync(automations) {
|
|
25
|
+
if (this.disposed)
|
|
26
|
+
return;
|
|
27
|
+
const desired = new Map();
|
|
28
|
+
for (const automation of automations) {
|
|
29
|
+
if (!automation.enabled)
|
|
30
|
+
continue;
|
|
31
|
+
if (automation.lifecycle !== "active")
|
|
32
|
+
continue;
|
|
33
|
+
if (automation.trigger.kind !== "watch")
|
|
34
|
+
continue;
|
|
35
|
+
desired.set(automation.id, automation);
|
|
36
|
+
}
|
|
37
|
+
// Stop watchers that are no longer desired or whose target changed.
|
|
38
|
+
for (const [id, entry] of this.entries) {
|
|
39
|
+
const automation = desired.get(id);
|
|
40
|
+
if (!automation || signatureOf(automation) !== entry.signature)
|
|
41
|
+
this.stopEntry(id);
|
|
42
|
+
}
|
|
43
|
+
// Start watchers for newly-desired (or just-rebuilt) automations.
|
|
44
|
+
for (const [id, automation] of desired) {
|
|
45
|
+
if (!this.entries.has(id))
|
|
46
|
+
this.startEntry(automation);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
dispose() {
|
|
50
|
+
this.disposed = true;
|
|
51
|
+
for (const id of [...this.entries.keys()])
|
|
52
|
+
this.stopEntry(id);
|
|
53
|
+
this.removeAllListeners();
|
|
54
|
+
}
|
|
55
|
+
startEntry(automation) {
|
|
56
|
+
if (automation.trigger.kind !== "watch")
|
|
57
|
+
return;
|
|
58
|
+
const { recursive } = automation.trigger;
|
|
59
|
+
const entry = {
|
|
60
|
+
watchers: [],
|
|
61
|
+
signature: signatureOf(automation),
|
|
62
|
+
debounceTimer: null,
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
const watcher = this.watch(automation.cwd, { recursive }, () => {
|
|
66
|
+
this.onFsEvent(automation.id);
|
|
67
|
+
});
|
|
68
|
+
// Don't keep the daemon alive on the watch alone (the http server does).
|
|
69
|
+
watcher.unref?.();
|
|
70
|
+
entry.watchers.push(watcher);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// cwd doesn't exist or isn't watchable right now — leave the entry empty;
|
|
74
|
+
// a later sync (after the directory is fixed) retries.
|
|
75
|
+
}
|
|
76
|
+
this.entries.set(automation.id, entry);
|
|
77
|
+
}
|
|
78
|
+
onFsEvent(automationId) {
|
|
79
|
+
if (this.disposed)
|
|
80
|
+
return;
|
|
81
|
+
const entry = this.entries.get(automationId);
|
|
82
|
+
if (!entry)
|
|
83
|
+
return;
|
|
84
|
+
if (entry.debounceTimer !== null)
|
|
85
|
+
clearTimeout(entry.debounceTimer);
|
|
86
|
+
entry.debounceTimer = setTimeout(() => {
|
|
87
|
+
entry.debounceTimer = null;
|
|
88
|
+
this.fire(automationId);
|
|
89
|
+
}, this.options.debounceMs);
|
|
90
|
+
entry.debounceTimer.unref?.();
|
|
91
|
+
}
|
|
92
|
+
fire(automationId) {
|
|
93
|
+
if (this.disposed)
|
|
94
|
+
return;
|
|
95
|
+
if (!this.entries.has(automationId))
|
|
96
|
+
return;
|
|
97
|
+
// No overlap: drop this change if a run is still in-flight. The next event
|
|
98
|
+
// after it settles re-arms, so writes made *during* the run are ignored.
|
|
99
|
+
if (this.options.isRunInFlight(automationId))
|
|
100
|
+
return;
|
|
101
|
+
// Re-read live state — the automation may have been disabled, hit its limit,
|
|
102
|
+
// or switched to a schedule while the debounce was pending.
|
|
103
|
+
const automation = this.options.getAutomation(automationId);
|
|
104
|
+
if (!automation || !automation.enabled || automation.lifecycle !== "active")
|
|
105
|
+
return;
|
|
106
|
+
if (automation.trigger.kind !== "watch")
|
|
107
|
+
return;
|
|
108
|
+
this.emit("due", automation);
|
|
109
|
+
}
|
|
110
|
+
stopEntry(automationId) {
|
|
111
|
+
const entry = this.entries.get(automationId);
|
|
112
|
+
if (!entry)
|
|
113
|
+
return;
|
|
114
|
+
if (entry.debounceTimer !== null) {
|
|
115
|
+
clearTimeout(entry.debounceTimer);
|
|
116
|
+
entry.debounceTimer = null;
|
|
117
|
+
}
|
|
118
|
+
for (const watcher of entry.watchers) {
|
|
119
|
+
try {
|
|
120
|
+
watcher.close();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* already closed */
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.entries.delete(automationId);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=folder-watch-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-watch-manager.js","sourceRoot":"","sources":["../src/folder-watch-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CaffeinateController } from "./caffeinate-controller.js";
|
|
2
|
+
import type { SnapshotProcesses } from "./caffeinate-process-match.js";
|
|
2
3
|
import { SessionRegistry } from "./session-registry.js";
|
|
3
4
|
export interface ServerOptions {
|
|
4
5
|
port?: number;
|
|
@@ -19,6 +20,12 @@ export interface ServerOptions {
|
|
|
19
20
|
* power assertion.
|
|
20
21
|
*/
|
|
21
22
|
caffeinateController?: CaffeinateController;
|
|
23
|
+
/**
|
|
24
|
+
* Override how automatic-mode keep-awake inspects running processes. Defaults
|
|
25
|
+
* to a real `ps` snapshot. Injectable so tests can drive automatic detection
|
|
26
|
+
* deterministically without spawning processes.
|
|
27
|
+
*/
|
|
28
|
+
caffeinateSnapshotProcesses?: SnapshotProcesses;
|
|
22
29
|
}
|
|
23
30
|
/** Opens and (optionally) closes the browser tab for an automation run. */
|
|
24
31
|
export interface AutomationTabController {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AA0CvE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAkBxD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,iBAAiB,CAAC;CACjD;AAED,2EAA2E;AAC3E,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,0EAA0E;IAC1E,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AA+DD,eAAO,MAAM,YAAY,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,aAAa,CA41BrF,CAAC;AAEF,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,9 +10,12 @@ import { AutomationRunTracker } from "./automation-run-tracker.js";
|
|
|
10
10
|
import { AutomationScheduler } from "./automation-scheduler.js";
|
|
11
11
|
import { AutomationStore } from "./automation-store.js";
|
|
12
12
|
import { CaffeinateController } from "./caffeinate-controller.js";
|
|
13
|
+
import { CaffeinateManager } from "./caffeinate-manager.js";
|
|
14
|
+
import { CaffeinatePreferencesStore } from "./caffeinate-preferences-store.js";
|
|
13
15
|
import { CdpClient } from "./cdp/cdp-client.js";
|
|
14
|
-
import { AUTOMATION_RECONCILE_MIN_DOWNTIME_MS, DEFAULT_HOST, DEFAULT_PORT, FRIENDLY_HOSTNAME, HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_CREATED, HTTP_STATUS_NOT_FOUND, MAX_AUTOMATIONS, MAX_CONCURRENT_SESSIONS, MS_PER_MINUTE, OUTPUT_BATCH_FLUSH_BYTES, OUTPUT_BATCH_WINDOW_MS, SERVER_STOP_GRACE_MS, WS_BACKPRESSURE_THRESHOLD_BYTES, WS_CLOSE_BACKPRESSURE, WS_CLOSE_CAPACITY_REACHED, WS_CLOSE_POLICY_VIOLATION, WS_HEARTBEAT_INTERVAL_MS, WS_HEARTBEAT_TIMEOUT_MS, WS_OUTBOUND_DRAIN_POLL_MS, WS_OUTBOUND_PAUSE_HIGH_WATER_BYTES, WS_OUTBOUND_RESUME_LOW_WATER_BYTES, WS_READY_STATE_OPEN, } from "./constants.js";
|
|
16
|
+
import { AUTOMATION_RECONCILE_MIN_DOWNTIME_MS, AUTOMATION_WATCH_DEBOUNCE_MS, DEFAULT_HOST, DEFAULT_PORT, FRIENDLY_HOSTNAME, HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_CREATED, HTTP_STATUS_NOT_FOUND, MAX_AUTOMATIONS, MAX_CONCURRENT_SESSIONS, MS_PER_MINUTE, OUTPUT_BATCH_FLUSH_BYTES, OUTPUT_BATCH_WINDOW_MS, SERVER_STOP_GRACE_MS, WS_BACKPRESSURE_THRESHOLD_BYTES, WS_CLOSE_BACKPRESSURE, WS_CLOSE_CAPACITY_REACHED, WS_CLOSE_POLICY_VIOLATION, WS_HEARTBEAT_INTERVAL_MS, WS_HEARTBEAT_TIMEOUT_MS, WS_OUTBOUND_DRAIN_POLL_MS, WS_OUTBOUND_PAUSE_HIGH_WATER_BYTES, WS_OUTBOUND_RESUME_LOW_WATER_BYTES, WS_READY_STATE_OPEN, } from "./constants.js";
|
|
15
17
|
import { ServerErrorException, serverError } from "./errors.js";
|
|
18
|
+
import { FolderWatchManager } from "./folder-watch-manager.js";
|
|
16
19
|
import { getGitDiff, getGitDiffFilePatch, getGitDiffFiles, getGitDiffSummary } from "./git-diff.js";
|
|
17
20
|
import { GitDiffWatcher } from "./git-diff-watcher.js";
|
|
18
21
|
import { HeartbeatStore } from "./heartbeat-store.js";
|
|
@@ -22,7 +25,7 @@ import { Session } from "./session.js";
|
|
|
22
25
|
import { createNetworkPolicyMiddleware, isAllowedSourceIp, isLoopbackHost } from "./security.js";
|
|
23
26
|
import { SessionRegistry } from "./session-registry.js";
|
|
24
27
|
import { resolveStaticAsset } from "./static-resolver.js";
|
|
25
|
-
import { compileSchedule, compileScheduleAll,
|
|
28
|
+
import { compileSchedule, compileScheduleAll, normalizeTriggerInput, } from "./utils/compile-schedule.js";
|
|
26
29
|
import { computeNextAutomationRunAt } from "./utils/compute-next-automation-run-at.js";
|
|
27
30
|
import { enumerateMissedOccurrences } from "./utils/reconcile-downtime.js";
|
|
28
31
|
const getRawBufferedAmount = (raw) => {
|
|
@@ -102,8 +105,27 @@ export const createServer = async (options = {}) => {
|
|
|
102
105
|
const automationStore = new AutomationStore(path.join(stateDirectory, "automations.json"));
|
|
103
106
|
const automationRunTracker = new AutomationRunTracker();
|
|
104
107
|
const automationScheduler = new AutomationScheduler(automationStore);
|
|
108
|
+
// Folder-watch triggers: one fs.watch per watch automation's cwd (no polling).
|
|
109
|
+
// isRunInFlight gates overlap (a launched/running latest run blocks a new
|
|
110
|
+
// launch); getAutomation re-reads live state when the debounce fires.
|
|
111
|
+
const folderWatchManager = new FolderWatchManager({
|
|
112
|
+
debounceMs: AUTOMATION_WATCH_DEBOUNCE_MS,
|
|
113
|
+
isRunInFlight: (automationId) => {
|
|
114
|
+
const status = automationStore.get(automationId)?.runs[0]?.status;
|
|
115
|
+
return status === "launched" || status === "running";
|
|
116
|
+
},
|
|
117
|
+
getAutomation: (automationId) => automationStore.get(automationId),
|
|
118
|
+
});
|
|
119
|
+
const syncFolderWatchers = () => folderWatchManager.sync(automationStore.list());
|
|
105
120
|
const heartbeatStore = new HeartbeatStore(path.join(stateDirectory, "daemon-heartbeat.json"));
|
|
106
121
|
const caffeinateController = options.caffeinateController ?? new CaffeinateController();
|
|
122
|
+
const caffeinatePreferencesStore = new CaffeinatePreferencesStore(path.join(stateDirectory, "caffeinate.json"));
|
|
123
|
+
const caffeinateManager = new CaffeinateManager({
|
|
124
|
+
controller: caffeinateController,
|
|
125
|
+
store: caffeinatePreferencesStore,
|
|
126
|
+
listSessionPids: () => registry.pids(),
|
|
127
|
+
snapshotProcesses: options.caffeinateSnapshotProcesses,
|
|
128
|
+
});
|
|
107
129
|
const clientSockets = new Set();
|
|
108
130
|
const cdpBackgroundTabsDisabled = process.env.LOCALTERM_DISABLE_CDP_TABS === "1";
|
|
109
131
|
// One persistent CDP socket for the daemon's lifetime — opened once at start
|
|
@@ -153,7 +175,7 @@ export const createServer = async (options = {}) => {
|
|
|
153
175
|
const toAutomationWithNextRun = (automation, from) => ({
|
|
154
176
|
...automation,
|
|
155
177
|
nextRunAt: computeNextAutomationRunAt(automation, from),
|
|
156
|
-
cron: compileSchedule(automation.schedule),
|
|
178
|
+
cron: automation.trigger.kind === "schedule" ? compileSchedule(automation.trigger.schedule) : null,
|
|
157
179
|
lastRun: deriveLastRun(automation),
|
|
158
180
|
});
|
|
159
181
|
const listAutomationsWithNextRun = () => {
|
|
@@ -171,29 +193,32 @@ export const createServer = async (options = {}) => {
|
|
|
171
193
|
};
|
|
172
194
|
const caffeinateStatePayload = () => ({
|
|
173
195
|
type: "caffeinate",
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
supported: caffeinateManager.supported,
|
|
197
|
+
active: caffeinateManager.active,
|
|
198
|
+
mode: caffeinateManager.mode,
|
|
199
|
+
defaultCommands: [...caffeinateManager.defaultCommands],
|
|
200
|
+
commands: caffeinateManager.commands,
|
|
176
201
|
});
|
|
177
202
|
// The daemon owns the single keep-awake process, so its state is broadcast to
|
|
178
|
-
// every tab — exactly like automations — and the coffee
|
|
203
|
+
// every tab — exactly like automations — and the coffee controls stay in sync.
|
|
179
204
|
const broadcastCaffeinate = () => {
|
|
180
205
|
const payload = caffeinateStatePayload();
|
|
181
206
|
for (const clientSocket of clientSockets) {
|
|
182
207
|
safeSend(clientSocket, payload);
|
|
183
208
|
}
|
|
184
209
|
};
|
|
185
|
-
|
|
186
|
-
// Open a browser tab for a run and record it in history. Scheduled
|
|
187
|
-
// count toward the limit (and can finish the automation); manual
|
|
188
|
-
// never count and are allowed even on a finished/disabled automation.
|
|
210
|
+
caffeinateManager.on("change", broadcastCaffeinate);
|
|
211
|
+
// Open a browser tab for a run and record it in history. Scheduled and watch
|
|
212
|
+
// launches count toward the limit (and can finish the automation); manual
|
|
213
|
+
// launches never count and are allowed even on a finished/disabled automation.
|
|
189
214
|
const tryLaunch = (automation, trigger) => {
|
|
190
|
-
if (trigger
|
|
215
|
+
if (trigger !== "manual") {
|
|
191
216
|
const current = automationStore.get(automation.id);
|
|
192
217
|
if (!current || !current.enabled || current.lifecycle === "finished")
|
|
193
218
|
return null;
|
|
194
219
|
}
|
|
195
220
|
const run = automationRunTracker.create(automation);
|
|
196
|
-
const counts = trigger
|
|
221
|
+
const counts = trigger !== "manual";
|
|
197
222
|
automationStore.appendRun(automation.id, {
|
|
198
223
|
runId: run.runId,
|
|
199
224
|
scheduledFor: trigger === "schedule"
|
|
@@ -209,6 +234,9 @@ export const createServer = async (options = {}) => {
|
|
|
209
234
|
if (counts)
|
|
210
235
|
automationStore.incrementRunCount(automation.id);
|
|
211
236
|
broadcastAutomations();
|
|
237
|
+
// A watch automation that just reached its limit is now "finished"; stop
|
|
238
|
+
// watching its folder promptly instead of waiting for the next mutation.
|
|
239
|
+
syncFolderWatchers();
|
|
212
240
|
const runUrl = `http://${FRIENDLY_HOSTNAME}:${actualPort}/?run=${run.runId}`;
|
|
213
241
|
void tabController
|
|
214
242
|
.open(runUrl)
|
|
@@ -335,10 +363,17 @@ export const createServer = async (options = {}) => {
|
|
|
335
363
|
return undefined;
|
|
336
364
|
}
|
|
337
365
|
};
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
366
|
+
// The trigger (new `trigger` union or legacy bare `schedule`) is valid iff a
|
|
367
|
+
// schedule trigger compiles to ≥1 parseable cron; a watch trigger is always
|
|
368
|
+
// valid (its watched cwd is validated separately). Returns true when neither
|
|
369
|
+
// field is present — a PATCH that doesn't touch the trigger.
|
|
370
|
+
const isValidTriggerInput = (input) => {
|
|
371
|
+
if (input.trigger === undefined && input.schedule === undefined)
|
|
372
|
+
return true;
|
|
373
|
+
const trigger = normalizeTriggerInput(input);
|
|
374
|
+
if (trigger.kind === "watch")
|
|
375
|
+
return true;
|
|
376
|
+
const crons = compileScheduleAll(trigger.schedule);
|
|
342
377
|
return crons.length > 0 && crons.every((cron) => parseCronExpression(cron) !== null);
|
|
343
378
|
};
|
|
344
379
|
api.get("/automations", (context) => context.json({ automations: listAutomationsWithNextRun() }));
|
|
@@ -349,7 +384,7 @@ export const createServer = async (options = {}) => {
|
|
|
349
384
|
if (automationStore.size() >= MAX_AUTOMATIONS) {
|
|
350
385
|
return context.json({ error: "too_many_automations" }, HTTP_STATUS_BAD_REQUEST);
|
|
351
386
|
}
|
|
352
|
-
if (!
|
|
387
|
+
if (!isValidTriggerInput(parsed.data)) {
|
|
353
388
|
return context.json({ error: "invalid_schedule" }, HTTP_STATUS_BAD_REQUEST);
|
|
354
389
|
}
|
|
355
390
|
if (!resolveCwdQuery(parsed.data.cwd)) {
|
|
@@ -357,13 +392,14 @@ export const createServer = async (options = {}) => {
|
|
|
357
392
|
}
|
|
358
393
|
const automation = automationStore.create(parsed.data);
|
|
359
394
|
broadcastAutomations();
|
|
395
|
+
syncFolderWatchers();
|
|
360
396
|
return context.json({ automation: toAutomationWithNextRun(automation, new Date()) }, HTTP_STATUS_CREATED);
|
|
361
397
|
});
|
|
362
398
|
api.patch("/automations/:id", async (context) => {
|
|
363
399
|
const parsed = updateAutomationInputSchema.safeParse(await readJsonBody(context));
|
|
364
400
|
if (!parsed.success)
|
|
365
401
|
return context.json({ error: "invalid_body" }, HTTP_STATUS_BAD_REQUEST);
|
|
366
|
-
if (
|
|
402
|
+
if (!isValidTriggerInput(parsed.data)) {
|
|
367
403
|
return context.json({ error: "invalid_schedule" }, HTTP_STATUS_BAD_REQUEST);
|
|
368
404
|
}
|
|
369
405
|
if (parsed.data.cwd !== undefined && !resolveCwdQuery(parsed.data.cwd)) {
|
|
@@ -381,6 +417,7 @@ export const createServer = async (options = {}) => {
|
|
|
381
417
|
if (!automation)
|
|
382
418
|
return context.json({ error: "not_found" }, HTTP_STATUS_NOT_FOUND);
|
|
383
419
|
broadcastAutomations();
|
|
420
|
+
syncFolderWatchers();
|
|
384
421
|
return context.json({ automation: toAutomationWithNextRun(automation, new Date()) });
|
|
385
422
|
});
|
|
386
423
|
api.delete("/automations/:id", (context) => {
|
|
@@ -388,13 +425,14 @@ export const createServer = async (options = {}) => {
|
|
|
388
425
|
return context.json({ error: "not_found" }, HTTP_STATUS_NOT_FOUND);
|
|
389
426
|
}
|
|
390
427
|
broadcastAutomations();
|
|
428
|
+
syncFolderWatchers();
|
|
391
429
|
return context.json({ ok: true });
|
|
392
430
|
});
|
|
393
431
|
api.post("/automations/:id/run", (context) => {
|
|
394
432
|
const automation = automationStore.get(context.req.param("id"));
|
|
395
433
|
if (!automation)
|
|
396
434
|
return context.json({ error: "not_found" }, HTTP_STATUS_NOT_FOUND);
|
|
397
|
-
// tryLaunch only returns null for
|
|
435
|
+
// tryLaunch only returns null for non-manual triggers (the finished/disabled
|
|
398
436
|
// guard); a manual launch always succeeds. The guard satisfies the shared
|
|
399
437
|
// nullable return type before reading run.runId.
|
|
400
438
|
const run = tryLaunch(automation, "manual");
|
|
@@ -410,6 +448,8 @@ export const createServer = async (options = {}) => {
|
|
|
410
448
|
if (!automation)
|
|
411
449
|
return context.json({ error: "not_found" }, HTTP_STATUS_NOT_FOUND);
|
|
412
450
|
broadcastAutomations();
|
|
451
|
+
// Reset re-enables + reactivates; a watch automation resumes watching.
|
|
452
|
+
syncFolderWatchers();
|
|
413
453
|
return context.json({ automation: toAutomationWithNextRun(automation, new Date()) });
|
|
414
454
|
});
|
|
415
455
|
api.notFound((context) => context.json({ error: "not_found" }, HTTP_STATUS_NOT_FOUND));
|
|
@@ -592,7 +632,12 @@ export const createServer = async (options = {}) => {
|
|
|
592
632
|
};
|
|
593
633
|
const onTitle = (title) => safeSend(ws, { type: "title", title });
|
|
594
634
|
const onCwd = (cwd) => safeSend(ws, { type: "cwd", cwd });
|
|
595
|
-
const onForeground = (process) =>
|
|
635
|
+
const onForeground = (process) => {
|
|
636
|
+
safeSend(ws, { type: "foreground", process });
|
|
637
|
+
// A foreground transition is the cheap signal that a recognized
|
|
638
|
+
// program may have started or stopped — nudge automatic detection.
|
|
639
|
+
caffeinateManager.pokeAuto();
|
|
640
|
+
};
|
|
596
641
|
const onNotification = (body) => safeSend(ws, { type: "notification", body });
|
|
597
642
|
const onExit = (code) => {
|
|
598
643
|
if (outputBatchTimer !== null) {
|
|
@@ -665,8 +710,11 @@ export const createServer = async (options = {}) => {
|
|
|
665
710
|
if (parsed.data.type === "input") {
|
|
666
711
|
session.write(parsed.data.data);
|
|
667
712
|
}
|
|
668
|
-
else if (parsed.data.type === "caffeinate") {
|
|
669
|
-
|
|
713
|
+
else if (parsed.data.type === "caffeinate-mode") {
|
|
714
|
+
caffeinateManager.setMode(parsed.data.mode);
|
|
715
|
+
}
|
|
716
|
+
else if (parsed.data.type === "caffeinate-commands") {
|
|
717
|
+
caffeinateManager.setCommands(parsed.data.commands);
|
|
670
718
|
}
|
|
671
719
|
else {
|
|
672
720
|
session.resize(parsed.data.cols, parsed.data.rows, parsed.data.pixelWidth, parsed.data.pixelHeight);
|
|
@@ -695,6 +743,9 @@ export const createServer = async (options = {}) => {
|
|
|
695
743
|
if (!session)
|
|
696
744
|
return;
|
|
697
745
|
registry.unregister(session);
|
|
746
|
+
// A closed session may have been the only one running a trigger;
|
|
747
|
+
// re-evaluate automatic keep-awake now that its pid is gone.
|
|
748
|
+
caffeinateManager.pokeAuto();
|
|
698
749
|
session.dispose();
|
|
699
750
|
session = null;
|
|
700
751
|
activeWs = null;
|
|
@@ -720,6 +771,9 @@ export const createServer = async (options = {}) => {
|
|
|
720
771
|
if (!session)
|
|
721
772
|
return;
|
|
722
773
|
registry.unregister(session);
|
|
774
|
+
// A closed session may have been the only one running a trigger;
|
|
775
|
+
// re-evaluate automatic keep-awake now that its pid is gone.
|
|
776
|
+
caffeinateManager.pokeAuto();
|
|
723
777
|
session.dispose();
|
|
724
778
|
session = null;
|
|
725
779
|
activeWs = null;
|
|
@@ -771,6 +825,9 @@ export const createServer = async (options = {}) => {
|
|
|
771
825
|
automationScheduler.on("due", (automation) => {
|
|
772
826
|
tryLaunch(automation, "schedule");
|
|
773
827
|
});
|
|
828
|
+
folderWatchManager.on("due", (automation) => {
|
|
829
|
+
tryLaunch(automation, "watch");
|
|
830
|
+
});
|
|
774
831
|
automationScheduler.on("tick", (now) => {
|
|
775
832
|
// Liveness heartbeat for downtime detection on the next boot.
|
|
776
833
|
heartbeatStore.write(now.getTime());
|
|
@@ -791,6 +848,8 @@ export const createServer = async (options = {}) => {
|
|
|
791
848
|
});
|
|
792
849
|
reconcileOnStartup(Date.now());
|
|
793
850
|
automationScheduler.start();
|
|
851
|
+
// Arm folder-watch triggers for the automations loaded at boot.
|
|
852
|
+
syncFolderWatchers();
|
|
794
853
|
// Open the persistent CDP connection now, while the user is present at
|
|
795
854
|
// `start` to clear any one-time remote-debugging prompt. Fire-and-forget:
|
|
796
855
|
// failure just means runs fall back to the OS opener.
|
|
@@ -808,7 +867,8 @@ export const createServer = async (options = {}) => {
|
|
|
808
867
|
}
|
|
809
868
|
const stop = async () => {
|
|
810
869
|
automationScheduler.dispose();
|
|
811
|
-
|
|
870
|
+
folderWatchManager.dispose();
|
|
871
|
+
caffeinateManager.dispose();
|
|
812
872
|
cdpClient?.close();
|
|
813
873
|
registry.disposeAll();
|
|
814
874
|
// Forcibly tear down every WS first. node-pty + ws upgraded sockets
|