@verax-ai/body 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.
Files changed (63) hide show
  1. package/dist/approve-cli.d.ts +12 -0
  2. package/dist/approve-cli.js +93 -0
  3. package/dist/atomic-write.d.ts +1 -0
  4. package/dist/atomic-write.js +65 -0
  5. package/dist/auth.d.ts +10 -0
  6. package/dist/auth.js +59 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +112 -0
  9. package/dist/config.d.ts +27 -0
  10. package/dist/config.js +71 -0
  11. package/dist/desktop.d.ts +29 -0
  12. package/dist/desktop.js +341 -0
  13. package/dist/doctor.d.ts +8 -0
  14. package/dist/doctor.js +342 -0
  15. package/dist/halt.d.ts +1 -0
  16. package/dist/halt.js +12 -0
  17. package/dist/health-extras.d.ts +11 -0
  18. package/dist/health-extras.js +47 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.js +4 -0
  21. package/dist/inputs-read.d.ts +12 -0
  22. package/dist/inputs-read.js +64 -0
  23. package/dist/inventory-file.d.ts +18 -0
  24. package/dist/inventory-file.js +41 -0
  25. package/dist/keys.d.ts +12 -0
  26. package/dist/keys.js +55 -0
  27. package/dist/main.d.ts +1 -0
  28. package/dist/main.js +34 -0
  29. package/dist/metrics.d.ts +2 -0
  30. package/dist/metrics.js +23 -0
  31. package/dist/no-bypass-scan.d.ts +10 -0
  32. package/dist/no-bypass-scan.js +95 -0
  33. package/dist/operator-cli.d.ts +1 -0
  34. package/dist/operator-cli.js +25 -0
  35. package/dist/operator-credentials.d.ts +23 -0
  36. package/dist/operator-credentials.js +56 -0
  37. package/dist/operator-pairing.d.ts +25 -0
  38. package/dist/operator-pairing.js +67 -0
  39. package/dist/policy-store.d.ts +3 -0
  40. package/dist/policy-store.js +44 -0
  41. package/dist/reconcile-cli.d.ts +24 -0
  42. package/dist/reconcile-cli.js +258 -0
  43. package/dist/revoke.d.ts +1 -0
  44. package/dist/revoke.js +16 -0
  45. package/dist/rp-config.d.ts +15 -0
  46. package/dist/rp-config.js +30 -0
  47. package/dist/server.d.ts +5 -0
  48. package/dist/server.js +484 -0
  49. package/dist/tools/audit.d.ts +2 -0
  50. package/dist/tools/audit.js +48 -0
  51. package/dist/tools/memory.d.ts +10 -0
  52. package/dist/tools/memory.js +137 -0
  53. package/dist/tools/message.d.ts +4 -0
  54. package/dist/tools/message.js +44 -0
  55. package/dist/tools/spend.d.ts +3 -0
  56. package/dist/tools/spend.js +19 -0
  57. package/dist/unlock.d.ts +9 -0
  58. package/dist/unlock.js +109 -0
  59. package/dist/wiring.d.ts +23 -0
  60. package/dist/wiring.js +101 -0
  61. package/dist/witness.d.ts +33 -0
  62. package/dist/witness.js +321 -0
  63. package/package.json +40 -0
