@herbertgao/pi-extensions 2026.9.9 → 2026.9.11
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 +1 -0
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/node_modules/@herbertgao/pi-bark/package.json +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -2
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +12 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +427 -120
- package/node_modules/@herbertgao/pi-subagents/docs/rpc.md +184 -0
- package/node_modules/@herbertgao/pi-subagents/docs/workflows.md +466 -0
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +6 -6
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/compose.js +52 -0
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/fan-out-audit.js +56 -0
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/gated-fix.js +60 -0
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/lib/count-child.js +30 -0
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/review-panel.js +68 -0
- package/node_modules/@herbertgao/pi-subagents/examples/workflows/structured-findings.js +81 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +12 -10
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +52 -12
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +837 -146
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +213 -39
- package/node_modules/@herbertgao/pi-subagents/src/cross-extension-rpc.ts +73 -14
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +101 -47
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +2249 -914
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +13 -0
- package/node_modules/@herbertgao/pi-subagents/src/mention-clone.ts +215 -0
- package/node_modules/@herbertgao/pi-subagents/src/mention.ts +147 -0
- package/node_modules/@herbertgao/pi-subagents/src/model-resolver.ts +9 -1
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +40 -26
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +18 -8
- package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +46 -9
- package/node_modules/@herbertgao/pi-subagents/src/schedule.ts +21 -16
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +137 -7
- package/node_modules/@herbertgao/pi-subagents/src/structured-output.ts +136 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +126 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-mention.ts +274 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +20 -5
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +10 -4
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +167 -22
- package/node_modules/@herbertgao/pi-subagents/src/ui/workflow-card.ts +555 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/workflow-dialog.ts +1304 -0
- package/node_modules/@herbertgao/pi-subagents/src/ui/workflow-menu.ts +226 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/collisions.ts +122 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/entry.ts +47 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/host.ts +463 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/journal.ts +164 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/json-schema.ts +142 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/meta.ts +401 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/progress.ts +622 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/runtime.ts +1399 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/saved.ts +230 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/task.ts +333 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/tool-description.ts +200 -0
- package/node_modules/@herbertgao/pi-subagents/src/workflow/worker-source.ts +781 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +97 -95
- package/node_modules/@herbertgao/pi-subagents/src/xml.ts +13 -0
- package/node_modules/@herbertgao/resume-from/package.json +1 -1
- package/node_modules/@herbertgao/sol-pi/README.md +3 -3
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +4 -4
- package/node_modules/@herbertgao/sol-pi/agents-install.md +4 -4
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +6 -6
- package/node_modules/@herbertgao/sol-pi/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/dist/index.ts +39 -89
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +3 -3
- package/node_modules/@narumitw/pi-btw/package.json +4 -4
- package/node_modules/@narumitw/pi-btw/src/btw.ts +28 -87
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +8 -0
- package/node_modules/@narumitw/pi-btw/src/side-thread.ts +40 -37
- package/node_modules/@narumitw/pi-caffeinate/README.md +21 -66
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts +10 -41
- package/node_modules/@narumitw/pi-caffeinate/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-caffeinate/package.json +50 -51
- package/node_modules/@narumitw/pi-caffeinate/src/caffeinate.ts +637 -663
- package/node_modules/@narumitw/pi-caffeinate/src/dbus-inhibit.ts +114 -120
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitor-process.ts +29 -29
- package/node_modules/@narumitw/pi-caffeinate/src/inhibitors.ts +108 -126
- package/node_modules/@narumitw/pi-caffeinate/src/settings.ts +124 -128
- package/node_modules/pi-multi-account/CHANGELOG.md +1209 -0
- package/node_modules/pi-multi-account/CONTRIBUTING.md +61 -0
- package/node_modules/pi-multi-account/LICENSE +21 -0
- package/node_modules/pi-multi-account/README.md +197 -0
- package/node_modules/pi-multi-account/SECURITY.md +27 -0
- package/node_modules/pi-multi-account/auth-file-transaction.ts +56 -0
- package/node_modules/pi-multi-account/child-usability.ts +233 -0
- package/node_modules/pi-multi-account/compaction-summary.ts +32 -0
- package/node_modules/pi-multi-account/completion-route-planner.ts +224 -0
- package/node_modules/pi-multi-account/context-guard.ts +420 -0
- package/node_modules/pi-multi-account/cursor/LICENSE +21 -0
- package/node_modules/pi-multi-account/cursor/NOTICE +2 -0
- package/node_modules/pi-multi-account/cursor/auth.ts +165 -0
- package/node_modules/pi-multi-account/cursor/bridge-handle.ts +155 -0
- package/node_modules/pi-multi-account/cursor/conversation-registry.ts +104 -0
- package/node_modules/pi-multi-account/cursor/cursor-models-raw.json +611 -0
- package/node_modules/pi-multi-account/cursor/cursor-shared.ts +192 -0
- package/node_modules/pi-multi-account/cursor/h2-bridge.mjs +175 -0
- package/node_modules/pi-multi-account/cursor/index.ts +572 -0
- package/node_modules/pi-multi-account/cursor/message-parsing.ts +323 -0
- package/node_modules/pi-multi-account/cursor/prompt-usage.ts +53 -0
- package/node_modules/pi-multi-account/cursor/proto/agent_pb.ts +15294 -0
- package/node_modules/pi-multi-account/cursor/proxy.ts +2510 -0
- package/node_modules/pi-multi-account/cursor/session-lifecycle.ts +40 -0
- package/node_modules/pi-multi-account/cursor/sse-keepalive.ts +24 -0
- package/node_modules/pi-multi-account/cursor/stream-lifecycle.ts +193 -0
- package/node_modules/pi-multi-account/cursor/upstream-watchdog.ts +88 -0
- package/node_modules/pi-multi-account/cursor-bridge.ts +240 -0
- package/node_modules/pi-multi-account/cursor-model-name.ts +12 -0
- package/node_modules/pi-multi-account/index.ts +11825 -0
- package/node_modules/pi-multi-account/model-catalog.ts +354 -0
- package/node_modules/pi-multi-account/package.json +101 -0
- package/node_modules/pi-multi-account/pi-contract.ts +281 -0
- package/node_modules/pi-multi-account/provider-payload-stream.ts +44 -0
- package/node_modules/pi-multi-account/provider-priority.ts +189 -0
- package/node_modules/pi-multi-account/slot-proxy-auth.ts +167 -0
- package/node_modules/pi-multi-account/slot-proxy.ts +344 -0
- package/node_modules/pi-multi-account/state-file-transaction.ts +67 -0
- package/node_modules/pi-multi-account/usage.ts +1099 -0
- package/node_modules/pi-typesafe/README.md +6 -2
- package/node_modules/pi-typesafe/dist/client.d.ts +11 -0
- package/node_modules/pi-typesafe/dist/client.js +45 -10
- package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/package.json +2 -2
- package/node_modules/pi-web-access/CHANGELOG.md +36 -0
- package/node_modules/pi-web-access/README.md +75 -18
- package/node_modules/pi-web-access/anysearch.ts +4 -15
- package/node_modules/pi-web-access/bocha.ts +3 -22
- package/node_modules/pi-web-access/brave.ts +3 -21
- package/node_modules/pi-web-access/brightdata.ts +5 -32
- package/node_modules/pi-web-access/content-find.ts +168 -53
- package/node_modules/pi-web-access/curator-page.ts +4 -1
- package/node_modules/pi-web-access/curator-run.ts +2 -1
- package/node_modules/pi-web-access/curator-server.ts +1 -0
- package/node_modules/pi-web-access/dist/index.js +24620 -0
- package/node_modules/pi-web-access/domain-filter-normalization.ts +14 -0
- package/node_modules/pi-web-access/duckduckgo.ts +3 -21
- package/node_modules/pi-web-access/extract.ts +3 -1
- package/node_modules/pi-web-access/firecrawl.ts +5 -29
- package/node_modules/pi-web-access/gemini-search.ts +81 -32
- package/node_modules/pi-web-access/index.ts +149 -148
- package/node_modules/pi-web-access/jina-search.ts +4 -15
- package/node_modules/pi-web-access/kagi.ts +4 -13
- package/node_modules/pi-web-access/kimi-search.ts +5 -30
- package/node_modules/pi-web-access/mistral-search.ts +1 -15
- package/node_modules/pi-web-access/ollama.ts +2 -7
- package/node_modules/pi-web-access/openai-search.ts +174 -36
- package/node_modules/pi-web-access/opencode-session-headers.ts +24 -0
- package/node_modules/pi-web-access/package.json +10 -4
- package/node_modules/pi-web-access/page-query.ts +10 -2
- package/node_modules/pi-web-access/parallel.ts +1 -15
- package/node_modules/pi-web-access/pdf-extract.ts +3 -0
- package/node_modules/pi-web-access/querit.ts +5 -29
- package/node_modules/pi-web-access/search-answer-formatting.ts +11 -0
- package/node_modules/pi-web-access/search-result-count-normalization.ts +4 -0
- package/node_modules/pi-web-access/search1api.ts +5 -29
- package/node_modules/pi-web-access/searchinfinity.ts +5 -29
- package/node_modules/pi-web-access/searxng.ts +3 -21
- package/node_modules/pi-web-access/serpapi.ts +5 -28
- package/node_modules/pi-web-access/serpbase.ts +3 -22
- package/node_modules/pi-web-access/serpdive.ts +3 -21
- package/node_modules/pi-web-access/serper.ts +5 -28
- package/node_modules/pi-web-access/serply.ts +197 -0
- package/node_modules/pi-web-access/source-check.ts +11 -47
- package/node_modules/pi-web-access/summary-review.ts +7 -3
- package/node_modules/pi-web-access/tavily.ts +3 -21
- package/node_modules/pi-web-access/tinyfish.ts +5 -29
- package/node_modules/pi-web-access/utils.ts +9 -1
- package/node_modules/pi-web-access/valyu.ts +5 -28
- package/node_modules/pi-web-access/xai-search.ts +1 -15
- package/node_modules/pi-web-access/xcrawl.ts +5 -32
- package/package.json +17 -11
|
@@ -11,143 +11,137 @@ const DBUS_CALL_TIMEOUT_MS = 2_000;
|
|
|
11
11
|
type DbusFailureHandler = (error: Error) => void;
|
|
12
12
|
|
|
13
13
|
export interface DbusScreenSaverClient {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
setFailureHandler(handler: DbusFailureHandler | undefined): void;
|
|
15
|
+
inhibit(reason: string, signal?: AbortSignal): Promise<void>;
|
|
16
|
+
uninhibit(): Promise<void>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type DbusScreenSaverFactory = () => Promise<DbusScreenSaverClient>;
|
|
21
21
|
|
|
22
22
|
export async function defaultDbusScreenSaverFactory(): Promise<DbusScreenSaverClient> {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const { sessionBus } = await import("dbus-native");
|
|
24
|
+
return new NativeScreenSaverClient(sessionBus());
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
class NativeScreenSaverClient implements DbusScreenSaverClient {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
else this.pendingFailure = error;
|
|
124
|
-
}
|
|
28
|
+
private cookie?: number;
|
|
29
|
+
private objectPath?: string;
|
|
30
|
+
private failureHandler?: DbusFailureHandler;
|
|
31
|
+
private pendingFailure?: Error;
|
|
32
|
+
private lastConnectionError?: Error;
|
|
33
|
+
private closing = false;
|
|
34
|
+
|
|
35
|
+
private readonly handleConnectionError = (error: unknown) => {
|
|
36
|
+
this.lastConnectionError = toError(error, "D-Bus session connection failed");
|
|
37
|
+
if (this.cookie !== undefined) this.reportFailure(this.lastConnectionError);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
private readonly handleConnectionClose = (error?: unknown) => {
|
|
41
|
+
if (this.closing || this.cookie === undefined) return;
|
|
42
|
+
this.reportFailure(toError(error, this.lastConnectionError?.message ?? "D-Bus session connection closed"));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
constructor(private readonly bus: MessageBus) {
|
|
46
|
+
this.bus.connection.on("error", this.handleConnectionError);
|
|
47
|
+
this.bus.connection.on("close", this.handleConnectionClose);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setFailureHandler(handler: DbusFailureHandler | undefined): void {
|
|
51
|
+
this.failureHandler = handler;
|
|
52
|
+
if (!handler || !this.pendingFailure) return;
|
|
53
|
+
const failure = this.pendingFailure;
|
|
54
|
+
this.pendingFailure = undefined;
|
|
55
|
+
handler(failure);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async inhibit(reason: string, signal?: AbortSignal): Promise<void> {
|
|
59
|
+
let lastError: unknown;
|
|
60
|
+
for (const objectPath of SCREENSAVER_OBJECT_PATHS) {
|
|
61
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
62
|
+
try {
|
|
63
|
+
this.cookie = await invoke<number>(
|
|
64
|
+
this.bus,
|
|
65
|
+
{
|
|
66
|
+
destination: SCREENSAVER_BUS_NAME,
|
|
67
|
+
path: objectPath,
|
|
68
|
+
interface: SCREENSAVER_INTERFACE,
|
|
69
|
+
member: "Inhibit",
|
|
70
|
+
signature: "ss",
|
|
71
|
+
body: [INHIBIT_APPLICATION_NAME, reason],
|
|
72
|
+
},
|
|
73
|
+
signal,
|
|
74
|
+
);
|
|
75
|
+
this.objectPath = objectPath;
|
|
76
|
+
return;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
79
|
+
lastError = error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`D-Bus idle inhibit failed: ${formatError(lastError)}`, {
|
|
83
|
+
cause: lastError,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async uninhibit(): Promise<void> {
|
|
88
|
+
if (this.cookie === undefined || !this.objectPath) return;
|
|
89
|
+
const cookie = this.cookie;
|
|
90
|
+
this.cookie = undefined;
|
|
91
|
+
const objectPath = this.objectPath;
|
|
92
|
+
this.objectPath = undefined;
|
|
93
|
+
await invoke<void>(this.bus, {
|
|
94
|
+
destination: SCREENSAVER_BUS_NAME,
|
|
95
|
+
path: objectPath,
|
|
96
|
+
interface: SCREENSAVER_INTERFACE,
|
|
97
|
+
member: "UnInhibit",
|
|
98
|
+
signature: "u",
|
|
99
|
+
body: [cookie],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async close(): Promise<void> {
|
|
104
|
+
this.closing = true;
|
|
105
|
+
this.cookie = undefined;
|
|
106
|
+
this.objectPath = undefined;
|
|
107
|
+
this.failureHandler = undefined;
|
|
108
|
+
try {
|
|
109
|
+
await this.bus.close();
|
|
110
|
+
} finally {
|
|
111
|
+
this.bus.connection.off("error", this.handleConnectionError);
|
|
112
|
+
this.bus.connection.off("close", this.handleConnectionClose);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private reportFailure(error: Error): void {
|
|
117
|
+
if (this.cookie === undefined) return;
|
|
118
|
+
this.cookie = undefined;
|
|
119
|
+
this.objectPath = undefined;
|
|
120
|
+
if (this.failureHandler) this.failureHandler(error);
|
|
121
|
+
else this.pendingFailure = error;
|
|
122
|
+
}
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
function invoke<TResult>(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (error) reject(error);
|
|
135
|
-
else resolve(values[0] as TResult);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
125
|
+
function invoke<TResult>(bus: MessageBus, message: Message, signal?: AbortSignal): Promise<TResult> {
|
|
126
|
+
return new Promise<TResult>((resolve, reject) => {
|
|
127
|
+
bus.invoke(message, { signal, timeout: DBUS_CALL_TIMEOUT_MS }, (error, ...values) => {
|
|
128
|
+
if (error) reject(error);
|
|
129
|
+
else resolve(values[0] as TResult);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
138
132
|
}
|
|
139
133
|
|
|
140
134
|
function abortReason(signal: AbortSignal): unknown {
|
|
141
|
-
|
|
135
|
+
return signal.reason ?? new DOMException("D-Bus idle inhibit cancelled", "AbortError");
|
|
142
136
|
}
|
|
143
137
|
|
|
144
138
|
function toError(error: unknown, fallback: string): Error {
|
|
145
|
-
|
|
139
|
+
return error instanceof Error ? error : new Error(fallback);
|
|
146
140
|
}
|
|
147
141
|
|
|
148
142
|
function formatError(error: unknown) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
143
|
+
if (error instanceof Error) {
|
|
144
|
+
return error.name === "Error" ? error.message : `${error.name}: ${error.message}`;
|
|
145
|
+
}
|
|
146
|
+
return String(error);
|
|
153
147
|
}
|
|
@@ -3,41 +3,41 @@ import process from "node:process";
|
|
|
3
3
|
import type { InhibitorCommand } from "./inhibitors.js";
|
|
4
4
|
|
|
5
5
|
export function startInhibitorProcess(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
command: InhibitorCommand,
|
|
7
|
+
onError: (error: Error) => void,
|
|
8
|
+
onUnexpectedExit: (description: string) => void,
|
|
9
9
|
): ChildProcess {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
const child = spawn(command.command, command.args, {
|
|
11
|
+
detached: false,
|
|
12
|
+
stdio: [command.releaseOnStdinClose ? "pipe" : "ignore", "pipe", "pipe"],
|
|
13
|
+
});
|
|
14
|
+
child.once("error", onError);
|
|
15
|
+
child.once("exit", (code, signal) => onUnexpectedExit(formatExit(code, signal)));
|
|
16
|
+
return child;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function stopInhibitorProcess(child: ChildProcess, command: InhibitorCommand | undefined) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
child.removeAllListeners("exit");
|
|
21
|
+
child.removeAllListeners("error");
|
|
22
|
+
if (child.killed) return;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
if (command?.releaseOnStdinClose && child.stdin && !child.stdin.destroyed) {
|
|
25
|
+
child.stdin.end();
|
|
26
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
27
|
+
const killTimer = setTimeout(() => {
|
|
28
|
+
if (child.exitCode === null && child.signalCode === null && !child.killed) child.kill();
|
|
29
|
+
}, 2000);
|
|
30
|
+
killTimer.unref();
|
|
31
|
+
child.once("exit", () => clearTimeout(killTimer));
|
|
32
|
+
}
|
|
33
|
+
} else if (process.platform === "win32") {
|
|
34
|
+
child.kill();
|
|
35
|
+
} else {
|
|
36
|
+
child.kill("SIGTERM");
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function formatExit(code: number | null, signal: NodeJS.Signals | null) {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if (signal) return `signal ${signal}`;
|
|
42
|
+
return `code ${code ?? "unknown"}`;
|
|
43
43
|
}
|
|
@@ -4,164 +4,146 @@ import process from "node:process";
|
|
|
4
4
|
import type { CaffeinateMode } from "./settings.js";
|
|
5
5
|
|
|
6
6
|
export interface InhibitorCommand {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
command: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
description: string;
|
|
10
|
+
releaseOnStdinClose?: boolean;
|
|
11
|
+
custom?: boolean;
|
|
12
|
+
/** Display mode also needs D-Bus because logind idle inhibition misses some compositors. */
|
|
13
|
+
addDbusIdleInhibit?: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function getInhibitorCommand(mode: CaffeinateMode): InhibitorCommand | undefined {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (commandExists("caffeinate")) {
|
|
50
|
-
return parentBoundUnixCommand(
|
|
51
|
-
"caffeinate",
|
|
52
|
-
macCaffeinateArgs(mode),
|
|
53
|
-
caffeinateDescription(mode),
|
|
54
|
-
mode === "display",
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (process.platform === "win32") return windowsPowerInhibitorCommand("powershell.exe", mode);
|
|
59
|
-
return undefined;
|
|
17
|
+
const customCommand = process.env.PI_CAFFEINATE_COMMAND?.trim();
|
|
18
|
+
if (customCommand) {
|
|
19
|
+
const [command, ...args] = splitCommand(customCommand);
|
|
20
|
+
if (command) return { command, args, description: `custom command (${command})`, custom: true };
|
|
21
|
+
}
|
|
22
|
+
if (process.platform === "darwin") {
|
|
23
|
+
return parentBoundUnixCommand("caffeinate", macCaffeinateArgs(mode), caffeinateDescription(mode));
|
|
24
|
+
}
|
|
25
|
+
if (process.platform === "linux") {
|
|
26
|
+
if (isWsl() && commandExists("powershell.exe")) {
|
|
27
|
+
return windowsPowerInhibitorCommand("powershell.exe", mode);
|
|
28
|
+
}
|
|
29
|
+
if (commandExists("systemd-inhibit")) {
|
|
30
|
+
const what = mode === "display" ? "idle:sleep" : "sleep";
|
|
31
|
+
return parentBoundUnixCommand(
|
|
32
|
+
"systemd-inhibit",
|
|
33
|
+
[`--what=${what}`, "--who=pi-caffeinate", "--why=Pi agent is running", "--mode=block", "sleep", "infinity"],
|
|
34
|
+
`systemd-inhibit (${formatMode(mode)})`,
|
|
35
|
+
mode === "display",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (commandExists("caffeinate")) {
|
|
39
|
+
return parentBoundUnixCommand(
|
|
40
|
+
"caffeinate",
|
|
41
|
+
macCaffeinateArgs(mode),
|
|
42
|
+
caffeinateDescription(mode),
|
|
43
|
+
mode === "display",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (process.platform === "win32") return windowsPowerInhibitorCommand("powershell.exe", mode);
|
|
48
|
+
return undefined;
|
|
60
49
|
}
|
|
61
50
|
|
|
62
51
|
function macCaffeinateArgs(mode: CaffeinateMode) {
|
|
63
|
-
|
|
52
|
+
return mode === "sleep" ? ["-ims"] : ["-dimsu"];
|
|
64
53
|
}
|
|
65
54
|
|
|
66
55
|
function caffeinateDescription(mode: CaffeinateMode) {
|
|
67
|
-
|
|
56
|
+
return `caffeinate (${formatMode(mode)})`;
|
|
68
57
|
}
|
|
69
58
|
|
|
70
59
|
function parentBoundUnixCommand(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
command: string,
|
|
61
|
+
args: string[],
|
|
62
|
+
description: string,
|
|
63
|
+
addDbusIdleInhibit = false,
|
|
75
64
|
): InhibitorCommand {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
String(process.pid),
|
|
83
|
-
command,
|
|
84
|
-
...args,
|
|
85
|
-
],
|
|
86
|
-
description,
|
|
87
|
-
...(addDbusIdleInhibit ? { addDbusIdleInhibit: true } : {}),
|
|
88
|
-
};
|
|
65
|
+
return {
|
|
66
|
+
command: "sh",
|
|
67
|
+
args: ["-c", unixParentBoundScript(), "pi-caffeinate-watch", String(process.pid), command, ...args],
|
|
68
|
+
description,
|
|
69
|
+
...(addDbusIdleInhibit ? { addDbusIdleInhibit: true } : {}),
|
|
70
|
+
};
|
|
89
71
|
}
|
|
90
72
|
|
|
91
73
|
function unixParentBoundScript() {
|
|
92
|
-
|
|
74
|
+
return `parent=$1; shift; "$@" & child=$!; ( while kill -0 "$parent" 2>/dev/null; do sleep 5; done; kill "$child" 2>/dev/null ) & watcher=$!; cleanup() { kill "$watcher" 2>/dev/null; kill "$child" 2>/dev/null; wait "$child" 2>/dev/null; }; trap 'cleanup; exit 0' INT TERM HUP EXIT; wait "$child"; status=$?; kill "$watcher" 2>/dev/null; trap - EXIT; exit "$status"`;
|
|
93
75
|
}
|
|
94
76
|
|
|
95
77
|
function commandExists(command: string) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
78
|
+
const searchPath = process.env.PATH ?? "";
|
|
79
|
+
const extensions = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
80
|
+
for (const directory of searchPath.split(process.platform === "win32" ? ";" : ":")) {
|
|
81
|
+
if (!directory) continue;
|
|
82
|
+
for (const extension of extensions) {
|
|
83
|
+
if (existsSync(path.join(directory, `${command}${extension}`))) return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
105
87
|
}
|
|
106
88
|
|
|
107
89
|
export function splitCommand(input: string) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
90
|
+
const parts: string[] = [];
|
|
91
|
+
let current = "";
|
|
92
|
+
let quote: '"' | "'" | undefined;
|
|
93
|
+
let escaping = false;
|
|
94
|
+
for (const char of input) {
|
|
95
|
+
if (escaping) {
|
|
96
|
+
current += char;
|
|
97
|
+
escaping = false;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (char === "\\") {
|
|
101
|
+
escaping = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if ((char === '"' || char === "'") && !quote) {
|
|
105
|
+
quote = char;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (char === quote) {
|
|
109
|
+
quote = undefined;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (/\s/.test(char) && !quote) {
|
|
113
|
+
if (current) {
|
|
114
|
+
parts.push(current);
|
|
115
|
+
current = "";
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
current += char;
|
|
120
|
+
}
|
|
121
|
+
if (current) parts.push(current);
|
|
122
|
+
return parts;
|
|
141
123
|
}
|
|
142
124
|
|
|
143
125
|
function windowsPowerInhibitorCommand(command: string, mode: CaffeinateMode): InhibitorCommand {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
126
|
+
return {
|
|
127
|
+
command,
|
|
128
|
+
args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", windowsInhibitorScript(mode)],
|
|
129
|
+
description: `PowerShell SetThreadExecutionState (${formatMode(mode)})`,
|
|
130
|
+
releaseOnStdinClose: true,
|
|
131
|
+
};
|
|
150
132
|
}
|
|
151
133
|
|
|
152
134
|
export function windowsInhibitorScript(mode: CaffeinateMode) {
|
|
153
|
-
|
|
154
|
-
|
|
135
|
+
const flags = mode === "sleep" ? "0x80000001" : "0x80000003";
|
|
136
|
+
return `$ErrorActionPreference = 'Stop'; Add-Type -Namespace Native -Name Power -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags);'; $flags = [uint32]'${flags}'; $release = [uint32]'0x80000000'; $stdin = [Console]::OpenStandardInput(); $buffer = New-Object byte[] 1; $readTask = $stdin.ReadAsync($buffer, 0, 1); try { while ($true) { [Native.Power]::SetThreadExecutionState($flags) | Out-Null; if ($readTask.Wait(30000)) { break } } } finally { [Native.Power]::SetThreadExecutionState($release) | Out-Null }`;
|
|
155
137
|
}
|
|
156
138
|
|
|
157
139
|
function isWsl() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
140
|
+
try {
|
|
141
|
+
return existsSync("/proc/sys/fs/binfmt_misc/WSLInterop");
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
163
145
|
}
|
|
164
146
|
|
|
165
147
|
export function formatMode(mode: CaffeinateMode) {
|
|
166
|
-
|
|
148
|
+
return mode === "sleep" ? "system-awake" : "display-awake";
|
|
167
149
|
}
|