@narumitw/pi-caffeinate 0.1.31 → 0.1.33

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/caffeinate.ts +46 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-caffeinate",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Pi extension that keeps the computer awake while the agent is running.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/caffeinate.ts CHANGED
@@ -11,6 +11,7 @@ interface InhibitorCommand {
11
11
  command: string;
12
12
  args: string[];
13
13
  description: string;
14
+ releaseOnStdinClose?: boolean;
14
15
  }
15
16
 
16
17
  interface CaffeinateState {
@@ -92,7 +93,7 @@ function startInhibitor(ctx: ExtensionContext) {
92
93
  try {
93
94
  const child = spawn(command.command, command.args, {
94
95
  detached: false,
95
- stdio: ["ignore", "pipe", "pipe"],
96
+ stdio: [command.releaseOnStdinClose ? "pipe" : "ignore", "pipe", "pipe"],
96
97
  });
97
98
 
98
99
  state.process = child;
@@ -144,13 +145,24 @@ function stopInhibitor(ctx: ExtensionContext, reason: string, options: { notify?
144
145
  child.removeAllListeners("error");
145
146
 
146
147
  if (!child.killed) {
147
- if (process.platform === "win32") {
148
+ if (state.command?.releaseOnStdinClose && child.stdin && !child.stdin.destroyed) {
149
+ child.stdin.end();
150
+ if (child.exitCode === null && child.signalCode === null) {
151
+ const killTimer = setTimeout(() => {
152
+ if (child.exitCode === null && child.signalCode === null && !child.killed) child.kill();
153
+ }, 2000);
154
+ killTimer.unref();
155
+ child.once("exit", () => clearTimeout(killTimer));
156
+ }
157
+ } else if (process.platform === "win32") {
148
158
  child.kill();
149
159
  } else {
150
160
  child.kill("SIGTERM");
151
161
  }
152
162
  }
153
163
 
164
+ state.command = undefined;
165
+
154
166
  if (options.notify !== false) {
155
167
  ctx.ui.notify(`Released pi-caffeinate (${reason}).`, "info");
156
168
  }
@@ -164,7 +176,7 @@ function getInhibitorCommand(): InhibitorCommand | undefined {
164
176
  }
165
177
 
166
178
  if (process.platform === "darwin") {
167
- return { command: "caffeinate", args: ["-dimsu"], description: "caffeinate" };
179
+ return parentBoundUnixCommand("caffeinate", ["-dimsu"], "caffeinate");
168
180
  }
169
181
 
170
182
  if (process.platform === "linux") {
@@ -173,9 +185,9 @@ function getInhibitorCommand(): InhibitorCommand | undefined {
173
185
  }
174
186
 
175
187
  if (commandExists("systemd-inhibit")) {
176
- return {
177
- command: "systemd-inhibit",
178
- args: [
188
+ return parentBoundUnixCommand(
189
+ "systemd-inhibit",
190
+ [
179
191
  "--what=idle:sleep",
180
192
  "--who=pi-caffeinate",
181
193
  "--why=Pi agent is running",
@@ -183,12 +195,12 @@ function getInhibitorCommand(): InhibitorCommand | undefined {
183
195
  "sleep",
184
196
  "infinity",
185
197
  ],
186
- description: "systemd-inhibit",
187
- };
198
+ "systemd-inhibit",
199
+ );
188
200
  }
189
201
 
190
202
  if (commandExists("caffeinate")) {
191
- return { command: "caffeinate", args: ["-dimsu"], description: "caffeinate" };
203
+ return parentBoundUnixCommand("caffeinate", ["-dimsu"], "caffeinate");
192
204
  }
193
205
  }
194
206
 
@@ -199,6 +211,29 @@ function getInhibitorCommand(): InhibitorCommand | undefined {
199
211
  return undefined;
200
212
  }
201
213
 
214
+ function parentBoundUnixCommand(
215
+ command: string,
216
+ args: string[],
217
+ description: string,
218
+ ): InhibitorCommand {
219
+ return {
220
+ command: "sh",
221
+ args: [
222
+ "-c",
223
+ unixParentBoundScript(),
224
+ "pi-caffeinate-watch",
225
+ String(process.pid),
226
+ command,
227
+ ...args,
228
+ ],
229
+ description,
230
+ };
231
+ }
232
+
233
+ function unixParentBoundScript() {
234
+ 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"`;
235
+ }
236
+
202
237
  function commandExists(command: string) {
203
238
  const path = process.env.PATH ?? "";
204
239
  const extensions = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
@@ -262,11 +297,12 @@ function windowsPowerInhibitorCommand(command: string): InhibitorCommand {
262
297
  command,
263
298
  args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", windowsInhibitorScript()],
264
299
  description: "PowerShell SetThreadExecutionState",
300
+ releaseOnStdinClose: true,
265
301
  };
266
302
  }
267
303
 
268
304
  function windowsInhibitorScript() {
269
- return `Add-Type -Namespace Native -Name Power -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags);'; while ($true) { [Native.Power]::SetThreadExecutionState(0x80000000 -bor 0x00000001 -bor 0x00000002) | Out-Null; Start-Sleep -Seconds 30 }`;
305
+ return `$ErrorActionPreference = 'Stop'; Add-Type -Namespace Native -Name Power -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint esFlags);'; $flags = [uint32]'0x80000003'; $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 }`;
270
306
  }
271
307
 
272
308
  function isWsl() {