botmux 3.3.0 → 3.4.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.
Files changed (147) hide show
  1. package/dist/adapters/backend/sandbox.d.ts +47 -0
  2. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  3. package/dist/adapters/backend/sandbox.js +137 -0
  4. package/dist/adapters/backend/sandbox.js.map +1 -1
  5. package/dist/adapters/cli/read-isolation.d.ts +72 -3
  6. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  7. package/dist/adapters/cli/read-isolation.js +143 -6
  8. package/dist/adapters/cli/read-isolation.js.map +1 -1
  9. package/dist/adapters/cli/registry.d.ts.map +1 -1
  10. package/dist/adapters/cli/registry.js +8 -2
  11. package/dist/adapters/cli/registry.js.map +1 -1
  12. package/dist/cli/pm2-command.d.ts.map +1 -1
  13. package/dist/cli/pm2-command.js +6 -0
  14. package/dist/cli/pm2-command.js.map +1 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +13 -0
  17. package/dist/cli.js.map +1 -1
  18. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  19. package/dist/core/dashboard-ipc-server.js +21 -0
  20. package/dist/core/dashboard-ipc-server.js.map +1 -1
  21. package/dist/core/device-isolation-activation.d.ts +56 -0
  22. package/dist/core/device-isolation-activation.d.ts.map +1 -0
  23. package/dist/core/device-isolation-activation.js +110 -0
  24. package/dist/core/device-isolation-activation.js.map +1 -0
  25. package/dist/core/device-isolation-daemon.d.ts +84 -0
  26. package/dist/core/device-isolation-daemon.d.ts.map +1 -0
  27. package/dist/core/device-isolation-daemon.js +591 -0
  28. package/dist/core/device-isolation-daemon.js.map +1 -0
  29. package/dist/core/types.d.ts +9 -0
  30. package/dist/core/types.d.ts.map +1 -1
  31. package/dist/core/types.js.map +1 -1
  32. package/dist/core/worker-pool.d.ts.map +1 -1
  33. package/dist/core/worker-pool.js +35 -0
  34. package/dist/core/worker-pool.js.map +1 -1
  35. package/dist/daemon.d.ts.map +1 -1
  36. package/dist/daemon.js +5 -0
  37. package/dist/daemon.js.map +1 -1
  38. package/dist/desktop/main/auto-start.d.ts +2 -0
  39. package/dist/desktop/main/auto-start.d.ts.map +1 -1
  40. package/dist/desktop/main/auto-start.js +10 -6
  41. package/dist/desktop/main/auto-start.js.map +1 -1
  42. package/dist/desktop/main/bundled-runtime.d.ts +12 -0
  43. package/dist/desktop/main/bundled-runtime.d.ts.map +1 -0
  44. package/dist/desktop/main/bundled-runtime.js +30 -0
  45. package/dist/desktop/main/bundled-runtime.js.map +1 -0
  46. package/dist/desktop/main/device-status.d.ts +15 -0
  47. package/dist/desktop/main/device-status.d.ts.map +1 -0
  48. package/dist/desktop/main/device-status.js +94 -0
  49. package/dist/desktop/main/device-status.js.map +1 -0
  50. package/dist/desktop/main/external-runtime.d.ts +9 -0
  51. package/dist/desktop/main/external-runtime.d.ts.map +1 -1
  52. package/dist/desktop/main/external-runtime.js +88 -18
  53. package/dist/desktop/main/external-runtime.js.map +1 -1
  54. package/dist/desktop/main/ipc.d.ts.map +1 -1
  55. package/dist/desktop/main/ipc.js +1 -0
  56. package/dist/desktop/main/ipc.js.map +1 -1
  57. package/dist/desktop/main/node-command.d.ts +21 -0
  58. package/dist/desktop/main/node-command.d.ts.map +1 -1
  59. package/dist/desktop/main/node-command.js +41 -0
  60. package/dist/desktop/main/node-command.js.map +1 -1
  61. package/dist/desktop/main/pm2-apps.d.ts +4 -2
  62. package/dist/desktop/main/pm2-apps.d.ts.map +1 -1
  63. package/dist/desktop/main/pm2-apps.js +11 -3
  64. package/dist/desktop/main/pm2-apps.js.map +1 -1
  65. package/dist/desktop/main/runtime-service.d.ts +21 -5
  66. package/dist/desktop/main/runtime-service.d.ts.map +1 -1
  67. package/dist/desktop/main/runtime-service.js +94 -10
  68. package/dist/desktop/main/runtime-service.js.map +1 -1
  69. package/dist/desktop/main/runtime-source.d.ts +2 -0
  70. package/dist/desktop/main/runtime-source.d.ts.map +1 -1
  71. package/dist/desktop/main/runtime-source.js +12 -6
  72. package/dist/desktop/main/runtime-source.js.map +1 -1
  73. package/dist/desktop/main/tray.d.ts +1 -0
  74. package/dist/desktop/main/tray.d.ts.map +1 -1
  75. package/dist/desktop/main/tray.js +3 -3
  76. package/dist/desktop/main/tray.js.map +1 -1
  77. package/dist/desktop/main.js +28 -8
  78. package/dist/desktop/main.js.map +1 -1
  79. package/dist/desktop/preload.js +3 -0
  80. package/dist/desktop/preload.js.map +1 -1
  81. package/dist/desktop/renderer/app.d.ts +2 -1
  82. package/dist/desktop/renderer/app.d.ts.map +1 -1
  83. package/dist/desktop/renderer/app.js +6 -1
  84. package/dist/desktop/renderer/app.js.map +1 -1
  85. package/dist/desktop/renderer/runtime-state.d.ts.map +1 -1
  86. package/dist/desktop/renderer/runtime-state.js +2 -0
  87. package/dist/desktop/renderer/runtime-state.js.map +1 -1
  88. package/dist/desktop/shared/shell-path-probes.d.ts +24 -0
  89. package/dist/desktop/shared/shell-path-probes.d.ts.map +1 -0
  90. package/dist/desktop/shared/shell-path-probes.js +38 -0
  91. package/dist/desktop/shared/shell-path-probes.js.map +1 -0
  92. package/dist/desktop/shared/types.d.ts +21 -1
  93. package/dist/desktop/shared/types.d.ts.map +1 -1
  94. package/dist/desktop/smoke.d.ts +1 -0
  95. package/dist/desktop/smoke.d.ts.map +1 -1
  96. package/dist/desktop/smoke.js +20 -5
  97. package/dist/desktop/smoke.js.map +1 -1
  98. package/dist/platform/bind.d.ts +4 -1
  99. package/dist/platform/bind.d.ts.map +1 -1
  100. package/dist/platform/bind.js +10 -1
  101. package/dist/platform/bind.js.map +1 -1
  102. package/dist/platform/binding.d.ts.map +1 -1
  103. package/dist/platform/binding.js +15 -9
  104. package/dist/platform/binding.js.map +1 -1
  105. package/dist/platform/device-command.d.ts +35 -0
  106. package/dist/platform/device-command.d.ts.map +1 -0
  107. package/dist/platform/device-command.js +187 -0
  108. package/dist/platform/device-command.js.map +1 -0
  109. package/dist/platform/device-enroll.d.ts +156 -0
  110. package/dist/platform/device-enroll.d.ts.map +1 -0
  111. package/dist/platform/device-enroll.js +781 -0
  112. package/dist/platform/device-enroll.js.map +1 -0
  113. package/dist/platform/device-isolation-activation-client.d.ts +28 -0
  114. package/dist/platform/device-isolation-activation-client.d.ts.map +1 -0
  115. package/dist/platform/device-isolation-activation-client.js +279 -0
  116. package/dist/platform/device-isolation-activation-client.js.map +1 -0
  117. package/dist/platform/device-isolation.d.ts +35 -0
  118. package/dist/platform/device-isolation.d.ts.map +1 -0
  119. package/dist/platform/device-isolation.js +90 -0
  120. package/dist/platform/device-isolation.js.map +1 -0
  121. package/dist/platform/device-paths.d.ts +9 -0
  122. package/dist/platform/device-paths.d.ts.map +1 -0
  123. package/dist/platform/device-paths.js +14 -0
  124. package/dist/platform/device-paths.js.map +1 -0
  125. package/dist/platform/device.d.ts +77 -0
  126. package/dist/platform/device.d.ts.map +1 -0
  127. package/dist/platform/device.js +212 -0
  128. package/dist/platform/device.js.map +1 -0
  129. package/dist/platform/host-command-context.d.ts +15 -0
  130. package/dist/platform/host-command-context.d.ts.map +1 -0
  131. package/dist/platform/host-command-context.js +26 -0
  132. package/dist/platform/host-command-context.js.map +1 -0
  133. package/dist/platform/secure-host-file.d.ts +12 -0
  134. package/dist/platform/secure-host-file.d.ts.map +1 -0
  135. package/dist/platform/secure-host-file.js +182 -0
  136. package/dist/platform/secure-host-file.js.map +1 -0
  137. package/dist/types.d.ts +9 -0
  138. package/dist/types.d.ts.map +1 -1
  139. package/dist/utils/atomic-write.d.ts +13 -0
  140. package/dist/utils/atomic-write.d.ts.map +1 -1
  141. package/dist/utils/atomic-write.js +89 -9
  142. package/dist/utils/atomic-write.js.map +1 -1
  143. package/dist/worker.js +289 -19
  144. package/dist/worker.js.map +1 -1
  145. package/dist/workflows/events/payloads.d.ts +4 -4
  146. package/dist/workflows/events/schema.d.ts +16 -16
  147. package/package.json +6 -3
