@skylandnpm/octopus-proxy 0.1.49 → 0.1.51

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.
@@ -12,7 +12,7 @@ import {
12
12
  probeRemote,
13
13
  remoteSessionBudget,
14
14
  z
15
- } from "./chunk-PPH4OB7Y.js";
15
+ } from "./chunk-XIJKEY4L.js";
16
16
 
17
17
  // ../../node_modules/.pnpm/ajv@8.17.1/node_modules/ajv/dist/compile/codegen/code.js
18
18
  var require_code = __commonJS({
@@ -13152,6 +13152,7 @@ import { randomUUID as randomUUID15 } from "node:crypto";
13152
13152
  import { access as access2, stat as stat9 } from "node:fs/promises";
13153
13153
  import { constants as constants5 } from "node:fs";
13154
13154
  import { platform as platform3, userInfo as userInfo3 } from "node:os";
13155
+ import { delimiter } from "node:path";
13155
13156
  import * as pty from "node-pty";
13156
13157
 
13157
13158
  // src/terminal-support.ts
@@ -13234,6 +13235,15 @@ var TerminalManager = class {
13234
13235
  }
13235
13236
  const { helper } = await ensureTerminalSupport();
13236
13237
  const id = randomUUID15();
13238
+ const path = [
13239
+ process.env.PATH,
13240
+ "/usr/bin",
13241
+ "/bin",
13242
+ "/usr/sbin",
13243
+ "/sbin",
13244
+ "/usr/local/bin",
13245
+ ...platform3() === "darwin" ? ["/opt/homebrew/bin"] : []
13246
+ ].filter(Boolean).join(delimiter);
13237
13247
  let terminalProcess;
13238
13248
  try {
13239
13249
  terminalProcess = pty.spawn(shell, [], {
@@ -13241,7 +13251,7 @@ var TerminalManager = class {
13241
13251
  cols: 80,
13242
13252
  rows: 24,
13243
13253
  cwd,
13244
- env: { PATH: process.env.PATH || "/usr/bin:/bin", HOME: userInfo3().homedir, TERM: "xterm-256color", USER: userInfo3().username }
13254
+ env: { PATH: path, HOME: userInfo3().homedir, TERM: "xterm-256color", USER: userInfo3().username }
13245
13255
  });
13246
13256
  } catch (error) {
13247
13257
  throw new Error(`\u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}\uFF08shell=${shell}\uFF0Ccwd=${cwd}${helper ? `\uFF0Chelper=${helper}` : ""}\uFF0C${process.platform}/${process.arch}\uFF0CNode ${process.version}\uFF09\uFF1B\u8BF7\u5728 Proxy \u4E3B\u673A\u8FD0\u884C octopus-proxy doctor --fix \u68C0\u67E5`);
@@ -4187,7 +4187,7 @@ function assertLgplFFmpeg(output) {
4187
4187
  import { execFile } from "node:child_process";
4188
4188
  import { promisify } from "node:util";
4189
4189
  import { createHash, randomUUID } from "node:crypto";
4190
- import { chmod, mkdir, mkdtemp, readFile, rename, rm, stat } from "node:fs/promises";
4190
+ import { chmod, mkdir, mkdtemp, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
4191
4191
  import { join } from "node:path";
4192
4192
  import { fileURLToPath } from "node:url";
4193
4193
  var DESKTOP_IMAGE_LIMIT = 20 * 1024 * 1024;
@@ -4257,16 +4257,20 @@ var PlatformDesktop = class {
4257
4257
  await mkdir(this.stateDirectory, { recursive: true, mode: 448 });
4258
4258
  const target = join(this.stateDirectory, "octopus-desktop-capture");
4259
4259
  const source = fileURLToPath(new URL("../native/desktop-macos.m", import.meta.url));
4260
- const sourceInfo = await stat(source), existing = await stat(target).catch(() => null);
4261
- if (existing?.isFile() && existing.mtimeMs >= sourceInfo.mtimeMs && (existing.mode & 63) === 0) return target;
4262
- const temporary = `${target}.${randomUUID()}`;
4260
+ const flags = ["-O2", "-fobjc-arc", "-mmacosx-version-min=12.3", "-framework", "Foundation", "-framework", "AppKit", "-framework", "ScreenCaptureKit", "-framework", "CoreMedia", "-framework", "CoreVideo", "-framework", "VideoToolbox", "-framework", "ImageIO"];
4261
+ const fingerprint = createHash("sha256").update(await readFile(source)).update(JSON.stringify([process.arch, flags])).digest("hex");
4262
+ const stamp = `${target}.sha256`, existing = await stat(target).catch(() => null);
4263
+ if (existing?.isFile() && (existing.mode & 63) === 0 && await readFile(stamp, "utf8").catch(() => "") === fingerprint) return target;
4264
+ const temporary = `${target}.${randomUUID()}`, temporaryStamp = `${temporary}.sha256`;
4263
4265
  try {
4264
- await this.execute("/usr/bin/clang", ["-O2", "-fobjc-arc", "-mmacosx-version-min=12.3", "-framework", "Foundation", "-framework", "AppKit", "-framework", "ScreenCaptureKit", "-framework", "CoreMedia", "-framework", "CoreVideo", "-framework", "VideoToolbox", "-framework", "ImageIO", source, "-o", temporary]);
4266
+ await this.execute("/usr/bin/clang", [...flags, source, "-o", temporary]);
4265
4267
  await chmod(temporary, 448);
4268
+ await writeFile(temporaryStamp, fingerprint, { mode: 384 });
4266
4269
  await rename(temporary, target);
4270
+ await rename(temporaryStamp, stamp);
4267
4271
  return target;
4268
4272
  } finally {
4269
- await rm(temporary, { force: true });
4273
+ await Promise.all([rm(temporary, { force: true }), rm(temporaryStamp, { force: true })]);
4270
4274
  }
4271
4275
  })();
4272
4276
  this.helper.catch(() => {
@@ -4447,7 +4451,7 @@ var DesktopManager = class {
4447
4451
  // src/remote-native.ts
4448
4452
  import { execFile as execFile2, spawn } from "node:child_process";
4449
4453
  import { promisify as promisify2 } from "node:util";
4450
- import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, rename as rename2, rm as rm2, stat as stat2, writeFile } from "node:fs/promises";
4454
+ import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, rename as rename2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
4451
4455
  import { join as join2 } from "node:path";
4452
4456
  import { fileURLToPath as fileURLToPath2 } from "node:url";
4453
4457
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
@@ -4461,7 +4465,7 @@ async function ensureRemoteHelper(stateDirectory) {
4461
4465
  const source = fileURLToPath2(new URL(mac ? "../native/remote-macos.m" : "../native/remote-x11.c", import.meta.url));
4462
4466
  const target = join2(stateDirectory, "octopus-remote-desktop");
4463
4467
  const stamp = `${target}.sha256`;
4464
- const flags = mac ? ["-O2", "-fobjc-arc", "-mmacosx-version-min=12.3", "-framework", "Foundation", "-framework", "AppKit", "-framework", "ScreenCaptureKit", "-framework", "CoreMedia", "-framework", "CoreVideo", "-framework", "ApplicationServices", "-framework", "Carbon"] : ["-O2", "-lX11", "-lXtst"];
4468
+ const flags = mac ? ["-O2", "-fobjc-arc", "-mmacosx-version-min=12.3", "-framework", "Foundation", "-framework", "AppKit", "-framework", "ScreenCaptureKit", "-framework", "CoreMedia", "-framework", "CoreVideo", "-framework", "ApplicationServices", "-framework", "Carbon", "-framework", "IOKit"] : ["-O2", "-lX11", "-lXtst"];
4465
4469
  const fingerprint = createHash2("sha256").update(await readFile2(source)).update(JSON.stringify([process.platform, process.arch, flags])).digest("hex");
4466
4470
  const existing = await stat2(target).catch(() => void 0);
4467
4471
  if (existing?.isFile() && (existing.mode & 63) === 0 && await readFile2(stamp, "utf8").catch(() => "") === fingerprint) return target;
@@ -4470,7 +4474,7 @@ async function ensureRemoteHelper(stateDirectory) {
4470
4474
  try {
4471
4475
  await exec(mac ? "/usr/bin/clang" : "cc", args, { timeout: 6e4, maxBuffer: 16384 });
4472
4476
  await chmod2(temporary, 448);
4473
- await writeFile(temporaryStamp, fingerprint, { mode: 384 });
4477
+ await writeFile2(temporaryStamp, fingerprint, { mode: 384 });
4474
4478
  await rename2(temporary, target);
4475
4479
  await rename2(temporaryStamp, stamp);
4476
4480
  return target;
package/dist/cli.js CHANGED
@@ -31,13 +31,13 @@ import {
31
31
  supportedPlatform,
32
32
  systemdUnitPath,
33
33
  toolSearchDirs
34
- } from "./chunk-AIAW33NC.js";
34
+ } from "./chunk-55TEBVWY.js";
35
35
  import {
36
36
  DESKTOP_PERMISSION_GUIDANCE,
37
37
  PlatformDesktop,
38
38
  checkDesktop,
39
39
  requestDesktopPermission
40
- } from "./chunk-PPH4OB7Y.js";
40
+ } from "./chunk-XIJKEY4L.js";
41
41
 
42
42
  // src/cli.ts
43
43
  import { spawn as spawn2 } from "node:child_process";
package/dist/main.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  ConfigError,
4
4
  runDaemon
5
- } from "./chunk-AIAW33NC.js";
6
- import "./chunk-PPH4OB7Y.js";
5
+ } from "./chunk-55TEBVWY.js";
6
+ import "./chunk-XIJKEY4L.js";
7
7
 
8
8
  // src/main.ts
9
9
  runDaemon(process.env.OCTOPUS_PROXY_CONFIG ?? "proxy.json").catch((error) => {
@@ -14,7 +14,7 @@ import {
14
14
  remoteResolutionTiers,
15
15
  remoteSessionBudget,
16
16
  terminateNative
17
- } from "./chunk-PPH4OB7Y.js";
17
+ } from "./chunk-XIJKEY4L.js";
18
18
 
19
19
  // src/remote-worker.ts
20
20
  import rtc from "@roamhq/wrtc";
@@ -106,7 +106,9 @@ int main(int argc, const char *argv[]) {
106
106
  if (argc == 2 && strcmp(argv[1], "list") == 0) {
107
107
  CGDirectDisplayID ids[64];
108
108
  uint32_t count = 0;
109
- if (CGGetActiveDisplayList(64, ids, &count) != kCGErrorSuccess) {
109
+ // Sleeping displays are still connected and must remain selectable so
110
+ // an authenticated remote connection can wake them with user input.
111
+ if (CGGetOnlineDisplayList(64, ids, &count) != kCGErrorSuccess) {
110
112
  fail(@"无法读取活动显示器,请检查 Proxy 是否运行在已登录的图形桌面会话中。" );
111
113
  return 1;
112
114
  }
@@ -3,6 +3,7 @@
3
3
  #import <ApplicationServices/ApplicationServices.h>
4
4
  #import <ScreenCaptureKit/ScreenCaptureKit.h>
5
5
  #import <Carbon/Carbon.h>
6
+ #import <IOKit/pwr_mgt/IOPMLib.h>
6
7
  #include <arpa/inet.h>
7
8
  #include <fcntl.h>
8
9
  #include <signal.h>
@@ -17,6 +18,8 @@ static NSMutableSet<NSNumber *> *heldKeys;
17
18
  static BOOL heldButtons[3];
18
19
  static double lastPing;
19
20
  static pid_t parentPID;
21
+ static IOPMAssertionID activityAssertion = kIOPMNullAssertionID;
22
+ static double lastActivity;
20
23
  static void signalStop(int signal) { stopping = 1; }
21
24
  static BOOL sessionPresent(void) {
22
25
  NSDictionary *s = CFBridgingRelease(CGSessionCopyCurrentDictionary());
@@ -29,6 +32,19 @@ static BOOL screenLocked(void) {
29
32
  return [s[@"CGSSessionScreenIsLocked"] boolValue];
30
33
  }
31
34
  static BOOL inputOK(void) { return AXIsProcessTrusted() && CGPreflightPostEventAccess() && sessionPresent(); }
35
+ // Quartz events can fill the password field without marking the console user
36
+ // active. loginwindow then times out its activity check and reports a bad
37
+ // password. Declare activity on connection and real console input, never on
38
+ // capture/pings. This also wakes a sleeping connected display.
39
+ static void userActivity(void) {
40
+ double now=NSProcessInfo.processInfo.systemUptime;
41
+ if(activityAssertion!=kIOPMNullAssertionID && now-lastActivity<1) return;
42
+ if(IOPMAssertionDeclareUserActivity(CFSTR("Octopus remote console input"),kIOPMUserActiveLocal,&activityAssertion)==kIOReturnSuccess) lastActivity=now;
43
+ }
44
+ static void releaseActivity(void) {
45
+ if(activityAssertion!=kIOPMNullAssertionID) IOPMAssertionRelease(activityAssertion);
46
+ activityAssertion=kIOPMNullAssertionID; lastActivity=0;
47
+ }
32
48
  static void emit(NSDictionary *value) {
33
49
  NSData *data = [NSJSONSerialization dataWithJSONObject:value options:0 error:NULL];
34
50
  if (!data) return;
@@ -205,15 +221,18 @@ static void command(NSString *line,RemoteStream *capture) {
205
221
  }
206
222
  if (([op isEqual:@"pointer"] && parts.count==3) || ([op isEqual:@"button"] && parts.count==5) || ([op isEqual:@"wheel"] && parts.count==5)) {
207
223
  position(parts[1].doubleValue,parts[2].doubleValue);
208
- if ([op isEqual:@"button"]) { int button=parts[3].intValue; if(button<0 || button>2) { stopping=1; return; } heldButtons[button]=parts[4].boolValue; mouseEvent(mouseType(button,heldButtons[button]),mouseButton(button)); }
224
+ if ([op isEqual:@"button"] && (parts[3].intValue<0 || parts[3].intValue>2)) { stopping=1; return; }
225
+ userActivity();
226
+ if ([op isEqual:@"button"]) { int button=parts[3].intValue; heldButtons[button]=parts[4].boolValue; mouseEvent(mouseType(button,heldButtons[button]),mouseButton(button)); }
209
227
  else if ([op isEqual:@"wheel"]) { mouseEvent(kCGEventMouseMoved,kCGMouseButtonLeft); CGEventRef e=CGEventCreateScrollWheelEvent(NULL,kCGScrollEventUnitPixel,2,-parts[4].intValue,-parts[3].intValue); if(e) { CGEventSetFlags(e,flags()); CGEventPost(kCGHIDEventTap,e); CFRelease(e); } }
210
228
  else { CGEventType type=heldButtons[0]?kCGEventLeftMouseDragged:heldButtons[2]?kCGEventRightMouseDragged:heldButtons[1]?kCGEventOtherMouseDragged:kCGEventMouseMoved; mouseEvent(type,heldButtons[2]?kCGMouseButtonRight:heldButtons[1]?kCGMouseButtonCenter:kCGMouseButtonLeft); }
211
- } else if ([op isEqual:@"key"] && parts.count==3) { NSNumber *key=keyCodes()[parts[1]]; if(key) keyEvent(key.unsignedShortValue,parts[2].boolValue); }
229
+ } else if ([op isEqual:@"key"] && parts.count==3) { NSNumber *key=keyCodes()[parts[1]]; if(key) { userActivity(); keyEvent(key.unsignedShortValue,parts[2].boolValue); } }
212
230
  else if (([op isEqual:@"text"] && parts.count==3) || ([op isEqual:@"password"] && parts.count==2)) {
213
231
  BOOL password=[op isEqual:@"password"];
214
232
  NSData *data=[[NSData alloc] initWithBase64EncodedString:parts[password ? 1 : 2] options:0]; NSString *text=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
215
233
  if(!text || data.length>(password ? 1024 : 16384) || (password && (!data.length || [text rangeOfCharacterFromSet:NSCharacterSet.controlCharacterSet].location!=NSNotFound))) { stopping=1; return; }
216
234
  if(password && !screenLocked()) { emit(@{@"kind":@"input-error"}); return; }
235
+ if(text.length) userActivity();
217
236
  releaseAll();
218
237
  for(NSUInteger i=0;i<text.length;) {
219
238
  if(!inputOK()) { stopping=1; return; }
@@ -252,9 +271,15 @@ int main(int argc,const char *argv[]) {
252
271
  emit(@{@"capture":@(CGPreflightScreenCaptureAccess()),@"input":@((BOOL)(AXIsProcessTrusted() && CGPreflightPostEventAccess())),@"session":@(sessionPresent()),@"locked":@(screenLocked()),@"lockCapture":@(lockCapture)}); return 0;
253
272
  }
254
273
  if(argc!=6 || strcmp(argv[1],"serve")!=0 || !inputOK() || !CGPreflightScreenCaptureAccess()) return 1;
255
- displayID=(CGDirectDisplayID)strtoul(argv[2],NULL,10); bounds=CGDisplayBounds(displayID);
274
+ displayID=(CGDirectDisplayID)strtoul(argv[2],NULL,10);
256
275
  int w=atoi(argv[3]),h=atoi(argv[4]),fps=atoi(argv[5]);
257
- if(!CGDisplayIsActive(displayID) || w<2 || h<2 || w>3840 || h>2160 || w%2 || h%2 || fps<1 || fps>30) return 1;
276
+ if(!CGDisplayIsOnline(displayID) || w<2 || h<2 || w>3840 || h>2160 || w%2 || h%2 || fps<1 || fps>30) return 1;
277
+ userActivity();
278
+ NSDate *wakeDeadline=[NSDate dateWithTimeIntervalSinceNow:2];
279
+ while(!CGDisplayIsActive(displayID) && CGDisplayIsOnline(displayID) && inputOK() && wakeDeadline.timeIntervalSinceNow>0)
280
+ [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.02]];
281
+ if(!CGDisplayIsActive(displayID) || !inputOK()) { releaseActivity(); return 1; }
282
+ bounds=CGDisplayBounds(displayID);
258
283
  heldKeys=[NSMutableSet set]; cursor=bounds.origin; parentPID=getppid(); lastPing=NSDate.date.timeIntervalSince1970;
259
284
  signal(SIGTERM,signalStop); signal(SIGINT,signalStop); signal(SIGPIPE,signalStop);
260
285
  fcntl(STDIN_FILENO,F_SETFL,O_NONBLOCK);
@@ -279,7 +304,7 @@ int main(int argc,const char *argv[]) {
279
304
  NSMutableData *incoming=[NSMutableData data];
280
305
  while(!stopping) {
281
306
  @autoreleasepool {
282
- if(getppid()!=parentPID || NSDate.date.timeIntervalSince1970-lastPing>15 || !inputOK() || !CGPreflightScreenCaptureAccess() || !CGDisplayIsActive(displayID) || !CGRectEqualToRect(bounds,CGDisplayBounds(displayID))) break;
307
+ if(getppid()!=parentPID || NSDate.date.timeIntervalSince1970-lastPing>15 || !inputOK() || !CGPreflightScreenCaptureAccess() || !CGDisplayIsOnline(displayID) || !CGRectEqualToRect(bounds,CGDisplayBounds(displayID))) break;
283
308
  if(screenLocked()) [capture captureLockedFrame];
284
309
  else if(capture.streamStopped && capture.stream) {
285
310
  capture.streamStopped=NO;
@@ -297,7 +322,7 @@ int main(int argc,const char *argv[]) {
297
322
  [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.005]];
298
323
  }
299
324
  }
300
- stopping=1; releaseAll(); [capture.stream stopCaptureWithCompletionHandler:nil]; return 0;
325
+ stopping=1; releaseAll(); releaseActivity(); [capture.stream stopCaptureWithCompletionHandler:nil]; return 0;
301
326
  }
302
327
  return 1;
303
328
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skylandnpm/octopus-proxy",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "type": "module",
5
5
  "description": "Octopus host proxy: OpenCode, Codex, ZCode and Pi adapters with service installer CLI",
6
6
  "bin": {