@@ -0,0 +1,12 @@
1
+ export declare function resolveApproveRef(pending: {
2
+ ref: string;
3
+ status?: string;
4
+ }[], given: string): {
5
+ ok: true;
6
+ ref: string;
7
+ } | {
8
+ ok: false;
9
+ reason: "unknown-ref" | "ambiguous-ref";
10
+ candidates: string[];
11
+ };
12
+ export declare function runApprove(argv: string[], writeErr?: (s: string) => void, writeOut?: (s: string) => void): Promise<number>;
@@ -0,0 +1,93 @@
1
+ import { userInfo } from "node:os";
2
+ import { approvePending, approvalsLogFor, enqueueApprovalCommand, FileLedger, loadApprovalsFromDir } from "@verax-ai/proxy";
3
+ import { loadOrCreateSigners } from "./keys.js";
4
+ function operatorName() {
5
+ try {
6
+ const name = userInfo().username;
7
+ return name.length > 0 ? name : "unknown";
8
+ }
9
+ catch {
10
+ return "unknown";
11
+ }
12
+ }
13
+ export function resolveApproveRef(pending, given) {
14
+ const open = pending.filter((r) => r.status === "pending" || r.status === undefined);
15
+ const exact = open.find((r) => r.ref === given);
16
+ if (exact)
17
+ return { ok: true, ref: exact.ref };
18
+ const suffix = `:${given}`;
19
+ const hits = open.filter((r) => r.ref.endsWith(suffix)).map((r) => r.ref);
20
+ const unique = [...new Set(hits)].sort();
21
+ if (unique.length === 1)
22
+ return { ok: true, ref: unique[0] };
23
+ if (unique.length > 1)
24
+ return { ok: false, reason: "ambiguous-ref", candidates: unique };
25
+ return { ok: false, reason: "unknown-ref", candidates: [] };
26
+ }
27
+ export async function runApprove(argv, writeErr = (s) => process.stderr.write(s), writeOut = (s) => process.stdout.write(s)) {
28
+ const rest = argv.slice(1);
29
+ const stateDir = rest[0];
30
+ const given = rest[1];
31
+ if (!stateDir || !given || rest.length !== 2) {
32
+ writeErr("verax approve <stateDir> <ref>\n");
33
+ return 78;
34
+ }
35
+ const resolved = resolveApproveRef(loadApprovalsFromDir(stateDir), given);
36
+ if (!resolved.ok) {
37
+ if (resolved.reason === "ambiguous-ref") {
38
+ writeErr(`ambiguous-ref\n${resolved.candidates.join("\n")}\n`);
39
+ return 78;
40
+ }
41
+ writeErr("approve-unknown-ref\n");
42
+ return 78;
43
+ }
44
+ const ref = resolved.ref;
45
+ let ledger;
46
+ try {
47
+ ledger = new FileLedger(stateDir);
48
+ }
49
+ catch (err) {
50
+ const msg = err instanceof Error ? err.message : "";
51
+ if (msg.startsWith("ledger-locked")) {
52
+ enqueueApprovalCommand(stateDir, { ref, approverId: operatorName(), atMs: Date.now() });
53
+ writeOut("approve-queued\n");
54
+ return 0;
55
+ }
56
+ writeErr(`approve-failed:${msg.split("\n")[0] ?? "unknown"}\n`);
57
+ return 1;
58
+ }
59
+ try {
60
+ const signers = loadOrCreateSigners(stateDir);
61
+ const approvals = approvalsLogFor(ledger);
62
+ const decisions = await ledger.decisions();
63
+ const defer = decisions.find((d) => d.claims.ref === ref && d.claims.decision === "defer");
64
+ if (!defer) {
65
+ writeErr("approve-unknown-ref\n");
66
+ return 78;
67
+ }
68
+ const result = await approvePending({
69
+ ledger,
70
+ recordSigner: signers.recordSigner,
71
+ now: () => Date.now(),
72
+ nonce: () => crypto.randomUUID(),
73
+ ref,
74
+ approverId: operatorName(),
75
+ via: "cli",
76
+ policyHash: defer.claims.policyHash,
77
+ approvals,
78
+ });
79
+ if (!result.ok) {
80
+ writeErr(`approve-${result.reason}\n`);
81
+ return result.reason === "expired" ? 2 : 1;
82
+ }
83
+ writeOut(`approved:${result.allowRef}\n`);
84
+ return 0;
85
+ }
86
+ catch (err) {
87
+ writeErr(`approve-failed:${err instanceof Error ? err.message : "unknown"}\n`);
88
+ return 1;
89
+ }
90
+ finally {
91
+ ledger.close();
92
+ }
93
+ }
@@ -0,0 +1 @@
1
+ export declare function writeFileAtomic(path: string, text: string): void;
@@ -0,0 +1,65 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { chmodSync, mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ /**
5
+ * Replace a file's contents in one step, so a reader sees the old bytes or the
6
+ * new ones and never a half-written file.
7
+ *
8
+ * Writing in place is not that: measured on 11 Sep 2026, a reader polling this
9
+ * path every 20 ms while the file was rewritten 60 times got 12 unparseable
10
+ * reads. The listen file is read by another process - that is its whole job -
11
+ * and a torn read becomes `null`, which the caller cannot tell from "nothing
12
+ * is running".
13
+ *
14
+ * The rename needs the retry. On Windows it fails with EPERM while a reader
15
+ * holds the destination open, and in the same measurement 26 of 60 plain
16
+ * renames failed that way. Retrying briefly lost none of them and still let no
17
+ * torn read through.
18
+ *
19
+ * The temporary name carries this process and a random suffix. A fixed `.tmp`
20
+ * next to the destination is what two writers, or a crashed writer, collide
21
+ * on.
22
+ */
23
+ const napper = new Int32Array(new SharedArrayBuffer(4));
24
+ /**
25
+ * Sleep without spinning. The first version of the retry below burned the
26
+ * processor between attempts, which is the worst thing to do while waiting for
27
+ * another process to let go of a file: under the unit suite's own load the
28
+ * spin starved the reader it was waiting on, the retries ran out in 200 ms,
29
+ * and the write threw EPERM. Atomics.wait yields the core instead.
30
+ */
31
+ function napSync(ms) {
32
+ Atomics.wait(napper, 0, 0, ms);
33
+ }
34
+ export function writeFileAtomic(path, text) {
35
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
36
+ const tmp = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
37
+ writeFileSync(tmp, text, { encoding: "utf8", mode: 0o600 });
38
+ try {
39
+ for (let attempt = 0;; attempt += 1) {
40
+ try {
41
+ renameSync(tmp, path);
42
+ chmodSync(path, 0o600);
43
+ return;
44
+ }
45
+ catch (err) {
46
+ const code = err.code;
47
+ // Two seconds of patience. A reader holds this file for microseconds
48
+ // when the machine is idle; the budget is for the machine that is not.
49
+ if (attempt >= 100 || (code !== "EPERM" && code !== "EACCES" && code !== "EBUSY")) {
50
+ throw err;
51
+ }
52
+ napSync(20);
53
+ }
54
+ }
55
+ }
56
+ catch (err) {
57
+ try {
58
+ unlinkSync(tmp);
59
+ }
60
+ catch {
61
+ // rename may already have moved it
62
+ }
63
+ throw err;
64
+ }
65
+ }
package/dist/auth.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { type JWTPayload } from "jose";
2
+ import type { Principal } from "@verax-ai/proxy";
3
+ export type VerifiedBearer = {
4
+ principal: Principal;
5
+ payload: JWTPayload;
6
+ };
7
+ export declare function resourceMetadataUrl(audience: string): string;
8
+ export declare function wwwAuthenticate(audience: string): string;
9
+ export declare function createVerifier(jwksUrl: string, issuer: string, audience: string): (token: string) => Promise<VerifiedBearer>;
10
+ export declare function readBearer(header: string | undefined): string | null;
package/dist/auth.js ADDED
@@ -0,0 +1,59 @@
1
+ import { createRemoteJWKSet, jwtVerify } from "jose";
2
+ const ALGS = ["ES256", "EdDSA"];
3
+ export function resourceMetadataUrl(audience) {
4
+ const u = new URL(audience);
5
+ const path = u.pathname.replace(/\/+$/, "").replace(/^\/+/, "");
6
+ if (path === "") {
7
+ return `${u.origin}/.well-known/oauth-protected-resource`;
8
+ }
9
+ return `${u.origin}/.well-known/oauth-protected-resource/${path}`;
10
+ }
11
+ export function wwwAuthenticate(audience) {
12
+ return `Bearer resource_metadata="${resourceMetadataUrl(audience)}"`;
13
+ }
14
+ export function createVerifier(jwksUrl, issuer, audience) {
15
+ const jwks = createRemoteJWKSet(new URL(jwksUrl));
16
+ return async (token) => {
17
+ const { payload } = await jwtVerify(token, jwks, {
18
+ issuer,
19
+ audience,
20
+ algorithms: [...ALGS],
21
+ requiredClaims: ["exp", "iat", "sub"],
22
+ clockTolerance: 60,
23
+ });
24
+ const nowSec = Math.floor(Date.now() / 1000);
25
+ if (typeof payload.iat === "number" && payload.iat > nowSec + 60) {
26
+ throw new Error("iat-in-future");
27
+ }
28
+ const sub = typeof payload.sub === "string" ? payload.sub : "";
29
+ if (sub === "") {
30
+ throw new Error("missing-sub");
31
+ }
32
+ const scopes = new Set();
33
+ if (typeof payload.scope === "string") {
34
+ for (const part of payload.scope.split(/\s+/)) {
35
+ if (part !== "")
36
+ scopes.add(part);
37
+ }
38
+ }
39
+ const iss = typeof payload.iss === "string" ? payload.iss : undefined;
40
+ const tenant = typeof payload.tenant === "string" && payload.tenant !== "" ? payload.tenant : undefined;
41
+ const org = typeof payload.org === "string" && payload.org !== "" ? payload.org : undefined;
42
+ return {
43
+ principal: {
44
+ brain: sub,
45
+ scopes,
46
+ ...(iss ? { iss } : {}),
47
+ ...(tenant ? { tenant } : {}),
48
+ ...(org ? { org } : {}),
49
+ },
50
+ payload,
51
+ };
52
+ };
53
+ }
54
+ export function readBearer(header) {
55
+ if (!header)
56
+ return null;
57
+ const match = /^Bearer\s+(\S+)/i.exec(header);
58
+ return match ? match[1] : null;
59
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { runApprove } from "./approve-cli.js";
6
+ import { desktopMain } from "./desktop.js";
7
+ import { doctorExit, runDoctor } from "./doctor.js";
8
+ import { runHalt } from "./halt.js";
9
+ import { main } from "./main.js";
10
+ import { runOperator } from "./operator-cli.js";
11
+ import { runReconcile } from "./reconcile-cli.js";
12
+ import { runUnlock } from "./unlock.js";
13
+ import { runWitness } from "./witness.js";
14
+ const HELP = `verax - the body an agent asks before it acts, and the ledger it answers from.
15
+
16
+ Usage: verax <command> [options]
17
+
18
+ (no command) serve the body over MCP stdio, or HTTP when VERAX_PORT is set
19
+ doctor [--json] check the configuration this process would run with
20
+ approve <args> approve a waiting request from this machine
21
+ operator <args> enrol an operator and manage their passkeys
22
+ reconcile <args> compare the ledger against a statement
23
+ witness <stateDir> run the witness alongside a body
24
+ halt <stateDir> stop the body from allowing anything further
25
+ unlock [--force] <stateDir> clear a stale ledger lock
26
+ desktop <args> open the local panel
27
+
28
+ --help, -h print this
29
+ --version, -v print the version
30
+
31
+ Serving needs VERAX_ISSUER, VERAX_JWKS_URL and VERAX_AUDIENCE; \`verax doctor\`
32
+ names what is missing. Records stay on this machine, under VERAX_STATE_DIR.
33
+ `;
34
+ function version() {
35
+ // The published package answers with its own version, not a copy of it.
36
+ const here = dirname(fileURLToPath(import.meta.url));
37
+ for (const path of [join(here, "..", "package.json"), join(here, "..", "..", "package.json")]) {
38
+ try {
39
+ const raw = JSON.parse(readFileSync(path, "utf8"));
40
+ if (raw.name === "@verax-ai/body" && typeof raw.version === "string")
41
+ return raw.version;
42
+ }
43
+ catch {
44
+ // try the next candidate: the file layout differs between src and dist.
45
+ }
46
+ }
47
+ return "unknown";
48
+ }
49
+ const argv = process.argv.slice(2);
50
+ // Asking what this is must work before it is configured. Without this, the
51
+ // first thing a published `verax --help` said was that three environment
52
+ // variables were missing.
53
+ if (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
54
+ process.stdout.write(HELP);
55
+ process.exit(0);
56
+ }
57
+ if (argv[0] === "--version" || argv[0] === "-v") {
58
+ process.stdout.write(`${version()}\n`);
59
+ process.exit(0);
60
+ }
61
+ if (argv[0] === "approve") {
62
+ process.exit(await runApprove(argv));
63
+ }
64
+ if (argv[0] === "operator") {
65
+ process.exit(await runOperator(argv));
66
+ }
67
+ if (argv[0] === "halt") {
68
+ const stateDir = argv[1];
69
+ if (!stateDir) {
70
+ process.stderr.write("verax halt <stateDir>\n");
71
+ process.exit(78);
72
+ }
73
+ process.exit(runHalt(stateDir));
74
+ }
75
+ if (argv[0] === "unlock") {
76
+ const force = argv.includes("--force");
77
+ const stateDir = argv.slice(1).find((a) => a !== "--force");
78
+ if (!stateDir) {
79
+ process.stderr.write("verax unlock [--force] <stateDir>\n");
80
+ process.exit(78);
81
+ }
82
+ process.exit(runUnlock(stateDir, (s) => process.stderr.write(s), { force }));
83
+ }
84
+ if (argv[0] === "doctor") {
85
+ const json = argv.includes("--json");
86
+ const checks = runDoctor(process.env, process.argv);
87
+ if (json) {
88
+ process.stdout.write(`${JSON.stringify({ checks })}\n`);
89
+ }
90
+ else {
91
+ for (const c of checks) {
92
+ process.stdout.write(`${c.level}\t${c.id}\t${c.detail}\n`);
93
+ }
94
+ }
95
+ process.exit(doctorExit(checks));
96
+ }
97
+ if (argv[0] === "reconcile") {
98
+ process.exit(runReconcile(argv));
99
+ }
100
+ if (argv[0] === "desktop") {
101
+ process.exit(await desktopMain(argv));
102
+ }
103
+ if (argv[0] === "witness") {
104
+ const stateDir = argv[1];
105
+ if (!stateDir) {
106
+ process.stderr.write("verax witness <stateDir>\n");
107
+ process.exit(78);
108
+ }
109
+ await runWitness(stateDir);
110
+ process.exit(0);
111
+ }
112
+ await main();
@@ -0,0 +1,27 @@
1
+ export declare const EX_CONFIG = 78;
2
+ export type BodyConfig = {
3
+ issuer: string;
4
+ jwksUrl: string;
5
+ audience: string;
6
+ stateDir: string;
7
+ bindHost: string;
8
+ bindPort: number;
9
+ policyFile: string;
10
+ tlsTerminated: boolean;
11
+ /** Path to an inventory JSON file. Missing file is absence, not a fault. */
12
+ inventoryFile?: string | null;
13
+ };
14
+ export type ConfigResult = {
15
+ ok: true;
16
+ value: BodyConfig;
17
+ } | {
18
+ ok: false;
19
+ reason: string;
20
+ code: typeof EX_CONFIG;
21
+ };
22
+ export declare function isLoopbackHost(host: string): boolean;
23
+ export declare function loadConfig(env: NodeJS.ProcessEnv): ConfigResult;
24
+ /** `--inventory <file>` wins over VERAX_INVENTORY_FILE. */
25
+ export declare function overlayInventoryArg(env: NodeJS.ProcessEnv, argv: readonly string[]): NodeJS.ProcessEnv | {
26
+ error: string;
27
+ };
package/dist/config.js ADDED
@@ -0,0 +1,71 @@
1
+ export const EX_CONFIG = 78;
2
+ const LOOPBACK = new Set(["127.0.0.1", "::1", "localhost"]);
3
+ function parseBind(raw) {
4
+ const text = raw && raw.trim() !== "" ? raw.trim() : "127.0.0.1:8787";
5
+ const idx = text.lastIndexOf(":");
6
+ if (idx <= 0) {
7
+ return { host: text, port: 8787 };
8
+ }
9
+ const host = text.slice(0, idx);
10
+ const port = Number(text.slice(idx + 1));
11
+ return { host, port: Number.isInteger(port) ? port : 8787 };
12
+ }
13
+ export function isLoopbackHost(host) {
14
+ const stripped = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
15
+ return LOOPBACK.has(stripped);
16
+ }
17
+ export function loadConfig(env) {
18
+ const issuer = env.VERAX_ISSUER?.trim() ?? "";
19
+ const jwksUrl = env.VERAX_JWKS_URL?.trim() ?? "";
20
+ const audience = env.VERAX_AUDIENCE?.trim() ?? "";
21
+ if (issuer === "" || jwksUrl === "" || audience === "") {
22
+ return {
23
+ ok: false,
24
+ code: EX_CONFIG,
25
+ reason: "missing VERAX_ISSUER, VERAX_JWKS_URL, or VERAX_AUDIENCE",
26
+ };
27
+ }
28
+ const bind = parseBind(env.VERAX_BIND);
29
+ const tlsTerminated = env.VERAX_TLS_TERMINATED === "1";
30
+ if (!isLoopbackHost(bind.host) && !tlsTerminated) {
31
+ return {
32
+ ok: false,
33
+ code: EX_CONFIG,
34
+ reason: "non-loopback bind without VERAX_TLS_TERMINATED=1",
35
+ };
36
+ }
37
+ const stateDir = env.VERAX_STATE_DIR?.trim() ?? "";
38
+ if (stateDir === "") {
39
+ return { ok: false, code: EX_CONFIG, reason: "missing VERAX_STATE_DIR" };
40
+ }
41
+ const policyFile = env.VERAX_POLICY_FILE?.trim() ?? "";
42
+ if (policyFile === "") {
43
+ return { ok: false, code: EX_CONFIG, reason: "missing VERAX_POLICY_FILE" };
44
+ }
45
+ const inventoryRaw = env.VERAX_INVENTORY_FILE?.trim() ?? "";
46
+ return {
47
+ ok: true,
48
+ value: {
49
+ issuer,
50
+ jwksUrl,
51
+ audience,
52
+ stateDir,
53
+ bindHost: bind.host,
54
+ bindPort: bind.port,
55
+ policyFile,
56
+ tlsTerminated,
57
+ inventoryFile: inventoryRaw === "" ? null : inventoryRaw,
58
+ },
59
+ };
60
+ }
61
+ /** `--inventory <file>` wins over VERAX_INVENTORY_FILE. */
62
+ export function overlayInventoryArg(env, argv) {
63
+ const i = argv.indexOf("--inventory");
64
+ if (i === -1)
65
+ return env;
66
+ const path = argv[i + 1];
67
+ if (!path || path.startsWith("-")) {
68
+ return { error: "missing --inventory path" };
69
+ }
70
+ return { ...env, VERAX_INVENTORY_FILE: path };
71
+ }
@@ -0,0 +1,29 @@
1
+ export type DesktopOpts = {
2
+ stateDir: string;
3
+ panelPort: number;
4
+ issuerPort: number;
5
+ bodyPort: number;
6
+ browser?: string;
7
+ inventoryFile?: string;
8
+ };
9
+ /**
10
+ * True when the panel has no build, or when a watched source is newer than the
11
+ * one it has. The launcher used to skip the build whenever `dist/index.html`
12
+ * existed, so a session served whatever had been built once: editing the panel
13
+ * and reopening it showed the previous app, with no sign that it was stale.
14
+ */
15
+ export declare function panelBuildNeeded(distIndex: string, sourceRoots: readonly string[]): boolean;
16
+ export declare function parseDesktopArgs(argv: string[]): DesktopOpts | {
17
+ error: string;
18
+ };
19
+ export declare function portOpen(port: number, host?: string): Promise<boolean>;
20
+ export declare function killTree(pid: number | undefined): void;
21
+ /** The panel's redirect is its own origin, so the issuer has to be told which port
22
+ * this run put it on: the allow-list default only holds 5173 and 4173. */
23
+ export declare function issuerEnv(base: NodeJS.ProcessEnv, opts: {
24
+ stateDir: string;
25
+ issuerPort: number;
26
+ panelPort: number;
27
+ }, audience: string, issuerUrl: string): NodeJS.ProcessEnv;
28
+ export declare function runDesktop(opts: DesktopOpts, writeErr?: (s: string) => void): Promise<number>;
29
+ export declare function desktopMain(argv: string[]): Promise<number>;