@@ -1,25 +1,32 @@
1
1
  // 平台绑定状态:存在 ~/.botmux/platform.json,记录这台机器绑到了哪个平台。
2
- import { readFileSync, existsSync, rmSync } from 'node:fs';
3
2
  import { join } from 'node:path';
4
3
  import { homedir } from 'node:os';
5
- import { atomicWriteFileSync } from '../utils/atomic-write.js';
4
+ import { readSecureHostFileSync, unlinkSecureHostFileSync, UnsafeHostAuthorityFileError, writeSecureHostFileSync, } from './secure-host-file.js';
6
5
  export const PLATFORM_BINDING_PATH = join(homedir(), '.botmux', 'platform.json');
7
6
  export function readPlatformBinding() {
8
7
  try {
9
- if (!existsSync(PLATFORM_BINDING_PATH))
8
+ const raw = readSecureHostFileSync(PLATFORM_BINDING_PATH);
9
+ if (raw === null)
10
10
  return null;
11
- const obj = JSON.parse(readFileSync(PLATFORM_BINDING_PATH, 'utf8'));
11
+ const obj = JSON.parse(raw);
12
12
  if (obj && typeof obj.platformUrl === 'string' && typeof obj.machineToken === 'string' && typeof obj.machineId === 'string') {
13
13
  return obj;
14
14
  }
15
15
  }
16
- catch {
17
- /* ignore */
16
+ catch (error) {
17
+ // File-missing / parse failures still mean "unbound". Permission/symlink
18
+ // failures are different: treat as unbound for callers, but log a clear
19
+ // recovery hint so a hand-copied platform.json does not look like a silent
20
+ // drop-binding mystery.
21
+ if (error instanceof UnsafeHostAuthorityFileError) {
22
+ console.error(`[platform-binding] 拒绝读取 ${PLATFORM_BINDING_PATH}: ${error.message}`
23
+ + '(请确保文件权限为 0600、路径不是符号链接,且 ~/.botmux 不可被组/其他用户写入;例: chmod 600 ~/.botmux/platform.json)');
24
+ }
18
25
  }
19
26
  return null;
20
27
  }
21
28
  export function writePlatformBinding(b) {
22
- atomicWriteFileSync(PLATFORM_BINDING_PATH, JSON.stringify(b, null, 2), { mode: 0o600 });
29
+ writeSecureHostFileSync(PLATFORM_BINDING_PATH, `${JSON.stringify(b, null, 2)}\n`);
23
30
  }
24
31
  /**
25
32
  * 解绑:删除本地平台绑定文件(~/.botmux/platform.json)。
@@ -28,8 +35,7 @@ export function writePlatformBinding(b) {
28
35
  */
29
36
  export function clearPlatformBinding() {
30
37
  try {
31
- if (existsSync(PLATFORM_BINDING_PATH))
32
- rmSync(PLATFORM_BINDING_PATH);
38
+ unlinkSecureHostFileSync(PLATFORM_BINDING_PATH);
33
39
  }
34
40
  catch {
35
41
  /* ignore */
@@ -1 +1 @@
1
- {"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/platform/binding.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAwB/D,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAEjF,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC;YAAE,OAAO,IAAI,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC;QACpE,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC5H,OAAO,GAAsB,CAAC;QAChC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAAkB;IACrD,mBAAmB,CAAC,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,qBAAqB,CAAC;YAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACjC,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAClD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,gBAAgB,CAAC,KAAqB;IACpD,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;IAChB,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/platform/binding.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAwB/B,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAEjF,MAAM,UAAU,mBAAmB;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,qBAAqB,CAAC,CAAC;QAC1D,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC5H,OAAO,GAAsB,CAAC;QAChC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yEAAyE;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,wBAAwB;QACxB,IAAI,KAAK,YAAY,4BAA4B,EAAE,CAAC;YAClD,OAAO,CAAC,KAAK,CACX,2BAA2B,qBAAqB,KAAK,KAAK,CAAC,OAAO,EAAE;kBAClE,uFAAuF,CAC1F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAAkB;IACrD,uBAAuB,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC;QACH,wBAAwB,CAAC,qBAAqB,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACjC,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAClD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,gBAAgB,CAAC,KAAqB;IACpD,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;IAChC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;IAChB,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type PlatformBinding } from './binding.js';
2
+ import { type DeviceCredentialPathOptions, type DevicePublicStatus } from './device.js';
3
+ import { type DeviceEnrollmentApi } from './device-enroll.js';
4
+ export type { DeviceEnrollmentApi } from './device-enroll.js';
5
+ export interface DeviceCommandDependencies extends DeviceCredentialPathOptions {
6
+ env?: NodeJS.ProcessEnv;
7
+ dataDir?: string;
8
+ startPid?: number;
9
+ stdout?: (line: string) => void;
10
+ stderr?: (line: string) => void;
11
+ readBinding?: () => PlatformBinding | null;
12
+ createClient?: (issuer: string) => DeviceEnrollmentApi;
13
+ hostName?: () => string;
14
+ isAgentContext?: () => boolean;
15
+ now?: () => Date;
16
+ isolationSupported?: () => boolean;
17
+ ensureIsolationMarker?: () => {
18
+ created: boolean;
19
+ path: string;
20
+ };
21
+ activateIsolation?: () => Promise<{
22
+ activated: boolean;
23
+ daemonCount: number;
24
+ }>;
25
+ }
26
+ /**
27
+ * UX guard for accidental invocation from a managed AI CLI. The mandatory OS
28
+ * credential mask is the actual security boundary; this early check only
29
+ * provides a clearer error. Never trust child-controlled SESSION_DATA_DIR/HOME
30
+ * when consulting the daemon breadcrumb and PID markers.
31
+ */
32
+ export declare function isManagedAgentDeviceCommandContext(options?: Pick<DeviceCommandDependencies, 'env' | 'dataDir' | 'startPid'>): boolean;
33
+ export declare function serializeDevicePublicStatus(status: DevicePublicStatus): string;
34
+ export declare function runDeviceCommand(args: string[], dependencies?: DeviceCommandDependencies): Promise<number>;
35
+ //# sourceMappingURL=device-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-command.d.ts","sourceRoot":"","sources":["../../src/platform/device-command.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAYzE,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,WAAW,yBAA0B,SAAQ,2BAA2B;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,qBAAqB,CAAC,EAAE,MAAM;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChF;AASD;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,GAAE,IAAI,CAAC,yBAAyB,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,CAAM,GAC5E,OAAO,CAET;AA8CD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAE9E;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,YAAY,GAAE,yBAA8B,GAC3C,OAAO,CAAC,MAAM,CAAC,CA6HjB"}
@@ -0,0 +1,187 @@
1
+ /** Host-only `botmux device ...` command surface. */
2
+ import { hostname } from 'node:os';
3
+ import { readPlatformBinding } from './binding.js';
4
+ import { isManagedAgentHostCommandContext, } from './host-command-context.js';
5
+ import { deviceCredentialIsolationSupported, ensureDeviceCredentialIsolationMarker, } from './device-isolation.js';
6
+ import { activateDeviceCredentialIsolation, DeviceIsolationDaemonActivationError, } from './device-isolation-activation-client.js';
7
+ import { DeviceCredentialError, readDeviceCredentials, readDevicePublicStatus, } from './device.js';
8
+ import { clearStoredDeviceCredentials, DeviceEnrollmentClient, DeviceProtocolError, enrollStoredDeviceCredentials, } from './device-enroll.js';
9
+ const DEVICE_USAGE = `用法:
10
+ botmux device enroll [--name <设备名>]
11
+ botmux device status [--json]
12
+ botmux device logout
13
+
14
+ 这些命令只能从宿主终端运行,botmux 管理的 AI CLI 会话内一律拒绝。`;
15
+ /**
16
+ * UX guard for accidental invocation from a managed AI CLI. The mandatory OS
17
+ * credential mask is the actual security boundary; this early check only
18
+ * provides a clearer error. Never trust child-controlled SESSION_DATA_DIR/HOME
19
+ * when consulting the daemon breadcrumb and PID markers.
20
+ */
21
+ export function isManagedAgentDeviceCommandContext(options = {}) {
22
+ return isManagedAgentHostCommandContext(options);
23
+ }
24
+ function parseEnrollName(args) {
25
+ let name;
26
+ for (let i = 0; i < args.length; i++) {
27
+ const arg = args[i];
28
+ if (arg === '--name') {
29
+ const value = args[++i];
30
+ if (!value || value.startsWith('-'))
31
+ return { ok: false };
32
+ name = value;
33
+ continue;
34
+ }
35
+ if (arg.startsWith('--name=')) {
36
+ name = arg.slice('--name='.length);
37
+ if (!name)
38
+ return { ok: false };
39
+ continue;
40
+ }
41
+ return { ok: false };
42
+ }
43
+ return { ok: true, ...(name !== undefined ? { name } : {}) };
44
+ }
45
+ function validDeviceName(raw) {
46
+ const name = raw.trim();
47
+ if (!name || name.length > 80 || /[\u0000-\u001f\u007f]/.test(name))
48
+ return null;
49
+ return name;
50
+ }
51
+ function formatDeviceExpiry(deviceExp) {
52
+ const date = new Date(deviceExp);
53
+ return Number.isNaN(date.getTime()) ? String(deviceExp) : date.toISOString();
54
+ }
55
+ function safeErrorMessage(error) {
56
+ if (error instanceof DeviceCredentialError
57
+ || error instanceof DeviceProtocolError
58
+ || error instanceof DeviceIsolationDaemonActivationError) {
59
+ return error.message;
60
+ }
61
+ // Do not reflect arbitrary transport/parser errors: third-party code may
62
+ // accidentally include a bearer or response body in Error.message.
63
+ return '设备操作失败;请检查平台连接和本地凭证文件后重试';
64
+ }
65
+ export function serializeDevicePublicStatus(status) {
66
+ return JSON.stringify(status);
67
+ }
68
+ export async function runDeviceCommand(args, dependencies = {}) {
69
+ const stdout = dependencies.stdout ?? console.log;
70
+ const stderr = dependencies.stderr ?? console.error;
71
+ const subcommand = (args[0] ?? 'status').toLowerCase();
72
+ const rest = args.slice(1);
73
+ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
74
+ stdout(DEVICE_USAGE);
75
+ return 0;
76
+ }
77
+ if (!['enroll', 'status', 'logout'].includes(subcommand)) {
78
+ stderr(DEVICE_USAGE);
79
+ return 2;
80
+ }
81
+ const inAgent = dependencies.isAgentContext
82
+ ? dependencies.isAgentContext()
83
+ : isManagedAgentDeviceCommandContext(dependencies);
84
+ if (inAgent) {
85
+ stderr('❌ botmux device 命令只能在宿主终端执行;AI CLI 会话不能读取或修改设备凭证。');
86
+ return 2;
87
+ }
88
+ const pathOptions = {
89
+ ...(dependencies.homeDir !== undefined ? { homeDir: dependencies.homeDir } : {}),
90
+ ...(dependencies.filePath !== undefined ? { filePath: dependencies.filePath } : {}),
91
+ };
92
+ try {
93
+ if (subcommand === 'status') {
94
+ if (rest.some(arg => arg !== '--json')) {
95
+ stderr(DEVICE_USAGE);
96
+ return 2;
97
+ }
98
+ const status = readDevicePublicStatus(pathOptions);
99
+ if (rest.includes('--json')) {
100
+ stdout(serializeDevicePublicStatus(status));
101
+ }
102
+ else if (!status.enrolled) {
103
+ stdout('未注册 desktop device。运行 `botmux device enroll` 开始注册。');
104
+ }
105
+ else {
106
+ stdout('✓ Desktop device 已注册');
107
+ stdout(` 平台: ${status.issuer}`);
108
+ stdout(` 绝对到期: ${formatDeviceExpiry(status.deviceExp)}`);
109
+ stdout(` 本地凭证更新时间: ${status.savedAt}`);
110
+ }
111
+ return 0;
112
+ }
113
+ if (subcommand === 'logout') {
114
+ if (rest.length > 0) {
115
+ stderr(DEVICE_USAGE);
116
+ return 2;
117
+ }
118
+ if (readDeviceCredentials(pathOptions)) {
119
+ (dependencies.ensureIsolationMarker ?? (() => ensureDeviceCredentialIsolationMarker({
120
+ ...(dependencies.homeDir !== undefined ? { homeDir: dependencies.homeDir } : {}),
121
+ ...(dependencies.now ? { now: dependencies.now } : {}),
122
+ })))();
123
+ }
124
+ const removed = await clearStoredDeviceCredentials(pathOptions);
125
+ if (removed) {
126
+ stdout('✓ 已删除本机 desktop device 凭证。');
127
+ stdout(' 这不会替代服务端吊销;若凭证可能泄露,请同时在平台设备管理页吊销该设备。');
128
+ }
129
+ else {
130
+ stdout('本机没有 desktop device 凭证,无需退出。');
131
+ }
132
+ return 0;
133
+ }
134
+ const parsed = parseEnrollName(rest);
135
+ if (!parsed.ok) {
136
+ stderr(DEVICE_USAGE);
137
+ return 2;
138
+ }
139
+ const deviceName = validDeviceName(parsed.name ?? (dependencies.hostName ?? hostname)());
140
+ if (!deviceName) {
141
+ stderr('设备名不能为空、不能包含控制字符,且最长 80 个字符。');
142
+ return 2;
143
+ }
144
+ const isolationSupported = (dependencies.isolationSupported
145
+ ?? deviceCredentialIsolationSupported)();
146
+ if (!isolationSupported) {
147
+ stderr('本机无法建立 mandatory device credential isolation;Windows 本期不支持,macOS 需要 sandbox-exec,Linux 需要 bubblewrap。拒绝注册。');
148
+ return 1;
149
+ }
150
+ if (readDeviceCredentials(pathOptions)) {
151
+ stderr('本机已有 desktop device 凭证;如需换平台或重新注册,请先执行 `botmux device logout`。');
152
+ return 1;
153
+ }
154
+ const binding = (dependencies.readBinding ?? readPlatformBinding)();
155
+ if (!binding) {
156
+ stderr('本机尚未绑定平台。请先从平台网页复制并执行 `botmux bind <凭证>`。');
157
+ return 1;
158
+ }
159
+ const activation = await (dependencies.activateIsolation ?? (() => activateDeviceCredentialIsolation({
160
+ ...(dependencies.homeDir !== undefined ? { homeDir: dependencies.homeDir } : {}),
161
+ ...(dependencies.now ? { now: dependencies.now } : {}),
162
+ })))();
163
+ if (activation.activated) {
164
+ stdout(`✓ 已冻结并清退 ${activation.daemonCount} 个 daemon 的旧本地 CLI,会话后续将在凭证隔离下冷启动。`);
165
+ }
166
+ const client = (dependencies.createClient ?? ((issuer) => new DeviceEnrollmentClient(issuer)))(binding.platformUrl);
167
+ const stored = await enrollStoredDeviceCredentials({
168
+ client,
169
+ machineToken: binding.machineToken,
170
+ deviceName,
171
+ ...pathOptions,
172
+ ...(dependencies.now ? { now: dependencies.now } : {}),
173
+ onGrantReady: () => {
174
+ stdout('已向机器 owner 发送飞书确认,请在 5 分钟内确认本次 desktop device 注册…');
175
+ },
176
+ });
177
+ stdout('✓ Desktop device 注册完成,凭证已安全写入本机(0600)。');
178
+ stdout(` 平台: ${stored.issuer}`);
179
+ stdout(` 绝对到期: ${formatDeviceExpiry(stored.deviceExp)}`);
180
+ return 0;
181
+ }
182
+ catch (error) {
183
+ stderr(`❌ ${safeErrorMessage(error)}`);
184
+ return 1;
185
+ }
186
+ }
187
+ //# sourceMappingURL=device-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-command.js","sourceRoot":"","sources":["../../src/platform/device-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ import { type DeviceCredentialPathOptions, type DeviceCredentials } from './device.js';
2
+ export declare const DEVICE_ENROLL_ENDPOINTS: Readonly<{
3
+ begin: "/api/devices/enroll";
4
+ poll: "/api/devices/enroll/poll";
5
+ refresh: "/api/devices/refresh";
6
+ }>;
7
+ export interface DeviceEnrollEndpoints {
8
+ begin: string;
9
+ poll: string;
10
+ refresh: string;
11
+ }
12
+ /**
13
+ * Wire contract consumed by the open client. The closed-source server owns
14
+ * implementation details but must return these fields before the CLI is
15
+ * enabled end-to-end.
16
+ */
17
+ export interface DeviceEnrollmentGrant {
18
+ grantId: string;
19
+ pollSecret: string;
20
+ /** Absolute grant expiry as Unix epoch ms. */
21
+ expiresAt: number;
22
+ /** Server-requested minimum polling delay. Optional; Retry-After also works. */
23
+ pollIntervalMs?: number;
24
+ }
25
+ export interface DeviceTokenPair {
26
+ accessToken: string;
27
+ /** Absolute access-token expiry as Unix epoch ms; used for proactive lease renewal. */
28
+ accessExpiresAt: number;
29
+ refreshToken: string;
30
+ /** Absolute device expiry as Unix epoch ms. */
31
+ deviceExp: number;
32
+ }
33
+ export type DeviceEnrollmentPoll = {
34
+ kind: 'pending';
35
+ retryAfterMs: number;
36
+ } | {
37
+ kind: 'issued';
38
+ credentials: DeviceTokenPair;
39
+ } | {
40
+ kind: 'denied';
41
+ } | {
42
+ kind: 'expired';
43
+ };
44
+ export interface DeviceEnrollmentApi {
45
+ readonly issuer: string;
46
+ beginEnrollment(input: BeginDeviceEnrollmentInput): Promise<DeviceEnrollmentGrant>;
47
+ waitForEnrollment(grant: DeviceEnrollmentGrant, options?: WaitForDeviceEnrollmentOptions): Promise<DeviceTokenPair>;
48
+ }
49
+ export interface BeginDeviceEnrollmentInput {
50
+ machineToken: string;
51
+ deviceName: string;
52
+ deviceKind?: 'desktop-ide';
53
+ signal?: AbortSignal;
54
+ }
55
+ export interface WaitForDeviceEnrollmentOptions {
56
+ /** Local upper bound; server grant expiry can shorten it. Defaults to 5min. */
57
+ timeoutMs?: number;
58
+ signal?: AbortSignal;
59
+ onPending?: () => void;
60
+ }
61
+ export interface RefreshDeviceOptions {
62
+ /** Durable idempotency key; required by the safe stored-refresh path. */
63
+ requestId: string;
64
+ signal?: AbortSignal;
65
+ }
66
+ export interface StoredDeviceRefreshOptions extends DeviceCredentialPathOptions {
67
+ signal?: AbortSignal;
68
+ now?: () => Date;
69
+ requestIdFactory?: () => string;
70
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
71
+ createClient?: (issuer: string) => Pick<DeviceEnrollmentClient, 'issuer' | 'refresh'>;
72
+ }
73
+ export interface StoredDeviceMutationOptions extends DeviceCredentialPathOptions {
74
+ now?: () => Date;
75
+ }
76
+ export interface DeviceHttpRequest {
77
+ url: string;
78
+ method: 'POST';
79
+ headers: Readonly<Record<string, string>>;
80
+ body: unknown;
81
+ timeoutMs: number;
82
+ signal?: AbortSignal;
83
+ }
84
+ export interface DeviceHttpResponse {
85
+ status: number;
86
+ body: unknown;
87
+ /** Header names must be lowercase. */
88
+ headers?: Readonly<Record<string, string | undefined>>;
89
+ }
90
+ export type DeviceHttpTransport = (request: DeviceHttpRequest) => Promise<DeviceHttpResponse>;
91
+ export interface DeviceEnrollmentClientOptions {
92
+ transport?: DeviceHttpTransport;
93
+ endpoints?: Partial<DeviceEnrollEndpoints>;
94
+ timeoutMs?: number;
95
+ now?: () => number;
96
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
97
+ }
98
+ export declare class DeviceProtocolError extends Error {
99
+ readonly code: 'network_error' | 'invalid_response' | 'request_rejected' | 'enrollment_timeout' | 'enrollment_denied' | 'enrollment_expired' | 'aborted';
100
+ readonly status?: number | undefined;
101
+ readonly serverCode?: DeviceProtocolServerCode | undefined;
102
+ /** Parsed and bounded Retry-After delay for a transient HTTP response. */
103
+ readonly retryAfterMs?: number | undefined;
104
+ constructor(message: string, code: 'network_error' | 'invalid_response' | 'request_rejected' | 'enrollment_timeout' | 'enrollment_denied' | 'enrollment_expired' | 'aborted', status?: number | undefined, serverCode?: DeviceProtocolServerCode | undefined,
105
+ /** Parsed and bounded Retry-After delay for a transient HTTP response. */
106
+ retryAfterMs?: number | undefined);
107
+ }
108
+ export type DeviceProtocolServerCode = 'bad_request' | 'invalid_refresh' | 'device_gone' | 'device_revoked' | 'device_expired' | 'recovery_required' | 'refresh_in_progress' | 'store_unavailable';
109
+ /**
110
+ * Small JSON transport with a bounded response and no redirect following.
111
+ * Redirects are surfaced as a rejection so a bearer can never cross origins.
112
+ */
113
+ export declare const nodeDeviceHttpTransport: DeviceHttpTransport;
114
+ export declare class DeviceEnrollmentClient {
115
+ readonly issuer: string;
116
+ private readonly transport;
117
+ private readonly endpoints;
118
+ private readonly timeoutMs;
119
+ private readonly now;
120
+ private readonly sleep;
121
+ constructor(issuer: string, options?: DeviceEnrollmentClientOptions);
122
+ private normalizeEndpoint;
123
+ private post;
124
+ beginEnrollment(input: BeginDeviceEnrollmentInput): Promise<DeviceEnrollmentGrant>;
125
+ pollEnrollment(grant: Pick<DeviceEnrollmentGrant, 'grantId' | 'pollSecret'>, signal?: AbortSignal): Promise<DeviceEnrollmentPoll>;
126
+ waitForEnrollment(grant: DeviceEnrollmentGrant, options?: WaitForDeviceEnrollmentOptions): Promise<DeviceTokenPair>;
127
+ /**
128
+ * Send one refresh attempt. The stored-credential coordinator performs
129
+ * bounded same-key retries for ambiguous failures inside the replay window;
130
+ * keeping retry policy outside this wire primitive also keeps direct callers
131
+ * from accidentally retrying with a new key.
132
+ */
133
+ refresh(refreshToken: string, options: RefreshDeviceOptions): Promise<DeviceTokenPair>;
134
+ }
135
+ export { DEVICE_ENROLLMENT_JOURNAL_FILE } from './device-paths.js';
136
+ export interface StoredDeviceEnrollmentOptions extends StoredDeviceMutationOptions {
137
+ client: DeviceEnrollmentApi;
138
+ machineToken: string;
139
+ deviceName: string;
140
+ signal?: AbortSignal;
141
+ onPending?: () => void;
142
+ onGrantReady?: () => void;
143
+ }
144
+ export declare function deviceEnrollmentJournalPath(options?: DeviceCredentialPathOptions): string;
145
+ /**
146
+ * Crash-safe F1 enrollment coordinator.
147
+ *
148
+ * The grant/poll secret is durably journaled before polling. A later host
149
+ * process resumes the same grant, and an issued response is installed together
150
+ * with journal cleanup under the same device lock.
151
+ */
152
+ export declare function enrollStoredDeviceCredentials(options: StoredDeviceEnrollmentOptions): Promise<DeviceCredentials>;
153
+ /** Serialize logout with refresh/install so a completed refresh cannot revive it. */
154
+ export declare function clearStoredDeviceCredentials(options?: DeviceCredentialPathOptions): Promise<boolean>;
155
+ export declare function refreshStoredDeviceCredentials(options?: StoredDeviceRefreshOptions, foreignJournalDepth?: number): Promise<DeviceCredentials>;
156
+ //# sourceMappingURL=device-enroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-enroll.d.ts","sourceRoot":"","sources":["../../src/platform/device-enroll.ts"],"names":[],"mappings":"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"}