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