botmux 3.2.1 → 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.
- package/dist/adapters/backend/sandbox.d.ts +47 -0
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +137 -0
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/cli/pi.d.ts.map +1 -1
- package/dist/adapters/cli/pi.js +4 -0
- package/dist/adapters/cli/pi.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +72 -3
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +143 -6
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +8 -2
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +8 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/cli/deferred-topic-send.d.ts +37 -0
- package/dist/cli/deferred-topic-send.d.ts.map +1 -0
- package/dist/cli/deferred-topic-send.js +69 -0
- package/dist/cli/deferred-topic-send.js.map +1 -0
- package/dist/cli/pm2-command.d.ts.map +1 -1
- package/dist/cli/pm2-command.js +6 -0
- package/dist/cli/pm2-command.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +173 -34
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +17 -12
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +4 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +99 -8
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/deferred-schedule-settlement.d.ts +20 -0
- package/dist/core/deferred-schedule-settlement.d.ts.map +1 -0
- package/dist/core/deferred-schedule-settlement.js +18 -0
- package/dist/core/deferred-schedule-settlement.js.map +1 -0
- package/dist/core/deferred-topic-binding.d.ts +19 -0
- package/dist/core/deferred-topic-binding.d.ts.map +1 -0
- package/dist/core/deferred-topic-binding.js +55 -0
- package/dist/core/deferred-topic-binding.js.map +1 -0
- package/dist/core/device-isolation-activation.d.ts +56 -0
- package/dist/core/device-isolation-activation.d.ts.map +1 -0
- package/dist/core/device-isolation-activation.js +110 -0
- package/dist/core/device-isolation-activation.js.map +1 -0
- package/dist/core/device-isolation-daemon.d.ts +84 -0
- package/dist/core/device-isolation-daemon.d.ts.map +1 -0
- package/dist/core/device-isolation-daemon.js +591 -0
- package/dist/core/device-isolation-daemon.js.map +1 -0
- package/dist/core/scheduler.d.ts +26 -20
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +114 -56
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +47 -15
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +178 -84
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +7 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +86 -1
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +95 -5
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/schedule-card-model.d.ts +12 -3
- package/dist/dashboard/schedule-card-model.d.ts.map +1 -1
- package/dist/dashboard/schedule-card-model.js +32 -8
- package/dist/dashboard/schedule-card-model.js.map +1 -1
- package/dist/dashboard/web/dashboard-components.d.ts +7 -0
- package/dist/dashboard/web/dashboard-components.d.ts.map +1 -1
- package/dist/dashboard/web/dashboard-components.js +11 -2
- package/dist/dashboard/web/dashboard-components.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +30 -6
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/schedules-page.d.ts +2 -0
- package/dist/dashboard/web/schedules-page.d.ts.map +1 -1
- package/dist/dashboard/web/schedules-page.js +74 -27
- package/dist/dashboard/web/schedules-page.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-O37SAQD5.js → bot-defaults-page-B5AY4B2J.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-6QHMLMIM.js → chunk-CUV65BIQ.js} +3 -3
- package/dist/dashboard-web/chunks/{chunk-O2DPQL7C.js → chunk-JNRSPKLS.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FYALHCPG.js → chunk-P6GO5KT4.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-QFMH6ITB.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-B42PCL5P.js → chunk-UBOLHQCK.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-DZBQ42MH.js → connectors-page-KTC2QWNW.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-3IEGRZKQ.js → groups-page-B3EKGOLY.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-X3ADRON3.js → insights-page-X6RXNCWX.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-IV36RGQY.js → monitor-room-LLRA5LQW.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-ADFKVON4.js → monitoring-page-7MT5FEOS.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-CALAOWNS.js → office-page-KD46D5TD.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-SLMGLK3M.js → overview-page-JUWNH32O.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-L7JJ4FFV.js → roles-page-P6F7NLOI.js} +1 -1
- package/dist/dashboard-web/chunks/schedules-page-XIHWZST4.js +1 -0
- package/dist/dashboard-web/chunks/{sessions-page-OKJ5YWUZ.js → sessions-page-2VECQ27T.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-P6MI2ZUE.js → settings-page-HMIUE2JA.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-FT7BVZ6X.js → skills-page-A46KVWBL.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-DEYCPUFB.js → team-federation-page-SDUC56DF.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-4IOWREZG.js → v3-page-J5JOTUSQ.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-2VMBETZH.js → whiteboards-page-U7LSOAWI.js} +1 -1
- package/dist/dashboard-web/style.css +34 -4
- package/dist/dashboard.js +16 -1
- package/dist/dashboard.js.map +1 -1
- package/dist/desktop/main/auto-start.d.ts +2 -0
- package/dist/desktop/main/auto-start.d.ts.map +1 -1
- package/dist/desktop/main/auto-start.js +10 -6
- package/dist/desktop/main/auto-start.js.map +1 -1
- package/dist/desktop/main/bundled-runtime.d.ts +12 -0
- package/dist/desktop/main/bundled-runtime.d.ts.map +1 -0
- package/dist/desktop/main/bundled-runtime.js +30 -0
- package/dist/desktop/main/bundled-runtime.js.map +1 -0
- package/dist/desktop/main/device-status.d.ts +15 -0
- package/dist/desktop/main/device-status.d.ts.map +1 -0
- package/dist/desktop/main/device-status.js +94 -0
- package/dist/desktop/main/device-status.js.map +1 -0
- package/dist/desktop/main/external-runtime.d.ts +9 -0
- package/dist/desktop/main/external-runtime.d.ts.map +1 -1
- package/dist/desktop/main/external-runtime.js +88 -18
- package/dist/desktop/main/external-runtime.js.map +1 -1
- package/dist/desktop/main/ipc.d.ts.map +1 -1
- package/dist/desktop/main/ipc.js +1 -0
- package/dist/desktop/main/ipc.js.map +1 -1
- package/dist/desktop/main/node-command.d.ts +21 -0
- package/dist/desktop/main/node-command.d.ts.map +1 -1
- package/dist/desktop/main/node-command.js +41 -0
- package/dist/desktop/main/node-command.js.map +1 -1
- package/dist/desktop/main/pm2-apps.d.ts +4 -2
- package/dist/desktop/main/pm2-apps.d.ts.map +1 -1
- package/dist/desktop/main/pm2-apps.js +11 -3
- package/dist/desktop/main/pm2-apps.js.map +1 -1
- package/dist/desktop/main/runtime-service.d.ts +21 -5
- package/dist/desktop/main/runtime-service.d.ts.map +1 -1
- package/dist/desktop/main/runtime-service.js +94 -10
- package/dist/desktop/main/runtime-service.js.map +1 -1
- package/dist/desktop/main/runtime-source.d.ts +2 -0
- package/dist/desktop/main/runtime-source.d.ts.map +1 -1
- package/dist/desktop/main/runtime-source.js +12 -6
- package/dist/desktop/main/runtime-source.js.map +1 -1
- package/dist/desktop/main/tray.d.ts +1 -0
- package/dist/desktop/main/tray.d.ts.map +1 -1
- package/dist/desktop/main/tray.js +3 -3
- package/dist/desktop/main/tray.js.map +1 -1
- package/dist/desktop/main.js +28 -8
- package/dist/desktop/main.js.map +1 -1
- package/dist/desktop/preload.js +3 -0
- package/dist/desktop/preload.js.map +1 -1
- package/dist/desktop/renderer/app.d.ts +2 -1
- package/dist/desktop/renderer/app.d.ts.map +1 -1
- package/dist/desktop/renderer/app.js +6 -1
- package/dist/desktop/renderer/app.js.map +1 -1
- package/dist/desktop/renderer/runtime-state.d.ts.map +1 -1
- package/dist/desktop/renderer/runtime-state.js +2 -0
- package/dist/desktop/renderer/runtime-state.js.map +1 -1
- package/dist/desktop/shared/shell-path-probes.d.ts +24 -0
- package/dist/desktop/shared/shell-path-probes.d.ts.map +1 -0
- package/dist/desktop/shared/shell-path-probes.js +38 -0
- package/dist/desktop/shared/shell-path-probes.js.map +1 -0
- package/dist/desktop/shared/types.d.ts +21 -1
- package/dist/desktop/shared/types.d.ts.map +1 -1
- package/dist/desktop/smoke.d.ts +1 -0
- package/dist/desktop/smoke.d.ts.map +1 -1
- package/dist/desktop/smoke.js +20 -5
- package/dist/desktop/smoke.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +16 -7
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +16 -7
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +3 -0
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +1 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +1 -1
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/schedules-card.d.ts +2 -2
- package/dist/im/lark/schedules-card.d.ts.map +1 -1
- package/dist/im/lark/schedules-card.js +50 -34
- package/dist/im/lark/schedules-card.js.map +1 -1
- package/dist/index-daemon.js +13 -1
- package/dist/index-daemon.js.map +1 -1
- package/dist/platform/bind.d.ts +4 -1
- package/dist/platform/bind.d.ts.map +1 -1
- package/dist/platform/bind.js +10 -1
- package/dist/platform/bind.js.map +1 -1
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +15 -9
- package/dist/platform/binding.js.map +1 -1
- package/dist/platform/device-command.d.ts +35 -0
- package/dist/platform/device-command.d.ts.map +1 -0
- package/dist/platform/device-command.js +187 -0
- package/dist/platform/device-command.js.map +1 -0
- package/dist/platform/device-enroll.d.ts +156 -0
- package/dist/platform/device-enroll.d.ts.map +1 -0
- package/dist/platform/device-enroll.js +781 -0
- package/dist/platform/device-enroll.js.map +1 -0
- package/dist/platform/device-isolation-activation-client.d.ts +28 -0
- package/dist/platform/device-isolation-activation-client.d.ts.map +1 -0
- package/dist/platform/device-isolation-activation-client.js +279 -0
- package/dist/platform/device-isolation-activation-client.js.map +1 -0
- package/dist/platform/device-isolation.d.ts +35 -0
- package/dist/platform/device-isolation.d.ts.map +1 -0
- package/dist/platform/device-isolation.js +90 -0
- package/dist/platform/device-isolation.js.map +1 -0
- package/dist/platform/device-paths.d.ts +9 -0
- package/dist/platform/device-paths.d.ts.map +1 -0
- package/dist/platform/device-paths.js +14 -0
- package/dist/platform/device-paths.js.map +1 -0
- package/dist/platform/device.d.ts +77 -0
- package/dist/platform/device.d.ts.map +1 -0
- package/dist/platform/device.js +212 -0
- package/dist/platform/device.js.map +1 -0
- package/dist/platform/host-command-context.d.ts +15 -0
- package/dist/platform/host-command-context.d.ts.map +1 -0
- package/dist/platform/host-command-context.js +26 -0
- package/dist/platform/host-command-context.js.map +1 -0
- package/dist/platform/secure-host-file.d.ts +12 -0
- package/dist/platform/secure-host-file.d.ts.map +1 -0
- package/dist/platform/secure-host-file.js +182 -0
- package/dist/platform/secure-host-file.js.map +1 -0
- package/dist/services/schedule-store.d.ts +6 -2
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +19 -4
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +4 -5
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +45 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/atomic-write.d.ts +13 -0
- package/dist/utils/atomic-write.d.ts.map +1 -1
- package/dist/utils/atomic-write.js +89 -9
- package/dist/utils/atomic-write.js.map +1 -1
- package/dist/utils/child-env.d.ts +40 -1
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +45 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/pending-input-queue.d.ts +10 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +15 -0
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/worker.js +391 -28
- package/dist/worker.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +4 -4
- package/dist/workflows/events/schema.d.ts +16 -16
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts +3 -1
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts.map +1 -1
- package/dist/workflows/hostExecutors/botmux-schedule.js +14 -3
- package/dist/workflows/hostExecutors/botmux-schedule.js.map +1 -1
- package/dist/workflows/v3/architect.js +1 -1
- package/dist/workflows/v3/architect.js.map +1 -1
- package/dist/workflows/v3/contract.d.ts +8 -0
- package/dist/workflows/v3/contract.d.ts.map +1 -1
- package/dist/workflows/v3/contract.js.map +1 -1
- package/dist/workflows/v3/daemon-run.d.ts.map +1 -1
- package/dist/workflows/v3/daemon-run.js +2 -0
- package/dist/workflows/v3/daemon-run.js.map +1 -1
- package/dist/workflows/v3/ephemeral-pool.d.ts.map +1 -1
- package/dist/workflows/v3/ephemeral-pool.js +10 -2
- package/dist/workflows/v3/ephemeral-pool.js.map +1 -1
- package/dist/workflows/v3/run-envelope.d.ts +24 -24
- package/dist/workflows/v3/runtime.d.ts +5 -0
- package/dist/workflows/v3/runtime.d.ts.map +1 -1
- package/dist/workflows/v3/runtime.js +1 -0
- package/dist/workflows/v3/runtime.js.map +1 -1
- package/package.json +6 -3
- package/dist/dashboard-web/chunks/chunk-I35DXGM4.js +0 -1
- package/dist/dashboard-web/chunks/schedules-page-Z3672POL.js +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export declare const DEVICE_CREDENTIAL_SCHEMA_VERSION: 1;
|
|
2
|
+
export interface DeviceCredentials {
|
|
3
|
+
schemaVersion: typeof DEVICE_CREDENTIAL_SCHEMA_VERSION;
|
|
4
|
+
/** HTTPS origin pinned when enrollment starts. */
|
|
5
|
+
issuer: string;
|
|
6
|
+
/** Short-lived signed bearer. Never expose outside the host process. */
|
|
7
|
+
accessToken: string;
|
|
8
|
+
/** Absolute access-token expiry supplied by the server, Unix epoch ms. */
|
|
9
|
+
accessExpiresAt: number;
|
|
10
|
+
/** Rotating opaque secret. Never expose outside the host process. */
|
|
11
|
+
refreshToken: string;
|
|
12
|
+
/** Absolute server-issued device expiry, represented as Unix epoch ms. */
|
|
13
|
+
deviceExp: number;
|
|
14
|
+
/** Local metadata only; ISO timestamp of the last atomic credential write. */
|
|
15
|
+
savedAt: string;
|
|
16
|
+
/**
|
|
17
|
+
* Durable idempotency key for an in-flight refresh rotation. A failed or
|
|
18
|
+
* interrupted request keeps this value so the next host process can replay
|
|
19
|
+
* the same server operation instead of reusing the old refresh as a new one.
|
|
20
|
+
*/
|
|
21
|
+
pendingRefreshRequestId?: string;
|
|
22
|
+
/** Unix epoch ms when the current replay window began. */
|
|
23
|
+
pendingRefreshStartedAt?: number;
|
|
24
|
+
/** Ambiguous/unsafe refresh state: never send the stored refresh again. */
|
|
25
|
+
refreshRecoveryRequired?: true;
|
|
26
|
+
}
|
|
27
|
+
export type DeviceCredentialWriteInput = Pick<DeviceCredentials, 'issuer' | 'accessToken' | 'accessExpiresAt' | 'refreshToken' | 'deviceExp'> & {
|
|
28
|
+
pendingRefreshRequestId?: string;
|
|
29
|
+
pendingRefreshStartedAt?: number;
|
|
30
|
+
refreshRecoveryRequired?: true;
|
|
31
|
+
};
|
|
32
|
+
export type DevicePublicStatus = {
|
|
33
|
+
schemaVersion: typeof DEVICE_CREDENTIAL_SCHEMA_VERSION;
|
|
34
|
+
enrolled: false;
|
|
35
|
+
} | {
|
|
36
|
+
schemaVersion: typeof DEVICE_CREDENTIAL_SCHEMA_VERSION;
|
|
37
|
+
enrolled: true;
|
|
38
|
+
issuer: string;
|
|
39
|
+
deviceExp: number;
|
|
40
|
+
savedAt: string;
|
|
41
|
+
};
|
|
42
|
+
export interface DeviceCredentialPathOptions {
|
|
43
|
+
/** Test/embedding seam. Defaults to os.homedir(). */
|
|
44
|
+
homeDir?: string;
|
|
45
|
+
/** Exact file override. Takes precedence over homeDir. */
|
|
46
|
+
filePath?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface WriteDeviceCredentialsOptions extends DeviceCredentialPathOptions {
|
|
49
|
+
/** Test seam; defaults to the current wall clock. */
|
|
50
|
+
now?: () => Date;
|
|
51
|
+
}
|
|
52
|
+
export declare class DeviceCredentialError extends Error {
|
|
53
|
+
readonly code: 'invalid_issuer' | 'issuer_mismatch' | 'unsafe_file' | 'invalid_file';
|
|
54
|
+
constructor(message: string, code: 'invalid_issuer' | 'issuer_mismatch' | 'unsafe_file' | 'invalid_file');
|
|
55
|
+
}
|
|
56
|
+
export declare function deviceCredentialsPath(options?: DeviceCredentialPathOptions): string;
|
|
57
|
+
/**
|
|
58
|
+
* Normalize and validate the credential issuer.
|
|
59
|
+
*
|
|
60
|
+
* Production enrollment is HTTPS-only. Loopback HTTP is accepted solely for
|
|
61
|
+
* local development/self-tests; credentials are never sent over cleartext to
|
|
62
|
+
* a non-loopback host. Paths, query strings, fragments and userinfo are
|
|
63
|
+
* rejected so every request stays pinned to one unambiguous origin.
|
|
64
|
+
*/
|
|
65
|
+
export declare function normalizeDeviceIssuer(raw: string): string;
|
|
66
|
+
export declare function readDeviceCredentials(options?: DeviceCredentialPathOptions): DeviceCredentials | null;
|
|
67
|
+
/**
|
|
68
|
+
* Atomically replace the device credential pair with exact 0600 permissions.
|
|
69
|
+
* If a credential already exists, its issuer is immutable until explicit
|
|
70
|
+
* logout. This prevents a changed env/platform binding from exfiltrating the
|
|
71
|
+
* rotating refresh token to another origin.
|
|
72
|
+
*/
|
|
73
|
+
export declare function writeDeviceCredentials(input: DeviceCredentialWriteInput, options?: WriteDeviceCredentialsOptions): DeviceCredentials;
|
|
74
|
+
/** Delete only the local credential file. Server-side revocation is separate. */
|
|
75
|
+
export declare function clearDeviceCredentials(options?: DeviceCredentialPathOptions): boolean;
|
|
76
|
+
export declare function readDevicePublicStatus(options?: DeviceCredentialPathOptions): DevicePublicStatus;
|
|
77
|
+
//# sourceMappingURL=device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/platform/device.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,gCAAgC,EAAG,CAAU,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,gCAAgC,CAAC;IACvD,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,0DAA0D;IAC1D,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,2EAA2E;IAC3E,uBAAuB,CAAC,EAAE,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,iBAAiB,EACjB,QAAQ,GAAG,aAAa,GAAG,iBAAiB,GAAG,cAAc,GAAG,WAAW,CAC5E,GAAG;IACF,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,uBAAuB,CAAC,EAAE,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,aAAa,EAAE,OAAO,gCAAgC,CAAC;IACvD,QAAQ,EAAE,KAAK,CAAC;CACjB,GACD;IACE,aAAa,EAAE,OAAO,gCAAgC,CAAC;IACvD,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,MAAM,WAAW,2BAA2B;IAC1C,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,2BAA2B;IAChF,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,IAAI,EACT,gBAAgB,GAChB,iBAAiB,GACjB,aAAa,GACb,cAAc;gBALlB,OAAO,EAAE,MAAM,EACN,IAAI,EACT,gBAAgB,GAChB,iBAAiB,GACjB,aAAa,GACb,cAAc;CAKrB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,2BAAgC,GAAG,MAAM,CAGvF;AAOD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBzD;AA2ED,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,2BAAgC,GACxC,iBAAiB,GAAG,IAAI,CAQ1B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,0BAA0B,EACjC,OAAO,GAAE,6BAAkC,GAC1C,iBAAiB,CA6DnB;AAED,iFAAiF;AACjF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAOzF;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,2BAAgC,GACxC,kBAAkB,CAYpB"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-only desktop device credential storage.
|
|
3
|
+
*
|
|
4
|
+
* Device credentials are deliberately kept separate from platform.json: the
|
|
5
|
+
* machine bearer identifies the daemon, while this file identifies a person on
|
|
6
|
+
* a desktop device. Callers must never serialize this object to stdout or a
|
|
7
|
+
* renderer process; use {@link readDevicePublicStatus} for display purposes.
|
|
8
|
+
*/
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { readSecureHostFileSync, unlinkSecureHostFileSync, UnsafeHostAuthorityFileError, writeSecureHostFileSync, } from './secure-host-file.js';
|
|
12
|
+
import { deviceCredentialFile } from './device-paths.js';
|
|
13
|
+
export const DEVICE_CREDENTIAL_SCHEMA_VERSION = 1;
|
|
14
|
+
export class DeviceCredentialError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(message, code) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.name = 'DeviceCredentialError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function deviceCredentialsPath(options = {}) {
|
|
23
|
+
return options.filePath
|
|
24
|
+
?? deviceCredentialFile(join(options.homeDir ?? homedir(), '.botmux'));
|
|
25
|
+
}
|
|
26
|
+
function isLoopbackHostname(hostname) {
|
|
27
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
28
|
+
return normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalize and validate the credential issuer.
|
|
32
|
+
*
|
|
33
|
+
* Production enrollment is HTTPS-only. Loopback HTTP is accepted solely for
|
|
34
|
+
* local development/self-tests; credentials are never sent over cleartext to
|
|
35
|
+
* a non-loopback host. Paths, query strings, fragments and userinfo are
|
|
36
|
+
* rejected so every request stays pinned to one unambiguous origin.
|
|
37
|
+
*/
|
|
38
|
+
export function normalizeDeviceIssuer(raw) {
|
|
39
|
+
let url;
|
|
40
|
+
try {
|
|
41
|
+
url = new URL(raw);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new DeviceCredentialError('设备平台地址无效', 'invalid_issuer');
|
|
45
|
+
}
|
|
46
|
+
if (url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
|
|
47
|
+
throw new DeviceCredentialError('设备平台地址必须是纯 origin,不能包含路径、查询、片段或用户信息', 'invalid_issuer');
|
|
48
|
+
}
|
|
49
|
+
const secure = url.protocol === 'https:';
|
|
50
|
+
const localDevelopment = url.protocol === 'http:' && isLoopbackHostname(url.hostname);
|
|
51
|
+
if (!secure && !localDevelopment) {
|
|
52
|
+
throw new DeviceCredentialError('设备注册只允许 HTTPS(本机 loopback 开发除外)', 'invalid_issuer');
|
|
53
|
+
}
|
|
54
|
+
return url.origin;
|
|
55
|
+
}
|
|
56
|
+
function asCredentialFileError(error) {
|
|
57
|
+
if (error instanceof UnsafeHostAuthorityFileError) {
|
|
58
|
+
throw new DeviceCredentialError(error.message, 'unsafe_file');
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
function parseDeviceCredentials(raw) {
|
|
63
|
+
let value;
|
|
64
|
+
try {
|
|
65
|
+
value = JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new DeviceCredentialError('device.json 已损坏或不是有效 JSON', 'invalid_file');
|
|
69
|
+
}
|
|
70
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
71
|
+
throw new DeviceCredentialError('device.json 结构无效', 'invalid_file');
|
|
72
|
+
}
|
|
73
|
+
const record = value;
|
|
74
|
+
if (record.schemaVersion !== DEVICE_CREDENTIAL_SCHEMA_VERSION
|
|
75
|
+
|| typeof record.issuer !== 'string'
|
|
76
|
+
|| typeof record.accessToken !== 'string'
|
|
77
|
+
|| !record.accessToken
|
|
78
|
+
|| typeof record.accessExpiresAt !== 'number'
|
|
79
|
+
|| !Number.isSafeInteger(record.accessExpiresAt)
|
|
80
|
+
|| record.accessExpiresAt <= 0
|
|
81
|
+
|| typeof record.refreshToken !== 'string'
|
|
82
|
+
|| !record.refreshToken
|
|
83
|
+
|| typeof record.deviceExp !== 'number'
|
|
84
|
+
|| !Number.isSafeInteger(record.deviceExp)
|
|
85
|
+
|| record.deviceExp <= 0
|
|
86
|
+
|| record.accessExpiresAt > record.deviceExp
|
|
87
|
+
|| typeof record.savedAt !== 'string'
|
|
88
|
+
|| !record.savedAt
|
|
89
|
+
|| !Number.isFinite(Date.parse(record.savedAt))
|
|
90
|
+
|| new Date(record.savedAt).toISOString() !== record.savedAt
|
|
91
|
+
|| (record.pendingRefreshRequestId !== undefined && (typeof record.pendingRefreshRequestId !== 'string'
|
|
92
|
+
|| !/^[a-zA-Z0-9._:-]{16,128}$/.test(record.pendingRefreshRequestId)))
|
|
93
|
+
|| (record.pendingRefreshStartedAt !== undefined && (typeof record.pendingRefreshStartedAt !== 'number'
|
|
94
|
+
|| !Number.isSafeInteger(record.pendingRefreshStartedAt)
|
|
95
|
+
|| record.pendingRefreshStartedAt <= 0))
|
|
96
|
+
|| ((record.pendingRefreshRequestId === undefined) !== (record.pendingRefreshStartedAt === undefined))
|
|
97
|
+
|| (record.refreshRecoveryRequired !== undefined && record.refreshRecoveryRequired !== true)
|
|
98
|
+
|| (record.refreshRecoveryRequired === true && record.pendingRefreshRequestId !== undefined)) {
|
|
99
|
+
throw new DeviceCredentialError('device.json 缺少必要字段或字段类型无效', 'invalid_file');
|
|
100
|
+
}
|
|
101
|
+
const issuer = normalizeDeviceIssuer(record.issuer);
|
|
102
|
+
if (issuer !== record.issuer) {
|
|
103
|
+
throw new DeviceCredentialError('device.json 中的 issuer 不是规范化 origin', 'invalid_file');
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
schemaVersion: DEVICE_CREDENTIAL_SCHEMA_VERSION,
|
|
107
|
+
issuer,
|
|
108
|
+
accessToken: record.accessToken,
|
|
109
|
+
accessExpiresAt: record.accessExpiresAt,
|
|
110
|
+
refreshToken: record.refreshToken,
|
|
111
|
+
deviceExp: record.deviceExp,
|
|
112
|
+
savedAt: record.savedAt,
|
|
113
|
+
...(typeof record.pendingRefreshRequestId === 'string'
|
|
114
|
+
? { pendingRefreshRequestId: record.pendingRefreshRequestId }
|
|
115
|
+
: {}),
|
|
116
|
+
...(typeof record.pendingRefreshStartedAt === 'number'
|
|
117
|
+
? { pendingRefreshStartedAt: record.pendingRefreshStartedAt }
|
|
118
|
+
: {}),
|
|
119
|
+
...(record.refreshRecoveryRequired === true ? { refreshRecoveryRequired: true } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function readDeviceCredentials(options = {}) {
|
|
123
|
+
const filePath = deviceCredentialsPath(options);
|
|
124
|
+
try {
|
|
125
|
+
const raw = readSecureHostFileSync(filePath);
|
|
126
|
+
return raw === null ? null : parseDeviceCredentials(raw);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
asCredentialFileError(error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Atomically replace the device credential pair with exact 0600 permissions.
|
|
134
|
+
* If a credential already exists, its issuer is immutable until explicit
|
|
135
|
+
* logout. This prevents a changed env/platform binding from exfiltrating the
|
|
136
|
+
* rotating refresh token to another origin.
|
|
137
|
+
*/
|
|
138
|
+
export function writeDeviceCredentials(input, options = {}) {
|
|
139
|
+
const issuer = normalizeDeviceIssuer(input.issuer);
|
|
140
|
+
if (!input.accessToken
|
|
141
|
+
|| !Number.isSafeInteger(input.accessExpiresAt)
|
|
142
|
+
|| input.accessExpiresAt <= 0
|
|
143
|
+
|| !input.refreshToken
|
|
144
|
+
|| !Number.isSafeInteger(input.deviceExp)
|
|
145
|
+
|| input.deviceExp <= 0
|
|
146
|
+
|| input.accessExpiresAt > input.deviceExp) {
|
|
147
|
+
throw new DeviceCredentialError('平台返回的设备凭证字段无效', 'invalid_file');
|
|
148
|
+
}
|
|
149
|
+
if (input.pendingRefreshRequestId !== undefined
|
|
150
|
+
&& !/^[a-zA-Z0-9._:-]{16,128}$/.test(input.pendingRefreshRequestId)) {
|
|
151
|
+
throw new DeviceCredentialError('设备续期 request ID 无效', 'invalid_file');
|
|
152
|
+
}
|
|
153
|
+
if ((input.pendingRefreshRequestId === undefined) !== (input.pendingRefreshStartedAt === undefined)
|
|
154
|
+
|| (input.pendingRefreshStartedAt !== undefined && (!Number.isSafeInteger(input.pendingRefreshStartedAt)
|
|
155
|
+
|| input.pendingRefreshStartedAt <= 0))
|
|
156
|
+
|| (input.refreshRecoveryRequired !== undefined && input.refreshRecoveryRequired !== true)
|
|
157
|
+
|| (input.refreshRecoveryRequired === true && input.pendingRefreshRequestId !== undefined)) {
|
|
158
|
+
throw new DeviceCredentialError('设备续期恢复状态无效', 'invalid_file');
|
|
159
|
+
}
|
|
160
|
+
const filePath = deviceCredentialsPath(options);
|
|
161
|
+
const current = readDeviceCredentials(options);
|
|
162
|
+
if (current && current.issuer !== issuer) {
|
|
163
|
+
throw new DeviceCredentialError('已有设备凭证已固定到另一个平台;请先执行 botmux device logout,再重新注册', 'issuer_mismatch');
|
|
164
|
+
}
|
|
165
|
+
const credential = {
|
|
166
|
+
schemaVersion: DEVICE_CREDENTIAL_SCHEMA_VERSION,
|
|
167
|
+
issuer,
|
|
168
|
+
accessToken: input.accessToken,
|
|
169
|
+
accessExpiresAt: input.accessExpiresAt,
|
|
170
|
+
refreshToken: input.refreshToken,
|
|
171
|
+
deviceExp: input.deviceExp,
|
|
172
|
+
savedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
173
|
+
...(input.pendingRefreshRequestId
|
|
174
|
+
? { pendingRefreshRequestId: input.pendingRefreshRequestId }
|
|
175
|
+
: {}),
|
|
176
|
+
...(input.pendingRefreshStartedAt !== undefined
|
|
177
|
+
? { pendingRefreshStartedAt: input.pendingRefreshStartedAt }
|
|
178
|
+
: {}),
|
|
179
|
+
...(input.refreshRecoveryRequired === true ? { refreshRecoveryRequired: true } : {}),
|
|
180
|
+
};
|
|
181
|
+
try {
|
|
182
|
+
writeSecureHostFileSync(filePath, `${JSON.stringify(credential, null, 2)}\n`);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
asCredentialFileError(error);
|
|
186
|
+
}
|
|
187
|
+
return credential;
|
|
188
|
+
}
|
|
189
|
+
/** Delete only the local credential file. Server-side revocation is separate. */
|
|
190
|
+
export function clearDeviceCredentials(options = {}) {
|
|
191
|
+
const filePath = deviceCredentialsPath(options);
|
|
192
|
+
try {
|
|
193
|
+
return unlinkSecureHostFileSync(filePath);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
asCredentialFileError(error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export function readDevicePublicStatus(options = {}) {
|
|
200
|
+
const credential = readDeviceCredentials(options);
|
|
201
|
+
if (!credential) {
|
|
202
|
+
return { schemaVersion: DEVICE_CREDENTIAL_SCHEMA_VERSION, enrolled: false };
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
schemaVersion: DEVICE_CREDENTIAL_SCHEMA_VERSION,
|
|
206
|
+
enrolled: true,
|
|
207
|
+
issuer: credential.issuer,
|
|
208
|
+
deviceExp: credential.deviceExp,
|
|
209
|
+
savedAt: credential.savedAt,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/platform/device.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAU,CAAC;AA8D3D,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAGnC;IAFX,YACE,OAAe,EACN,IAIS;QAElB,KAAK,CAAC,OAAO,CAAC,CAAC;QANN,SAAI,GAAJ,IAAI,CAIK;QAGlB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,UAAuC,EAAE;IAC7E,OAAO,OAAO,CAAC,QAAQ;WAClB,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,KAAK,CAAC;AAC1F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,qBAAqB,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACnF,MAAM,IAAI,qBAAqB,CAAC,qCAAqC,EAAE,gBAAgB,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACzC,MAAM,gBAAgB,GAAG,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACjC,MAAM,IAAI,qBAAqB,CAAC,iCAAiC,EAAE,gBAAgB,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC;AACpB,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC3C,IAAI,KAAK,YAAY,4BAA4B,EAAE,CAAC;QAClD,MAAM,IAAI,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,KAAK,CAAC;AACd,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAW;IACzC,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,qBAAqB,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,qBAAqB,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IACE,MAAM,CAAC,aAAa,KAAK,gCAAgC;WACtD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;WACjC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;WACtC,CAAC,MAAM,CAAC,WAAW;WACnB,OAAO,MAAM,CAAC,eAAe,KAAK,QAAQ;WAC1C,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,eAAe,CAAC;WAC7C,MAAM,CAAC,eAAe,IAAI,CAAC;WAC3B,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ;WACvC,CAAC,MAAM,CAAC,YAAY;WACpB,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;WACpC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC;WACvC,MAAM,CAAC,SAAS,IAAI,CAAC;WACrB,MAAM,CAAC,eAAe,GAAG,MAAM,CAAC,SAAS;WACzC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;WAClC,CAAC,MAAM,CAAC,OAAO;WACf,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;WAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,OAAO;WACzD,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,IAAI,CAClD,OAAO,MAAM,CAAC,uBAAuB,KAAK,QAAQ;eAC/C,CAAC,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,CACrE,CAAC;WACC,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,IAAI,CAClD,OAAO,MAAM,CAAC,uBAAuB,KAAK,QAAQ;eAC/C,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,uBAAuB,CAAC;eACrD,MAAM,CAAC,uBAAuB,IAAI,CAAC,CACvC,CAAC;WACC,CAAC,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,CAAC,CAAC;WACnG,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS,IAAI,MAAM,CAAC,uBAAuB,KAAK,IAAI,CAAC;WACzF,CAAC,MAAM,CAAC,uBAAuB,KAAK,IAAI,IAAI,MAAM,CAAC,uBAAuB,KAAK,SAAS,CAAC,EAC5F,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,2BAA2B,EAAE,cAAc,CAAC,CAAC;IAC/E,CAAC;IACD,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,IAAI,qBAAqB,CAAC,oCAAoC,EAAE,cAAc,CAAC,CAAC;IACxF,CAAC;IACD,OAAO;QACL,aAAa,EAAE,gCAAgC;QAC/C,MAAM;QACN,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,GAAG,CAAC,OAAO,MAAM,CAAC,uBAAuB,KAAK,QAAQ;YACpD,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,EAAE;YAC7D,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,MAAM,CAAC,uBAAuB,KAAK,QAAQ;YACpD,CAAC,CAAC,EAAE,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,EAAE;YAC7D,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,MAAM,CAAC,uBAAuB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/F,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,UAAuC,EAAE;IAEzC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAiC,EACjC,UAAyC,EAAE;IAE3C,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACnD,IACE,CAAC,KAAK,CAAC,WAAW;WACf,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC;WAC5C,KAAK,CAAC,eAAe,IAAI,CAAC;WAC1B,CAAC,KAAK,CAAC,YAAY;WACnB,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC;WACtC,KAAK,CAAC,SAAS,IAAI,CAAC;WACpB,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,SAAS,EAC1C,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IACnE,CAAC;IACD,IACE,KAAK,CAAC,uBAAuB,KAAK,SAAS;WACxC,CAAC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,EACnE,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,oBAAoB,EAAE,cAAc,CAAC,CAAC;IACxE,CAAC;IACD,IACE,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS,CAAC;WAC5F,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS,IAAI,CACjD,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,uBAAuB,CAAC;eACjD,KAAK,CAAC,uBAAuB,IAAI,CAAC,CACtC,CAAC;WACC,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS,IAAI,KAAK,CAAC,uBAAuB,KAAK,IAAI,CAAC;WACvF,CAAC,KAAK,CAAC,uBAAuB,KAAK,IAAI,IAAI,KAAK,CAAC,uBAAuB,KAAK,SAAS,CAAC,EAC1F,CAAC;QACD,MAAM,IAAI,qBAAqB,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QACzC,MAAM,IAAI,qBAAqB,CAC7B,iDAAiD,EACjD,iBAAiB,CAClB,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAsB;QACpC,aAAa,EAAE,gCAAgC;QAC/C,MAAM;QACN,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE;QAC5D,GAAG,CAAC,KAAK,CAAC,uBAAuB;YAC/B,CAAC,CAAC,EAAE,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,EAAE;YAC5D,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,uBAAuB,KAAK,SAAS;YAC7C,CAAC,CAAC,EAAE,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,EAAE;YAC5D,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,uBAAuB,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,IAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9F,CAAC;IACF,IAAI,CAAC;QACH,uBAAuB,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,sBAAsB,CAAC,UAAuC,EAAE;IAC9E,MAAM,QAAQ,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAAuC,EAAE;IAEzC,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,aAAa,EAAE,gCAAgC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC9E,CAAC;IACD,OAAO;QACL,aAAa,EAAE,gCAAgC;QAC/C,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,SAAS,EAAE,UAAU,CAAC,SAAS;QAC/B,OAAO,EAAE,UAAU,CAAC,OAAO;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ManagedAgentHostCommandContextOptions {
|
|
2
|
+
env?: NodeJS.ProcessEnv;
|
|
3
|
+
dataDir?: string;
|
|
4
|
+
startPid?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Detect commands launched from a botmux-managed AI CLI rather than a trusted
|
|
8
|
+
* host terminal. Environment hints cover detached/workflow launches; the
|
|
9
|
+
* daemon-owned PID marker covers shells which scrub those hints. Child-provided
|
|
10
|
+
* HOME/SESSION_DATA_DIR are deliberately ignored when resolving the authority
|
|
11
|
+
* store. This is an early UX/consumption guard; OS credential isolation remains
|
|
12
|
+
* the actual secret boundary.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isManagedAgentHostCommandContext(options?: ManagedAgentHostCommandContextOptions): boolean;
|
|
15
|
+
//# sourceMappingURL=host-command-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-command-context.d.ts","sourceRoot":"","sources":["../../src/platform/host-command-context.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,qCAAqC;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,qCAA0C,GAClD,OAAO,CAWT"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { resolveBotmuxDataDir } from '../core/data-dir.js';
|
|
3
|
+
import { resolveSessionContext } from '../core/session-marker.js';
|
|
4
|
+
/**
|
|
5
|
+
* Detect commands launched from a botmux-managed AI CLI rather than a trusted
|
|
6
|
+
* host terminal. Environment hints cover detached/workflow launches; the
|
|
7
|
+
* daemon-owned PID marker covers shells which scrub those hints. Child-provided
|
|
8
|
+
* HOME/SESSION_DATA_DIR are deliberately ignored when resolving the authority
|
|
9
|
+
* store. This is an early UX/consumption guard; OS credential isolation remains
|
|
10
|
+
* the actual secret boundary.
|
|
11
|
+
*/
|
|
12
|
+
export function isManagedAgentHostCommandContext(options = {}) {
|
|
13
|
+
const env = options.env ?? process.env;
|
|
14
|
+
if (env.BOTMUX_WORKFLOW === '1' || !!env.BOTMUX_SESSION_ID?.trim())
|
|
15
|
+
return true;
|
|
16
|
+
try {
|
|
17
|
+
const dataDir = options.dataDir
|
|
18
|
+
?? resolveBotmuxDataDir({ env: {}, homeDir: homedir() });
|
|
19
|
+
return resolveSessionContext(dataDir, undefined, options.startPid ?? process.ppid) !== null;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// If the authority boundary cannot be evaluated, fail closed.
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=host-command-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-command-context.js","sourceRoot":"","sources":["../../src/platform/host-command-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAQlE;;;;;;;GAOG;AACH,MAAM,UAAU,gCAAgC,CAC9C,UAAiD,EAAE;IAEnD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,IAAI,GAAG,CAAC,eAAe,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAChF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;eAC1B,oBAAoB,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3D,OAAO,qBAAqB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC9F,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class UnsafeHostAuthorityFileError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
/** Create/resolve the parent without ever resolving the final path component. */
|
|
5
|
+
export declare function secureHostFilePath(filePath: string): string;
|
|
6
|
+
/** Return null only for a genuinely absent leaf; unsafe shapes fail closed. */
|
|
7
|
+
export declare function readSecureHostFileSync(filePath: string, maxBytes?: number): string | null;
|
|
8
|
+
/** Strict, durable atomic replace that never follows a leaf symlink. */
|
|
9
|
+
export declare function writeSecureHostFileSync(filePath: string, data: string): void;
|
|
10
|
+
/** Strict durable unlink. Returns false only if the leaf is absent. */
|
|
11
|
+
export declare function unlinkSecureHostFileSync(filePath: string): boolean;
|
|
12
|
+
//# sourceMappingURL=secure-host-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-host-file.d.ts","sourceRoot":"","sources":["../../src/platform/secure-host-file.ts"],"names":[],"mappings":"AAwBA,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM;CAI5B;AAyDD,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc3D;AAeD,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,MAAM,GAAG,IAAI,CA2C5F;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAmB5E;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CASlE"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict host-authority file primitives.
|
|
3
|
+
*
|
|
4
|
+
* Unlike general dotfiles, machine/device credentials must never follow a
|
|
5
|
+
* leaf symlink. Reads pin one inode with O_NOFOLLOW, writes canonicalize only
|
|
6
|
+
* the parent and atomically replace the leaf, and the containing directory
|
|
7
|
+
* must not be writable by group/other users.
|
|
8
|
+
*/
|
|
9
|
+
import { closeSync, constants, fstatSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, statSync, unlinkSync, } from 'node:fs';
|
|
10
|
+
import { basename, dirname, join } from 'node:path';
|
|
11
|
+
import { atomicWriteFileSync } from '../utils/atomic-write.js';
|
|
12
|
+
export class UnsafeHostAuthorityFileError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'UnsafeHostAuthorityFileError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function sameInode(left, right) {
|
|
19
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
20
|
+
}
|
|
21
|
+
function assertOwnedByCurrentUser(stats, label) {
|
|
22
|
+
if (process.platform === 'win32' || !process.getuid)
|
|
23
|
+
return;
|
|
24
|
+
if (stats.uid !== process.getuid()) {
|
|
25
|
+
throw new UnsafeHostAuthorityFileError(`${label} 不属于当前用户`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A 0700 credential directory is still replaceable when one of its ancestors
|
|
30
|
+
* is writable by another user. Walk the canonical chain so later path-based
|
|
31
|
+
* open/rename operations cannot be redirected by renaming the whole directory.
|
|
32
|
+
*
|
|
33
|
+
* POSIX sticky directories (notably /tmp) are the one safe writable exception:
|
|
34
|
+
* when both the directory owner and child owner are trusted, unrelated users
|
|
35
|
+
* cannot rename that child despite the directory's 01777 mode.
|
|
36
|
+
*/
|
|
37
|
+
function assertAncestorChainCannotReplace(canonicalParent) {
|
|
38
|
+
if (process.platform === 'win32' || !process.getuid)
|
|
39
|
+
return;
|
|
40
|
+
const uid = process.getuid();
|
|
41
|
+
const trustedOwner = (owner) => owner === uid || owner === 0;
|
|
42
|
+
let childPath = canonicalParent;
|
|
43
|
+
let childStats = statSync(childPath);
|
|
44
|
+
while (true) {
|
|
45
|
+
const ancestorPath = dirname(childPath);
|
|
46
|
+
if (ancestorPath === childPath)
|
|
47
|
+
return;
|
|
48
|
+
const ancestorStats = statSync(ancestorPath);
|
|
49
|
+
if (!ancestorStats.isDirectory()) {
|
|
50
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证祖先路径不是目录');
|
|
51
|
+
}
|
|
52
|
+
const untrustedOwnerCanWrite = !trustedOwner(ancestorStats.uid)
|
|
53
|
+
&& (ancestorStats.mode & 0o200) !== 0;
|
|
54
|
+
const groupOrOtherCanWrite = (ancestorStats.mode & 0o022) !== 0;
|
|
55
|
+
if (untrustedOwnerCanWrite || groupOrOtherCanWrite) {
|
|
56
|
+
const stickyProtectsChild = (ancestorStats.mode & 0o1000) !== 0
|
|
57
|
+
&& trustedOwner(ancestorStats.uid)
|
|
58
|
+
&& trustedOwner(childStats.uid);
|
|
59
|
+
if (!stickyProtectsChild) {
|
|
60
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证目录可被不可信祖先目录替换');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
childPath = ancestorPath;
|
|
64
|
+
childStats = ancestorStats;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Create/resolve the parent without ever resolving the final path component. */
|
|
68
|
+
export function secureHostFilePath(filePath) {
|
|
69
|
+
const parent = dirname(filePath);
|
|
70
|
+
mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
71
|
+
const canonicalParent = realpathSync(parent);
|
|
72
|
+
const parentStats = statSync(canonicalParent);
|
|
73
|
+
if (!parentStats.isDirectory()) {
|
|
74
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证目录不是普通目录');
|
|
75
|
+
}
|
|
76
|
+
assertOwnedByCurrentUser(parentStats, '宿主凭证目录');
|
|
77
|
+
if (process.platform !== 'win32' && (parentStats.mode & 0o022) !== 0) {
|
|
78
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证目录可被组内或其它用户写入');
|
|
79
|
+
}
|
|
80
|
+
assertAncestorChainCannotReplace(canonicalParent);
|
|
81
|
+
return join(canonicalParent, basename(filePath));
|
|
82
|
+
}
|
|
83
|
+
function assertSecureFileStats(stats, maxBytes) {
|
|
84
|
+
if (!stats.isFile()) {
|
|
85
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证必须是普通文件');
|
|
86
|
+
}
|
|
87
|
+
assertOwnedByCurrentUser(stats, '宿主凭证文件');
|
|
88
|
+
if (process.platform !== 'win32' && (stats.mode & 0o777) !== 0o600) {
|
|
89
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证文件权限必须严格为 0600');
|
|
90
|
+
}
|
|
91
|
+
if (stats.size < 0 || stats.size > maxBytes) {
|
|
92
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证文件大小异常');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Return null only for a genuinely absent leaf; unsafe shapes fail closed. */
|
|
96
|
+
export function readSecureHostFileSync(filePath, maxBytes = 64 * 1024) {
|
|
97
|
+
try {
|
|
98
|
+
lstatSync(dirname(filePath));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error.code === 'ENOENT')
|
|
102
|
+
return null;
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
const resolved = secureHostFilePath(filePath);
|
|
106
|
+
let fd;
|
|
107
|
+
try {
|
|
108
|
+
const flags = process.platform === 'win32'
|
|
109
|
+
? constants.O_RDONLY
|
|
110
|
+
: constants.O_RDONLY | constants.O_NOFOLLOW;
|
|
111
|
+
fd = openSync(resolved, flags);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code === 'ENOENT')
|
|
115
|
+
return null;
|
|
116
|
+
if (error.code === 'ELOOP') {
|
|
117
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证拒绝符号链接');
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const before = fstatSync(fd);
|
|
123
|
+
assertSecureFileStats(before, maxBytes);
|
|
124
|
+
const pathStats = lstatSync(resolved);
|
|
125
|
+
if (pathStats.isSymbolicLink() || !sameInode(before, pathStats)) {
|
|
126
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证路径在读取时发生变化');
|
|
127
|
+
}
|
|
128
|
+
const raw = readFileSync(fd, 'utf8');
|
|
129
|
+
const after = fstatSync(fd);
|
|
130
|
+
if (!sameInode(before, after)
|
|
131
|
+
|| before.size !== after.size
|
|
132
|
+
|| before.mtimeMs !== after.mtimeMs
|
|
133
|
+
|| before.ctimeMs !== after.ctimeMs) {
|
|
134
|
+
throw new UnsafeHostAuthorityFileError('宿主凭证在读取时发生变化');
|
|
135
|
+
}
|
|
136
|
+
return raw;
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
closeSync(fd);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Strict, durable atomic replace that never follows a leaf symlink. */
|
|
143
|
+
export function writeSecureHostFileSync(filePath, data) {
|
|
144
|
+
const resolved = secureHostFilePath(filePath);
|
|
145
|
+
let leafExists = false;
|
|
146
|
+
try {
|
|
147
|
+
lstatSync(resolved);
|
|
148
|
+
leafExists = true;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (error.code !== 'ENOENT')
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
if (leafExists) {
|
|
155
|
+
// Pin and validate the existing leaf before replacement. The final rename
|
|
156
|
+
// never follows a leaf symlink, so a later swap cannot redirect the write.
|
|
157
|
+
readSecureHostFileSync(resolved);
|
|
158
|
+
}
|
|
159
|
+
atomicWriteFileSync(resolved, data, {
|
|
160
|
+
mode: 0o600,
|
|
161
|
+
durable: true,
|
|
162
|
+
followTargetSymlink: false,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** Strict durable unlink. Returns false only if the leaf is absent. */
|
|
166
|
+
export function unlinkSecureHostFileSync(filePath) {
|
|
167
|
+
const resolved = secureHostFilePath(filePath);
|
|
168
|
+
if (readSecureHostFileSync(resolved) === null)
|
|
169
|
+
return false;
|
|
170
|
+
unlinkSync(resolved);
|
|
171
|
+
if (process.platform !== 'win32') {
|
|
172
|
+
const fd = openSync(dirname(resolved), constants.O_RDONLY);
|
|
173
|
+
try {
|
|
174
|
+
fsyncSync(fd);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
closeSync(fd);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=secure-host-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure-host-file.js","sourceRoot":"","sources":["../../src/platform/secure-host-file.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScheduledTask, ParsedSchedule } from '../types.js';
|
|
1
|
+
import type { ScheduledTask, ParsedSchedule, ScheduleExecutionPosition } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Raised by `createTask` when an `id` is supplied but a task already exists
|
|
4
4
|
* with that id AND its canonical input differs from the incoming params.
|
|
@@ -46,6 +46,8 @@ export declare function canonicalScheduleInput(t: {
|
|
|
46
46
|
chatType?: 'group' | 'p2p' | 'topic_group';
|
|
47
47
|
rootMessageId?: string;
|
|
48
48
|
scope?: 'thread' | 'chat';
|
|
49
|
+
executionPosition?: ScheduleExecutionPosition;
|
|
50
|
+
topicTitle?: string;
|
|
49
51
|
larkAppId?: string;
|
|
50
52
|
repeat?: {
|
|
51
53
|
times: number | null;
|
|
@@ -82,6 +84,8 @@ export declare function createTask(params: {
|
|
|
82
84
|
chatId: string;
|
|
83
85
|
rootMessageId?: string;
|
|
84
86
|
scope?: 'thread' | 'chat';
|
|
87
|
+
executionPosition?: ScheduleExecutionPosition;
|
|
88
|
+
topicTitle?: string;
|
|
85
89
|
chatType?: 'group' | 'p2p' | 'topic_group';
|
|
86
90
|
larkAppId?: string;
|
|
87
91
|
creatorChatId?: string;
|
|
@@ -97,7 +101,7 @@ export declare function createTask(params: {
|
|
|
97
101
|
}): ScheduledTask;
|
|
98
102
|
export declare function getTask(id: string): ScheduledTask | undefined;
|
|
99
103
|
export declare function removeTask(id: string): boolean;
|
|
100
|
-
export declare function updateTask(id: string, updates: Partial<Pick<ScheduledTask, 'enabled' | 'lastRunAt' | 'nextRunAt' | 'lastStatus' | 'lastError' | 'lastDeliveryError' | 'repeat' | 'rootMessageId' | 'chatType' | 'deliver' | 'name' | 'prompt' | 'schedule' | 'parsed' | 'silent' | 'workingDir'>>): void;
|
|
104
|
+
export declare function updateTask(id: string, updates: Partial<Pick<ScheduledTask, 'enabled' | 'lastRunAt' | 'nextRunAt' | 'lastStatus' | 'lastError' | 'lastDeliveryError' | 'repeat' | 'rootMessageId' | 'scope' | 'executionPosition' | 'topicTitle' | 'chatType' | 'deliver' | 'name' | 'prompt' | 'schedule' | 'parsed' | 'silent' | 'workingDir'>>): void;
|
|
101
105
|
/**
|
|
102
106
|
* Record a run outcome and auto-manage repeat counter. If the task has a
|
|
103
107
|
* finite repeat count and we've hit it, the task is removed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-store.d.ts","sourceRoot":"","sources":["../../src/services/schedule-store.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"schedule-store.d.ts","sourceRoot":"","sources":["../../src/services/schedule-store.ts"],"names":[],"mappings":"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"}
|