@parlor/agent-host 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +16 -2
- package/dist/acp.d.ts +165 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +626 -0
- package/dist/acp.js.map +1 -0
- package/dist/adapter-launch.d.ts +7 -0
- package/dist/adapter-launch.d.ts.map +1 -0
- package/dist/adapter-launch.js +35 -0
- package/dist/adapter-launch.js.map +1 -0
- package/dist/agents.d.ts +32 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +132 -0
- package/dist/agents.js.map +1 -0
- package/dist/assertion.d.ts +9 -0
- package/dist/assertion.d.ts.map +1 -0
- package/dist/assertion.js +22 -0
- package/dist/assertion.js.map +1 -0
- package/dist/control.d.ts +106 -0
- package/dist/control.d.ts.map +1 -0
- package/dist/control.js +32 -0
- package/dist/control.js.map +1 -0
- package/dist/daemon.d.ts +50 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +543 -0
- package/dist/daemon.js.map +1 -0
- package/dist/dev-host.d.ts +5 -0
- package/dist/dev-host.d.ts.map +1 -0
- package/dist/dev-host.js +29 -0
- package/dist/dev-host.js.map +1 -0
- package/dist/device-list.d.ts +6 -0
- package/dist/device-list.d.ts.map +1 -0
- package/dist/device-list.js +30 -0
- package/dist/device-list.js.map +1 -0
- package/dist/embedded.d.ts +6 -0
- package/dist/embedded.d.ts.map +1 -0
- package/dist/embedded.js +3 -0
- package/dist/embedded.js.map +1 -0
- package/dist/fold.d.ts +38 -0
- package/dist/fold.d.ts.map +1 -0
- package/dist/fold.js +165 -0
- package/dist/fold.js.map +1 -0
- package/dist/grants.d.ts +54 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +157 -0
- package/dist/grants.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/json-file.d.ts +6 -0
- package/dist/json-file.d.ts.map +1 -0
- package/dist/json-file.js +35 -0
- package/dist/json-file.js.map +1 -0
- package/dist/lines.d.ts +5 -0
- package/dist/lines.d.ts.map +1 -0
- package/dist/lines.js +9 -0
- package/dist/lines.js.map +1 -0
- package/dist/link-device.d.ts +28 -0
- package/dist/link-device.d.ts.map +1 -0
- package/dist/link-device.js +40 -0
- package/dist/link-device.js.map +1 -0
- package/dist/link.d.ts +52 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +277 -0
- package/dist/link.js.map +1 -0
- package/dist/lock.d.ts +7 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +105 -0
- package/dist/lock.js.map +1 -0
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +18 -0
- package/dist/log.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +520 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp-proxy.d.ts +33 -0
- package/dist/mcp-proxy.d.ts.map +1 -0
- package/dist/mcp-proxy.js +317 -0
- package/dist/mcp-proxy.js.map +1 -0
- package/dist/naming.d.ts +7 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +42 -0
- package/dist/naming.js.map +1 -0
- package/dist/pairing.d.ts +68 -0
- package/dist/pairing.d.ts.map +1 -0
- package/dist/pairing.js +312 -0
- package/dist/pairing.js.map +1 -0
- package/dist/paths.d.ts +19 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +23 -0
- package/dist/paths.js.map +1 -0
- package/dist/policy.d.ts +37 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +60 -0
- package/dist/policy.js.map +1 -0
- package/dist/pool.d.ts +34 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +112 -0
- package/dist/pool.js.map +1 -0
- package/dist/prompter.d.ts +33 -0
- package/dist/prompter.d.ts.map +1 -0
- package/dist/prompter.js +98 -0
- package/dist/prompter.js.map +1 -0
- package/dist/release-main.d.ts +2 -0
- package/dist/release-main.d.ts.map +1 -0
- package/dist/release-main.js +50 -0
- package/dist/release-main.js.map +1 -0
- package/dist/release.d.ts +22 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +81 -0
- package/dist/release.js.map +1 -0
- package/dist/revoke.d.ts +10 -0
- package/dist/revoke.d.ts.map +1 -0
- package/dist/revoke.js +23 -0
- package/dist/revoke.js.map +1 -0
- package/dist/self.d.ts +7 -0
- package/dist/self.d.ts.map +1 -0
- package/dist/self.js +10 -0
- package/dist/self.js.map +1 -0
- package/dist/service.d.ts +97 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +205 -0
- package/dist/service.js.map +1 -0
- package/dist/shell-path.d.ts +10 -0
- package/dist/shell-path.d.ts.map +1 -0
- package/dist/shell-path.js +64 -0
- package/dist/shell-path.js.map +1 -0
- package/dist/skeleton-kit.d.ts +8 -0
- package/dist/skeleton-kit.d.ts.map +1 -0
- package/dist/skeleton-kit.js +143 -0
- package/dist/skeleton-kit.js.map +1 -0
- package/dist/spec-kit.d.ts +102 -0
- package/dist/spec-kit.d.ts.map +1 -0
- package/dist/spec-kit.js +372 -0
- package/dist/spec-kit.js.map +1 -0
- package/dist/testing/fake-agent.d.ts +65 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +238 -0
- package/dist/testing/fake-agent.js.map +1 -0
- package/dist/trust.d.ts +20 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +27 -0
- package/dist/trust.js.map +1 -0
- package/dist/turn.d.ts +32 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +650 -0
- package/dist/turn.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AssertionRequest } from "./assertion.js";
|
|
2
|
+
import type { TrustedDevice, TrustStore } from "./trust.js";
|
|
3
|
+
export declare const LINK_PATH = "/_parlor/auth/hosts/devices/link";
|
|
4
|
+
export interface LinkDeviceInput {
|
|
5
|
+
readonly appOrigin: string;
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly assert: (request: AssertionRequest) => Promise<string>;
|
|
8
|
+
readonly trust: TrustStore;
|
|
9
|
+
/** Asked on this computer once the code checked out; only true links. */
|
|
10
|
+
readonly confirm: (device: {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly platform: string;
|
|
13
|
+
}) => Promise<boolean>;
|
|
14
|
+
readonly now?: (() => number) | undefined;
|
|
15
|
+
readonly fetch?: typeof fetch | undefined;
|
|
16
|
+
}
|
|
17
|
+
export type LinkOutcome = {
|
|
18
|
+
readonly linked: true;
|
|
19
|
+
readonly device: TrustedDevice;
|
|
20
|
+
} | {
|
|
21
|
+
readonly linked: false;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Links the device showing `code` from this computer: the app finds it, this computer checks
|
|
25
|
+
* its keys hash to that code, so the app cannot slip in other keys, and the user confirms here.
|
|
26
|
+
*/
|
|
27
|
+
export declare function linkDevice(input: LinkDeviceInput): Promise<LinkOutcome>;
|
|
28
|
+
//# sourceMappingURL=link-device.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-device.d.ts","sourceRoot":"","sources":["../src/link-device.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,SAAS,qCAAqC,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE;QACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;KAC3B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAA;CAAE,CAAC;AAE/B;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAuC7E"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { deviceCommitment, linkCode, normalizeLinkCode, } from "@parlor/auth";
|
|
2
|
+
import { printable } from "./prompter.js";
|
|
3
|
+
export const LINK_PATH = "/_parlor/auth/hosts/devices/link";
|
|
4
|
+
/**
|
|
5
|
+
* Links the device showing `code` from this computer: the app finds it, this computer checks
|
|
6
|
+
* its keys hash to that code, so the app cannot slip in other keys, and the user confirms here.
|
|
7
|
+
*/
|
|
8
|
+
export async function linkDevice(input) {
|
|
9
|
+
const origin = new URL(input.appOrigin).origin;
|
|
10
|
+
const url = `${origin}${LINK_PATH}`;
|
|
11
|
+
const authorization = `ParlorHost ${await input.assert({ method: "POST", url })}`;
|
|
12
|
+
const response = await (input.fetch ?? fetch)(url, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: { authorization, "content-type": "application/json" },
|
|
15
|
+
body: JSON.stringify({ code: input.code }),
|
|
16
|
+
});
|
|
17
|
+
const body = (await response.json().catch(() => undefined));
|
|
18
|
+
if (!response.ok || !body?.signingKey) {
|
|
19
|
+
throw new Error(body?.error?.message ?? `The app answered HTTP ${response.status}.`);
|
|
20
|
+
}
|
|
21
|
+
const commitment = await deviceCommitment(origin, body.id, body.signingKey, body.presenceKey?.publicKey);
|
|
22
|
+
if (linkCode(commitment) !== normalizeLinkCode(input.code)) {
|
|
23
|
+
throw new Error("The app returned a device whose keys do not match this code, so nothing was linked. Show a new code on the device and try again.");
|
|
24
|
+
}
|
|
25
|
+
const shown = { name: printable(body.name), platform: printable(body.platform) };
|
|
26
|
+
if (!(await input.confirm(shown)))
|
|
27
|
+
return { linked: false };
|
|
28
|
+
const device = {
|
|
29
|
+
deviceId: body.id,
|
|
30
|
+
signingKey: body.signingKey,
|
|
31
|
+
presenceKey: body.presenceKey,
|
|
32
|
+
name: body.name,
|
|
33
|
+
platform: body.platform,
|
|
34
|
+
approvedAt: (input.now ?? Date.now)(),
|
|
35
|
+
via: "local",
|
|
36
|
+
};
|
|
37
|
+
await input.trust.pin(origin, device);
|
|
38
|
+
return { linked: true, device };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=link-device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-device.js","sourceRoot":"","sources":["../src/link-device.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACR,iBAAiB,GAElB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG1C,MAAM,CAAC,MAAM,SAAS,GAAG,kCAAkC,CAAC;AAoB5D;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB;IACrD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,SAAS,EAAE,CAAC;IACpC,MAAM,aAAa,GAAG,cAAc,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAClF,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE;QACjD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC9D,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;KAC3C,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAE7C,CAAC;IACd,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,IAAI,yBAAyB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,UAAU,GAAG,MAAM,gBAAgB,CACvC,MAAM,EACN,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,WAAW,EAAE,SAAS,CAC5B,CAAC;IACF,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,kIAAkI,CACnI,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;IACjF,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC5D,MAAM,MAAM,GAAkB;QAC5B,QAAQ,EAAE,IAAI,CAAC,EAAE;QACjB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE;QACrC,GAAG,EAAE,OAAO;KACb,CAAC;IACF,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAClC,CAAC"}
|
package/dist/link.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type HostStreamBatch, type HostWelcome } from "@parlor/chat";
|
|
2
|
+
import { type ChatHostActionMap } from "@parlor/chat/server";
|
|
3
|
+
import { type PluginClient } from "@parlor/client";
|
|
4
|
+
import type { AppGrant, HostKeys } from "./grants.js";
|
|
5
|
+
export declare const RECONNECT_MAX_MS = 4000;
|
|
6
|
+
export declare const ACTIVE_POLL_MS = 2000;
|
|
7
|
+
export declare const IDLE_POLL_MS = 15000;
|
|
8
|
+
export interface HostApi {
|
|
9
|
+
readonly calls: PluginClient<ChatHostActionMap>;
|
|
10
|
+
/** At most one post in flight per turn; frames produced meanwhile coalesce into the next. */
|
|
11
|
+
stream(batch: HostStreamBatch): void;
|
|
12
|
+
heartbeat(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface AppLinkDeps {
|
|
15
|
+
readonly keys: HostKeys;
|
|
16
|
+
readonly hostVersion: string;
|
|
17
|
+
readonly hostName: string;
|
|
18
|
+
readonly platform: string;
|
|
19
|
+
readonly fetch: typeof fetch;
|
|
20
|
+
/** A turn may be waiting: claim until not_found, then ask for running turns' signals. */
|
|
21
|
+
readonly onWake: (link: AppLink) => void;
|
|
22
|
+
/** Whether a turn runs on this link, which keeps a poll deployment's `/sync` at the active interval. */
|
|
23
|
+
readonly busy?: (() => boolean) | undefined;
|
|
24
|
+
/** Any 401, or the stream closed for revocation. */
|
|
25
|
+
readonly onRevoked: (grant: AppGrant) => void;
|
|
26
|
+
/** No shared protocol: shown locally ("update Parlor Host"); the link stays down. */
|
|
27
|
+
readonly onIncompatible: (grant: AppGrant, welcome: Extract<HostWelcome, {
|
|
28
|
+
readonly accepted: false;
|
|
29
|
+
}>) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* `idle` until started; `serving` wakes the host for turns; `draining` stays up for the
|
|
33
|
+
* turns it runs but wakes nobody (a newer service took over the claiming); `closed` is final.
|
|
34
|
+
*/
|
|
35
|
+
export type LinkMode = "idle" | "serving" | "draining" | "closed";
|
|
36
|
+
/** The host's whole conversation with one app, each request carrying a fresh assertion. */
|
|
37
|
+
export interface AppLink {
|
|
38
|
+
readonly grant: AppGrant;
|
|
39
|
+
readonly api: HostApi;
|
|
40
|
+
/** Set once `hostHello` was welcomed; every other call waits for it. */
|
|
41
|
+
readonly protocol: number | undefined;
|
|
42
|
+
readonly mode: LinkMode;
|
|
43
|
+
start(): void;
|
|
44
|
+
/** Stops waking the host and keeps everything else, so running turns finish on this link. */
|
|
45
|
+
drain(): void;
|
|
46
|
+
/** With `revoke`, a refused revoke throws and leaves the link running. */
|
|
47
|
+
stop(options?: {
|
|
48
|
+
readonly revoke?: boolean | undefined;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export declare function createAppLink(grant: AppGrant, deps: AppLinkDeps): AppLink;
|
|
52
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,KAAK,iBAAiB,EAAyB,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAItD,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,eAAO,MAAM,cAAc,OAAQ,CAAC;AACpC,eAAO,MAAM,YAAY,QAAS,CAAC;AAKnC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAChD,6FAA6F;IAC7F,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wGAAwG;IACxG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5C,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC9C,qFAAqF;IACrF,QAAQ,CAAC,cAAc,EAAE,CACvB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,OAAO,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAA;KAAE,CAAC,KACxD,IAAI,CAAC;CACX;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;AAElE,2FAA2F;AAC3F,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,KAAK,IAAI,IAAI,CAAC;IACd,6FAA6F;IAC7F,KAAK,IAAI,IAAI,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1E;AAuFD,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAyMzE"}
|
package/dist/link.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { CHAT_ACTIVITY_TAG, CHAT_TAG, HOST_PROTOCOL, } from "@parlor/chat";
|
|
2
|
+
import { MAX_STREAM_BODY_BYTES } from "@parlor/chat/server";
|
|
3
|
+
import { createActionClient, createPluginClient, } from "@parlor/client";
|
|
4
|
+
import { createSyncClient } from "@parlor/sync/client";
|
|
5
|
+
import { logEvent } from "./log.js";
|
|
6
|
+
import { revokeGrant } from "./revoke.js";
|
|
7
|
+
export const RECONNECT_MAX_MS = 4_000;
|
|
8
|
+
export const ACTIVE_POLL_MS = 2_000;
|
|
9
|
+
export const IDLE_POLL_MS = 15_000;
|
|
10
|
+
const USER_ACTIVE_WINDOW_MS = 2 * 60_000;
|
|
11
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
12
|
+
const RETRY_BASE_MS = 250;
|
|
13
|
+
const MAX_BATCH_BYTES = (MAX_STREAM_BODY_BYTES * 3) / 4;
|
|
14
|
+
class LinkDown extends Error {
|
|
15
|
+
name = "LinkDown";
|
|
16
|
+
}
|
|
17
|
+
function coalesce(frames) {
|
|
18
|
+
const merged = [];
|
|
19
|
+
for (const frame of frames) {
|
|
20
|
+
const last = merged.at(-1);
|
|
21
|
+
if (last?.type === "delta" &&
|
|
22
|
+
frame.type === "delta" &&
|
|
23
|
+
last.itemId === frame.itemId &&
|
|
24
|
+
last.offset + last.text.length === frame.offset) {
|
|
25
|
+
merged[merged.length - 1] = { ...last, text: last.text + frame.text };
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
merged.push(frame);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return merged;
|
|
32
|
+
}
|
|
33
|
+
function batches(frames) {
|
|
34
|
+
const out = [];
|
|
35
|
+
let current = [];
|
|
36
|
+
let bytes = 0;
|
|
37
|
+
for (const frame of frames) {
|
|
38
|
+
const size = JSON.stringify(frame).length;
|
|
39
|
+
if (current.length > 0 && bytes + size > MAX_BATCH_BYTES) {
|
|
40
|
+
out.push(current);
|
|
41
|
+
current = [];
|
|
42
|
+
bytes = 0;
|
|
43
|
+
}
|
|
44
|
+
current.push(frame);
|
|
45
|
+
bytes += size;
|
|
46
|
+
}
|
|
47
|
+
if (current.length > 0)
|
|
48
|
+
out.push(current);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
|
+
class RequestTimeout extends Error {
|
|
53
|
+
name = "RequestTimeout";
|
|
54
|
+
constructor(ms) {
|
|
55
|
+
super(`The app did not answer within ${ms} ms.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function untilHeaders(inner, ms) {
|
|
59
|
+
return async (input, init) => {
|
|
60
|
+
const own = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => own.abort(new RequestTimeout(ms)), ms);
|
|
62
|
+
const signal = init?.signal ? AbortSignal.any([init.signal, own.signal]) : own.signal;
|
|
63
|
+
try {
|
|
64
|
+
return await inner(input, { ...init, signal });
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function adaptivePoll(advertised, busy) {
|
|
72
|
+
let activeUntil = 0;
|
|
73
|
+
const idle = () => advertised && !busy() && Date.now() >= activeUntil;
|
|
74
|
+
return {
|
|
75
|
+
pollEveryMs: () => (idle() ? IDLE_POLL_MS : ACTIVE_POLL_MS),
|
|
76
|
+
touch: () => {
|
|
77
|
+
const wasIdle = idle();
|
|
78
|
+
activeUntil = Date.now() + USER_ACTIVE_WINDOW_MS;
|
|
79
|
+
return wasIdle;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function createAppLink(grant, deps) {
|
|
84
|
+
const origin = grant.appOrigin.replace(/\/+$/, "");
|
|
85
|
+
let welcome;
|
|
86
|
+
let mode = "idle";
|
|
87
|
+
const modeNow = () => mode;
|
|
88
|
+
let revoked = false;
|
|
89
|
+
let sync;
|
|
90
|
+
let heartbeat;
|
|
91
|
+
const streams = new Map();
|
|
92
|
+
let admit = () => undefined;
|
|
93
|
+
let refuse = () => undefined;
|
|
94
|
+
const welcomed = new Promise((resolve, reject) => {
|
|
95
|
+
admit = resolve;
|
|
96
|
+
refuse = reject;
|
|
97
|
+
});
|
|
98
|
+
welcomed.catch(() => undefined);
|
|
99
|
+
const signedFetch = async (input, init) => {
|
|
100
|
+
if (mode === "closed")
|
|
101
|
+
throw new LinkDown("This app's link is closed.");
|
|
102
|
+
const request = new Request(input, init);
|
|
103
|
+
const method = request.method === "GET" ? "GET" : "POST";
|
|
104
|
+
const assertion = await deps.keys.assert(origin, { method, url: request.url });
|
|
105
|
+
const headers = new Headers(request.headers);
|
|
106
|
+
headers.set("authorization", `ParlorHost ${assertion}`);
|
|
107
|
+
const response = await deps.fetch(new Request(request, { headers }));
|
|
108
|
+
logEvent("http_request", {
|
|
109
|
+
method,
|
|
110
|
+
path: new URL(request.url).pathname,
|
|
111
|
+
status: response.status,
|
|
112
|
+
});
|
|
113
|
+
if (response.status === 401 && !revoked && modeNow() !== "closed") {
|
|
114
|
+
revoked = true;
|
|
115
|
+
queueMicrotask(() => deps.onRevoked(grant));
|
|
116
|
+
}
|
|
117
|
+
return response;
|
|
118
|
+
};
|
|
119
|
+
const timedFetch = untilHeaders(signedFetch, REQUEST_TIMEOUT_MS);
|
|
120
|
+
const plugin = createPluginClient(createActionClient({ baseUrl: origin, fetch: timedFetch }), "chat");
|
|
121
|
+
const drained = async (turnId) => {
|
|
122
|
+
if (turnId === undefined)
|
|
123
|
+
return;
|
|
124
|
+
while (true) {
|
|
125
|
+
const posting = streams.get(turnId)?.posting;
|
|
126
|
+
if (!posting)
|
|
127
|
+
return;
|
|
128
|
+
await posting;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const invoke = async (name, input, options) => {
|
|
132
|
+
if (name !== "hostHello")
|
|
133
|
+
await welcomed;
|
|
134
|
+
if (name === "hostAppend" || name === "hostFinish") {
|
|
135
|
+
await drained(input.turnId);
|
|
136
|
+
}
|
|
137
|
+
return plugin.invoke(name, input, options);
|
|
138
|
+
};
|
|
139
|
+
const calls = {
|
|
140
|
+
invoke,
|
|
141
|
+
call: async (name, input, options) => (await invoke(name, input, options)).data,
|
|
142
|
+
};
|
|
143
|
+
const post = (path, body) => timedFetch(`${origin}/_parlor/chat/${path}`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "content-type": "application/json" },
|
|
146
|
+
body: JSON.stringify(body),
|
|
147
|
+
});
|
|
148
|
+
const flush = async (turnId, stream) => {
|
|
149
|
+
while (stream.pending.length > 0) {
|
|
150
|
+
if (mode === "closed")
|
|
151
|
+
break;
|
|
152
|
+
const frames = coalesce(stream.pending);
|
|
153
|
+
stream.pending = [];
|
|
154
|
+
for (const chunk of batches(frames)) {
|
|
155
|
+
await post("stream", { turnId, frames: chunk }).catch(() => undefined);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
stream.posting = undefined;
|
|
159
|
+
if (stream.pending.length === 0)
|
|
160
|
+
streams.delete(turnId);
|
|
161
|
+
};
|
|
162
|
+
const api = {
|
|
163
|
+
calls,
|
|
164
|
+
stream(batch) {
|
|
165
|
+
if (mode === "closed" || batch.frames.length === 0)
|
|
166
|
+
return;
|
|
167
|
+
let stream = streams.get(batch.turnId);
|
|
168
|
+
if (!stream) {
|
|
169
|
+
stream = { pending: [], posting: undefined };
|
|
170
|
+
streams.set(batch.turnId, stream);
|
|
171
|
+
}
|
|
172
|
+
stream.pending.push(...batch.frames);
|
|
173
|
+
if (!stream.posting)
|
|
174
|
+
stream.posting = flush(batch.turnId, stream);
|
|
175
|
+
},
|
|
176
|
+
async heartbeat() {
|
|
177
|
+
await welcomed;
|
|
178
|
+
await post("heartbeat", {});
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
const close = () => {
|
|
182
|
+
mode = "closed";
|
|
183
|
+
sync?.stop();
|
|
184
|
+
sync = undefined;
|
|
185
|
+
if (heartbeat)
|
|
186
|
+
clearInterval(heartbeat);
|
|
187
|
+
heartbeat = undefined;
|
|
188
|
+
refuse(new LinkDown("This app's link is closed."));
|
|
189
|
+
};
|
|
190
|
+
const hello = async () => {
|
|
191
|
+
for (let attempt = 0;; attempt += 1) {
|
|
192
|
+
if (mode !== "serving" && mode !== "draining")
|
|
193
|
+
return undefined;
|
|
194
|
+
try {
|
|
195
|
+
return await plugin.call("hostHello", {
|
|
196
|
+
protocols: { min: 1, max: HOST_PROTOCOL },
|
|
197
|
+
hostVersion: deps.hostVersion,
|
|
198
|
+
name: deps.hostName,
|
|
199
|
+
platform: deps.platform,
|
|
200
|
+
agents: [],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
if (revoked || modeNow() === "closed")
|
|
205
|
+
return undefined;
|
|
206
|
+
await sleep(Math.min(RETRY_BASE_MS * 2 ** attempt, RECONNECT_MAX_MS));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const link = {
|
|
211
|
+
grant,
|
|
212
|
+
api,
|
|
213
|
+
get protocol() {
|
|
214
|
+
return welcome?.protocol;
|
|
215
|
+
},
|
|
216
|
+
get mode() {
|
|
217
|
+
return mode;
|
|
218
|
+
},
|
|
219
|
+
start() {
|
|
220
|
+
if (mode !== "idle")
|
|
221
|
+
return;
|
|
222
|
+
mode = "serving";
|
|
223
|
+
void hello().then((answer) => {
|
|
224
|
+
if (!answer || modeNow() === "closed")
|
|
225
|
+
return;
|
|
226
|
+
if (!answer.accepted) {
|
|
227
|
+
close();
|
|
228
|
+
logEvent("link_closed", { appOrigin: origin, reason: "incompatible" });
|
|
229
|
+
deps.onIncompatible(grant, answer);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
welcome = answer;
|
|
233
|
+
admit();
|
|
234
|
+
const polling = adaptivePoll(answer.userActivity === true, () => deps.busy?.() ?? false);
|
|
235
|
+
sync = createSyncClient({
|
|
236
|
+
request: (path, init) => signedFetch(`${origin}${path}`, init),
|
|
237
|
+
coalesceMs: 0,
|
|
238
|
+
reconnectMaxMs: RECONNECT_MAX_MS,
|
|
239
|
+
pollEveryMs: polling.pollEveryMs,
|
|
240
|
+
onEvents: (events) => {
|
|
241
|
+
const heads = events.flatMap((event) => event.tags.map((tag) => tag[0]));
|
|
242
|
+
if (heads.some((head) => head === CHAT_TAG || head === CHAT_ACTIVITY_TAG)) {
|
|
243
|
+
if (polling.touch())
|
|
244
|
+
sync?.reconnectNow();
|
|
245
|
+
}
|
|
246
|
+
if (modeNow() === "serving" && heads.includes(CHAT_TAG))
|
|
247
|
+
deps.onWake(link);
|
|
248
|
+
},
|
|
249
|
+
onUnauthorized: () => undefined,
|
|
250
|
+
onError: () => undefined,
|
|
251
|
+
});
|
|
252
|
+
sync.start();
|
|
253
|
+
heartbeat = setInterval(() => void api.heartbeat().catch(() => undefined), answer.heartbeatMs);
|
|
254
|
+
heartbeat.unref();
|
|
255
|
+
if (modeNow() === "serving")
|
|
256
|
+
deps.onWake(link);
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
drain() {
|
|
260
|
+
if (mode === "serving")
|
|
261
|
+
mode = "draining";
|
|
262
|
+
},
|
|
263
|
+
async stop(options = {}) {
|
|
264
|
+
if (options.revoke && !grant.dev) {
|
|
265
|
+
await revokeGrant({
|
|
266
|
+
appOrigin: origin,
|
|
267
|
+
appName: grant.appName,
|
|
268
|
+
assert: (request) => deps.keys.assert(origin, request),
|
|
269
|
+
fetch: deps.fetch,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
close();
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
return link;
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=link.js.map
|
package/dist/link.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"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"}
|
package/dist/lock.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const LOCK_TOKEN_ENV = "PARLOR_HOST_LOCK_TOKEN";
|
|
2
|
+
export declare function holdsLock(path: string, token: string): Promise<boolean>;
|
|
3
|
+
/** A lock is held until its owner dies or its pid is taken by a process that started after the lock was written. */
|
|
4
|
+
export declare function withFileLock<T>(path: string, work: (token: string) => Promise<T>): Promise<T>;
|
|
5
|
+
/** Runs `work` under the lock `inherited` names when this process's parent holds it, else under a lock of its own. */
|
|
6
|
+
export declare function withInheritedLock<T>(path: string, inherited: string | undefined, work: () => Promise<T>): Promise<T>;
|
|
7
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,cAAc,2BAA2B,CAAC;AA6EvD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7E;AAED,oHAAoH;AACpH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC,CAQZ;AAED,sHAAsH;AACtH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACrB,OAAO,CAAC,CAAC,CAAC,CAGZ"}
|
package/dist/lock.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const run = promisify(execFile);
|
|
6
|
+
export const LOCK_TOKEN_ENV = "PARLOR_HOST_LOCK_TOKEN";
|
|
7
|
+
const RETRY_MS = 20;
|
|
8
|
+
const UNWRITTEN_MS = 1_000;
|
|
9
|
+
const ACQUIRE_TIMEOUT_MS = 120_000;
|
|
10
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
+
function alive(pid) {
|
|
12
|
+
try {
|
|
13
|
+
process.kill(pid, 0);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return error.code === "EPERM";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function startedAt(pid) {
|
|
21
|
+
try {
|
|
22
|
+
const { stdout } = await run("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
23
|
+
env: { ...process.env, LC_ALL: "C" },
|
|
24
|
+
});
|
|
25
|
+
const started = Date.parse(stdout.trim());
|
|
26
|
+
return Number.isNaN(started) ? undefined : started;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** `ps` reports whole seconds, rounded down, so a real owner never reads as started after its own lock. */
|
|
33
|
+
async function reusedPid(pid, lockedAt) {
|
|
34
|
+
const started = await startedAt(pid);
|
|
35
|
+
return started !== undefined && started > lockedAt;
|
|
36
|
+
}
|
|
37
|
+
async function stale(path, verdicts) {
|
|
38
|
+
try {
|
|
39
|
+
const [text, { mtimeMs }] = await Promise.all([readFile(path, "utf8"), stat(path)]);
|
|
40
|
+
const age = Date.now() - mtimeMs;
|
|
41
|
+
const pid = Number.parseInt(text, 10);
|
|
42
|
+
if (!Number.isInteger(pid))
|
|
43
|
+
return age > UNWRITTEN_MS;
|
|
44
|
+
if (!alive(pid))
|
|
45
|
+
return true;
|
|
46
|
+
const key = `${pid}:${mtimeMs}`;
|
|
47
|
+
let reused = verdicts.get(key);
|
|
48
|
+
if (reused === undefined) {
|
|
49
|
+
reused = await reusedPid(pid, mtimeMs);
|
|
50
|
+
verdicts.set(key, reused);
|
|
51
|
+
}
|
|
52
|
+
return reused;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function acquire(path, token) {
|
|
59
|
+
const deadline = Date.now() + ACQUIRE_TIMEOUT_MS;
|
|
60
|
+
const verdicts = new Map();
|
|
61
|
+
for (;;) {
|
|
62
|
+
try {
|
|
63
|
+
const file = await open(path, "wx", 0o600);
|
|
64
|
+
await file.writeFile(`${process.pid}\n${token}\n`);
|
|
65
|
+
await file.close();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error.code !== "EEXIST")
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (await stale(path, verdicts)) {
|
|
73
|
+
await rename(path, `${path}.stale.${process.pid}`).catch(() => undefined);
|
|
74
|
+
await rm(`${path}.stale.${process.pid}`, { force: true });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (Date.now() > deadline)
|
|
78
|
+
throw new Error(`Could not take the lock ${path}.`);
|
|
79
|
+
await sleep(RETRY_MS);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const tokenOf = (text) => text.split("\n")[1];
|
|
83
|
+
export async function holdsLock(path, token) {
|
|
84
|
+
const text = await readFile(path, "utf8").catch(() => undefined);
|
|
85
|
+
return text !== undefined && tokenOf(text) === token;
|
|
86
|
+
}
|
|
87
|
+
/** A lock is held until its owner dies or its pid is taken by a process that started after the lock was written. */
|
|
88
|
+
export async function withFileLock(path, work) {
|
|
89
|
+
const token = randomUUID();
|
|
90
|
+
await acquire(path, token);
|
|
91
|
+
try {
|
|
92
|
+
return await work(token);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (await holdsLock(path, token))
|
|
96
|
+
await rm(path, { force: true });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Runs `work` under the lock `inherited` names when this process's parent holds it, else under a lock of its own. */
|
|
100
|
+
export async function withInheritedLock(path, inherited, work) {
|
|
101
|
+
if (inherited && (await holdsLock(path, inherited)))
|
|
102
|
+
return work();
|
|
103
|
+
return withFileLock(path, work);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=lock.js.map
|
package/dist/lock.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAKA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAOvF"}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
let file;
|
|
4
|
+
export function openHostLog(path) {
|
|
5
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
6
|
+
file = path;
|
|
7
|
+
}
|
|
8
|
+
export function logEvent(event, fields) {
|
|
9
|
+
if (file === undefined)
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
appendFileSync(file, `${JSON.stringify({ at: Date.now(), event, ...fields })}\n`, {
|
|
13
|
+
mode: 0o600,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch { }
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,IAAwB,CAAC;AAE7B,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,IAAI,GAAG,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,MAAyC;IAC/E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,CAAC;QACH,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE;YAChF,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|