@rndmjoker/proton-mcp 0.4.4
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 +20 -0
- package/README.md +405 -0
- package/dist/activity.d.ts +49 -0
- package/dist/activity.js +72 -0
- package/dist/activity.js.map +1 -0
- package/dist/bridge/certificate.d.ts +75 -0
- package/dist/bridge/certificate.js +176 -0
- package/dist/bridge/certificate.js.map +1 -0
- package/dist/bridge/connection.d.ts +99 -0
- package/dist/bridge/connection.js +244 -0
- package/dist/bridge/connection.js.map +1 -0
- package/dist/bridge/errors.d.ts +35 -0
- package/dist/bridge/errors.js +56 -0
- package/dist/bridge/errors.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.js +142 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials/availability.d.ts +62 -0
- package/dist/credentials/availability.js +107 -0
- package/dist/credentials/availability.js.map +1 -0
- package/dist/credentials/encrypted-file.d.ts +50 -0
- package/dist/credentials/encrypted-file.js +156 -0
- package/dist/credentials/encrypted-file.js.map +1 -0
- package/dist/credentials/keyring.d.ts +48 -0
- package/dist/credentials/keyring.js +97 -0
- package/dist/credentials/keyring.js.map +1 -0
- package/dist/credentials/plain-file.d.ts +24 -0
- package/dist/credentials/plain-file.js +78 -0
- package/dist/credentials/plain-file.js.map +1 -0
- package/dist/credentials/probe-keyring.d.ts +11 -0
- package/dist/credentials/probe-keyring.js +57 -0
- package/dist/credentials/probe-keyring.js.map +1 -0
- package/dist/credentials/session.d.ts +15 -0
- package/dist/credentials/session.js +21 -0
- package/dist/credentials/session.js.map +1 -0
- package/dist/credentials/store.d.ts +80 -0
- package/dist/credentials/store.js +94 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/in-flight.d.ts +33 -0
- package/dist/in-flight.js +78 -0
- package/dist/in-flight.js.map +1 -0
- package/dist/mail/actions.d.ts +116 -0
- package/dist/mail/actions.js +265 -0
- package/dist/mail/actions.js.map +1 -0
- package/dist/mail/attachments.d.ts +39 -0
- package/dist/mail/attachments.js +86 -0
- package/dist/mail/attachments.js.map +1 -0
- package/dist/mail/compose.d.ts +264 -0
- package/dist/mail/compose.js +395 -0
- package/dist/mail/compose.js.map +1 -0
- package/dist/mail/drafts.d.ts +138 -0
- package/dist/mail/drafts.js +449 -0
- package/dist/mail/drafts.js.map +1 -0
- package/dist/mail/ids.d.ts +78 -0
- package/dist/mail/ids.js +135 -0
- package/dist/mail/ids.js.map +1 -0
- package/dist/mail/labels.d.ts +61 -0
- package/dist/mail/labels.js +139 -0
- package/dist/mail/labels.js.map +1 -0
- package/dist/mail/markup.d.ts +91 -0
- package/dist/mail/markup.js +265 -0
- package/dist/mail/markup.js.map +1 -0
- package/dist/mail/messages.d.ts +147 -0
- package/dist/mail/messages.js +219 -0
- package/dist/mail/messages.js.map +1 -0
- package/dist/mail/quote.d.ts +65 -0
- package/dist/mail/quote.js +157 -0
- package/dist/mail/quote.js.map +1 -0
- package/dist/mail/search.d.ts +66 -0
- package/dist/mail/search.js +107 -0
- package/dist/mail/search.js.map +1 -0
- package/dist/mail/send.d.ts +61 -0
- package/dist/mail/send.js +132 -0
- package/dist/mail/send.js.map +1 -0
- package/dist/mime/parse.d.ts +110 -0
- package/dist/mime/parse.js +194 -0
- package/dist/mime/parse.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +387 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +86 -0
- package/dist/session.js +220 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +52 -0
- package/dist/settings.js +80 -0
- package/dist/settings.js.map +1 -0
- package/dist/tools/actions.d.ts +21 -0
- package/dist/tools/actions.js +133 -0
- package/dist/tools/actions.js.map +1 -0
- package/dist/tools/capabilities.d.ts +52 -0
- package/dist/tools/capabilities.js +96 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/confirm.d.ts +84 -0
- package/dist/tools/confirm.js +212 -0
- package/dist/tools/confirm.js.map +1 -0
- package/dist/tools/drafts.d.ts +21 -0
- package/dist/tools/drafts.js +199 -0
- package/dist/tools/drafts.js.map +1 -0
- package/dist/tools/failures.d.ts +46 -0
- package/dist/tools/failures.js +157 -0
- package/dist/tools/failures.js.map +1 -0
- package/dist/tools/format.d.ts +55 -0
- package/dist/tools/format.js +220 -0
- package/dist/tools/format.js.map +1 -0
- package/dist/tools/guidance.d.ts +38 -0
- package/dist/tools/guidance.js +226 -0
- package/dist/tools/guidance.js.map +1 -0
- package/dist/tools/interface.d.ts +28 -0
- package/dist/tools/interface.js +63 -0
- package/dist/tools/interface.js.map +1 -0
- package/dist/tools/labels.d.ts +21 -0
- package/dist/tools/labels.js +92 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/list-folders.d.ts +14 -0
- package/dist/tools/list-folders.js +70 -0
- package/dist/tools/list-folders.js.map +1 -0
- package/dist/tools/messages.d.ts +9 -0
- package/dist/tools/messages.js +221 -0
- package/dist/tools/messages.js.map +1 -0
- package/dist/tools/send.d.ts +29 -0
- package/dist/tools/send.js +236 -0
- package/dist/tools/send.js.map +1 -0
- package/dist/tools/sign-in-required.d.ts +94 -0
- package/dist/tools/sign-in-required.js +168 -0
- package/dist/tools/sign-in-required.js.map +1 -0
- package/dist/web/layout.d.ts +75 -0
- package/dist/web/layout.js +590 -0
- package/dist/web/layout.js.map +1 -0
- package/dist/web/pages.d.ts +79 -0
- package/dist/web/pages.js +284 -0
- package/dist/web/pages.js.map +1 -0
- package/dist/web/sections.d.ts +105 -0
- package/dist/web/sections.js +207 -0
- package/dist/web/sections.js.map +1 -0
- package/dist/web/security.d.ts +122 -0
- package/dist/web/security.js +180 -0
- package/dist/web/security.js.map +1 -0
- package/dist/web/server.d.ts +153 -0
- package/dist/web/server.js +622 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials in the operating system's keyring.
|
|
3
|
+
*
|
|
4
|
+
* @napi-rs/keyring is an optional dependency. On a platform without a prebuilt
|
|
5
|
+
* binary the install does not fail, this store simply becomes unavailable and
|
|
6
|
+
* the others remain. So the import has to be dynamic and its failure has to be
|
|
7
|
+
* an ordinary outcome rather than a crash.
|
|
8
|
+
*
|
|
9
|
+
* Every call here can block: a locked keyring opens a dialog, and the underlying
|
|
10
|
+
* calls are synchronous. Callers must treat this store as potentially
|
|
11
|
+
* unresponsive, see probeKeyring in availability.ts.
|
|
12
|
+
*/
|
|
13
|
+
import type { CredentialStore, Credentials } from './store.js';
|
|
14
|
+
/** Identifies our entries in the keyring. */
|
|
15
|
+
export declare const SERVICE_NAME = "proton-mcp";
|
|
16
|
+
/** The account under which the Bridge credentials are stored. */
|
|
17
|
+
export declare const ACCOUNT_NAME = "bridge";
|
|
18
|
+
/** Minimal shape of what @napi-rs/keyring provides. */
|
|
19
|
+
interface KeyringEntry {
|
|
20
|
+
setPassword(password: string): void;
|
|
21
|
+
getPassword(): string | null;
|
|
22
|
+
deletePassword(): boolean;
|
|
23
|
+
}
|
|
24
|
+
interface KeyringModule {
|
|
25
|
+
Entry: new (service: string, account: string) => KeyringEntry;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Loads the keyring module, or reports that it is not there.
|
|
29
|
+
*
|
|
30
|
+
* The result is cached, including the negative one: a missing optional
|
|
31
|
+
* dependency does not appear halfway through a session.
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadKeyringModule(): Promise<KeyringModule | undefined>;
|
|
34
|
+
/** For tests, which must not depend on what is installed here. */
|
|
35
|
+
export declare function _setKeyringModule(module: KeyringModule | undefined): void;
|
|
36
|
+
/** Resets the cache. For tests. */
|
|
37
|
+
export declare function _resetKeyringModule(): void;
|
|
38
|
+
export declare class KeyringStore implements CredentialStore {
|
|
39
|
+
#private;
|
|
40
|
+
private readonly service;
|
|
41
|
+
private readonly account;
|
|
42
|
+
readonly kind: 'keyring';
|
|
43
|
+
constructor(service?: string, account?: string);
|
|
44
|
+
load(): Promise<Credentials | undefined>;
|
|
45
|
+
save(credentials: Credentials): Promise<void>;
|
|
46
|
+
clear(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials in the operating system's keyring.
|
|
3
|
+
*
|
|
4
|
+
* @napi-rs/keyring is an optional dependency. On a platform without a prebuilt
|
|
5
|
+
* binary the install does not fail, this store simply becomes unavailable and
|
|
6
|
+
* the others remain. So the import has to be dynamic and its failure has to be
|
|
7
|
+
* an ordinary outcome rather than a crash.
|
|
8
|
+
*
|
|
9
|
+
* Every call here can block: a locked keyring opens a dialog, and the underlying
|
|
10
|
+
* calls are synchronous. Callers must treat this store as potentially
|
|
11
|
+
* unresponsive, see probeKeyring in availability.ts.
|
|
12
|
+
*/
|
|
13
|
+
import { BridgeError } from '../bridge/errors.js';
|
|
14
|
+
/** Identifies our entries in the keyring. */
|
|
15
|
+
export const SERVICE_NAME = 'proton-mcp';
|
|
16
|
+
/** The account under which the Bridge credentials are stored. */
|
|
17
|
+
export const ACCOUNT_NAME = 'bridge';
|
|
18
|
+
let cached = undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Loads the keyring module, or reports that it is not there.
|
|
21
|
+
*
|
|
22
|
+
* The result is cached, including the negative one: a missing optional
|
|
23
|
+
* dependency does not appear halfway through a session.
|
|
24
|
+
*/
|
|
25
|
+
export async function loadKeyringModule() {
|
|
26
|
+
if (cached !== undefined)
|
|
27
|
+
return cached ?? undefined;
|
|
28
|
+
try {
|
|
29
|
+
const module = (await import('@napi-rs/keyring'));
|
|
30
|
+
cached = module;
|
|
31
|
+
return module;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
cached = null;
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** For tests, which must not depend on what is installed here. */
|
|
39
|
+
export function _setKeyringModule(module) {
|
|
40
|
+
cached = module ?? null;
|
|
41
|
+
}
|
|
42
|
+
/** Resets the cache. For tests. */
|
|
43
|
+
export function _resetKeyringModule() {
|
|
44
|
+
cached = undefined;
|
|
45
|
+
}
|
|
46
|
+
export class KeyringStore {
|
|
47
|
+
service;
|
|
48
|
+
account;
|
|
49
|
+
kind = 'keyring';
|
|
50
|
+
constructor(service = SERVICE_NAME, account = ACCOUNT_NAME) {
|
|
51
|
+
this.service = service;
|
|
52
|
+
this.account = account;
|
|
53
|
+
}
|
|
54
|
+
async #entry() {
|
|
55
|
+
const module = await loadKeyringModule();
|
|
56
|
+
if (!module) {
|
|
57
|
+
throw new BridgeError('The system keyring is not available in this installation. The optional dependency ' +
|
|
58
|
+
'@napi-rs/keyring has no prebuilt binary for this platform. Choose a different place ' +
|
|
59
|
+
'to keep the credentials.');
|
|
60
|
+
}
|
|
61
|
+
return new module.Entry(this.service, this.account);
|
|
62
|
+
}
|
|
63
|
+
async load() {
|
|
64
|
+
const entry = await this.#entry();
|
|
65
|
+
const raw = entry.getPassword();
|
|
66
|
+
if (!raw)
|
|
67
|
+
return undefined;
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(raw);
|
|
70
|
+
if (!parsed.user || !parsed.pass)
|
|
71
|
+
return undefined;
|
|
72
|
+
return { user: parsed.user, pass: parsed.pass };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Someone or something wrote a value we cannot read. Treating it as
|
|
76
|
+
// absent lets the user simply sign in again.
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async save(credentials) {
|
|
81
|
+
const entry = await this.#entry();
|
|
82
|
+
// Both values go into one entry: the keyring stores a single secret per
|
|
83
|
+
// account, and splitting them across two accounts would make a partial
|
|
84
|
+
// state possible.
|
|
85
|
+
entry.setPassword(JSON.stringify({ user: credentials.user, pass: credentials.pass }));
|
|
86
|
+
}
|
|
87
|
+
async clear() {
|
|
88
|
+
const entry = await this.#entry();
|
|
89
|
+
try {
|
|
90
|
+
entry.deletePassword();
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Nothing stored is the outcome we wanted anyway.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=keyring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyring.js","sourceRoot":"","sources":["../../src/credentials/keyring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials in an unencrypted file.
|
|
3
|
+
*
|
|
4
|
+
* A fully supported option, not a leftover. It is the only one that never
|
|
5
|
+
* prompts for anything and can be written by a script, which is what makes it
|
|
6
|
+
* the choice for automated setups. The price is stated plainly: file permissions
|
|
7
|
+
* are all that protect it.
|
|
8
|
+
*
|
|
9
|
+
* This is also the format the server already reads on startup, so an existing
|
|
10
|
+
* setup keeps working.
|
|
11
|
+
*/
|
|
12
|
+
import type { CredentialStore, Credentials } from './store.js';
|
|
13
|
+
export declare class PlainFileStore implements CredentialStore {
|
|
14
|
+
private readonly path;
|
|
15
|
+
/** Where warnings go. stderr, never stdout, which carries the protocol. */
|
|
16
|
+
private readonly notify;
|
|
17
|
+
readonly kind: 'plain-file';
|
|
18
|
+
constructor(path?: string,
|
|
19
|
+
/** Where warnings go. stderr, never stdout, which carries the protocol. */
|
|
20
|
+
notify?: (message: string) => void);
|
|
21
|
+
load(): Promise<Credentials | undefined>;
|
|
22
|
+
save(credentials: Credentials): Promise<void>;
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials in an unencrypted file.
|
|
3
|
+
*
|
|
4
|
+
* A fully supported option, not a leftover. It is the only one that never
|
|
5
|
+
* prompts for anything and can be written by a script, which is what makes it
|
|
6
|
+
* the choice for automated setups. The price is stated plainly: file permissions
|
|
7
|
+
* are all that protect it.
|
|
8
|
+
*
|
|
9
|
+
* This is also the format the server already reads on startup, so an existing
|
|
10
|
+
* setup keeps working.
|
|
11
|
+
*/
|
|
12
|
+
import { readFile, writeFile, unlink, mkdir, stat, chmod } from 'node:fs/promises';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
import { platform } from 'node:os';
|
|
15
|
+
import { credentialsPath, parseEnvFile, permissionWarning } from '../config.js';
|
|
16
|
+
export class PlainFileStore {
|
|
17
|
+
path;
|
|
18
|
+
notify;
|
|
19
|
+
kind = 'plain-file';
|
|
20
|
+
constructor(path = credentialsPath(),
|
|
21
|
+
/** Where warnings go. stderr, never stdout, which carries the protocol. */
|
|
22
|
+
notify = () => undefined) {
|
|
23
|
+
this.path = path;
|
|
24
|
+
this.notify = notify;
|
|
25
|
+
}
|
|
26
|
+
async load() {
|
|
27
|
+
let content;
|
|
28
|
+
try {
|
|
29
|
+
content = await readFile(this.path, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (platform() !== 'win32') {
|
|
35
|
+
try {
|
|
36
|
+
const warning = permissionWarning((await stat(this.path)).mode);
|
|
37
|
+
if (warning)
|
|
38
|
+
this.notify(`${this.path}: ${warning}`);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Failing to stat a file we just read is not worth reporting.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const values = parseEnvFile(content);
|
|
45
|
+
const user = values.BRIDGE_USER?.trim();
|
|
46
|
+
const pass = values.BRIDGE_PASS;
|
|
47
|
+
if (!user || !pass)
|
|
48
|
+
return undefined;
|
|
49
|
+
return { user, pass };
|
|
50
|
+
}
|
|
51
|
+
async save(credentials) {
|
|
52
|
+
await mkdir(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
53
|
+
// Keep the shape a person can read and edit, since being editable by hand is
|
|
54
|
+
// half the point of this option.
|
|
55
|
+
const content = [
|
|
56
|
+
'# Proton Mail Bridge credentials for proton-mcp.',
|
|
57
|
+
'#',
|
|
58
|
+
'# BRIDGE_PASS is the password the Bridge generates, NOT your Proton account',
|
|
59
|
+
'# password. This file is unencrypted: its permissions are what protect it.',
|
|
60
|
+
`BRIDGE_USER=${credentials.user}`,
|
|
61
|
+
`BRIDGE_PASS=${credentials.pass}`,
|
|
62
|
+
'',
|
|
63
|
+
].join('\n');
|
|
64
|
+
await writeFile(this.path, content, { mode: 0o600 });
|
|
65
|
+
// writeFile only applies the mode when it creates the file, so an existing
|
|
66
|
+
// one with loose permissions would silently keep them.
|
|
67
|
+
await chmod(this.path, 0o600);
|
|
68
|
+
}
|
|
69
|
+
async clear() {
|
|
70
|
+
try {
|
|
71
|
+
await unlink(this.path);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Already gone is the outcome we wanted.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=plain-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plain-file.js","sourceRoot":"","sources":["../../src/credentials/plain-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElC,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAE/E,MAAM,OAAO,cAAc;IAIN,IAAI;IAEJ,MAAM;IALhB,IAAI,GAAG,YAAqB,CAAA;IAErC,YACmB,IAAI,GAAW,eAAe,EAAE;IACjD,2EAA2E;IAC1D,MAAM,GAA8B,GAAG,EAAE,CAAC,SAAS;oBAFnD,IAAI;sBAEJ,MAAM;IACtB,CAAC;IAEJ,KAAK,CAAC,IAAI;QACR,IAAI,OAAe,CAAA;QACnB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAA;QAClB,CAAC;QAED,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;gBAC/D,IAAI,OAAO;oBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,CAAA;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,CAAA;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAA;QAC/B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAA;QACpC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAwB;QACjC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QAEjE,6EAA6E;QAC7E,iCAAiC;QACjC,MAAM,OAAO,GAAG;YACd,kDAAkD;YAClD,GAAG;YACH,6EAA6E;YAC7E,4EAA4E;YAC5E,eAAe,WAAW,CAAC,IAAI,EAAE;YACjC,eAAe,WAAW,CAAC,IAAI,EAAE;YACjC,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAEZ,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACpD,2EAA2E;QAC3E,uDAAuD;QACvD,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,yCAAyC;QAC3C,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probes the system keyring, meant to be run as a child process.
|
|
3
|
+
*
|
|
4
|
+
* It lives in its own process for one reason: a locked keyring opens a dialog
|
|
5
|
+
* and the underlying calls are synchronous, so the probe may never return.
|
|
6
|
+
* A hanging process can be killed, a hanging call cannot.
|
|
7
|
+
*
|
|
8
|
+
* The result goes back over IPC. Nothing is written to stdout or stderr, because
|
|
9
|
+
* the parent speaks the MCP protocol over stdout.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probes the system keyring, meant to be run as a child process.
|
|
3
|
+
*
|
|
4
|
+
* It lives in its own process for one reason: a locked keyring opens a dialog
|
|
5
|
+
* and the underlying calls are synchronous, so the probe may never return.
|
|
6
|
+
* A hanging process can be killed, a hanging call cannot.
|
|
7
|
+
*
|
|
8
|
+
* The result goes back over IPC. Nothing is written to stdout or stderr, because
|
|
9
|
+
* the parent speaks the MCP protocol over stdout.
|
|
10
|
+
*/
|
|
11
|
+
import { KeyringStore, loadKeyringModule } from './keyring.js';
|
|
12
|
+
const SERVICE = 'proton-mcp-selftest';
|
|
13
|
+
const ACCOUNT = 'availability-probe';
|
|
14
|
+
function report(result) {
|
|
15
|
+
process.send?.(result);
|
|
16
|
+
// Exit right away. A keyring binding may keep handles open that would
|
|
17
|
+
// otherwise hold the process alive.
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
async function main() {
|
|
21
|
+
const module = await loadKeyringModule();
|
|
22
|
+
if (!module) {
|
|
23
|
+
report({
|
|
24
|
+
ok: false,
|
|
25
|
+
reason: 'the optional dependency @napi-rs/keyring is not installed, or has no prebuilt binary for ' +
|
|
26
|
+
'this platform.',
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// A separate service name, so the probe never touches the real entry.
|
|
31
|
+
const store = new KeyringStore(SERVICE, ACCOUNT);
|
|
32
|
+
const value = { user: 'probe', pass: `probe-${process.pid}` };
|
|
33
|
+
try {
|
|
34
|
+
const t0 = process.hrtime.bigint();
|
|
35
|
+
await store.save(value);
|
|
36
|
+
const writeMs = Number(process.hrtime.bigint() - t0) / 1e6;
|
|
37
|
+
const t1 = process.hrtime.bigint();
|
|
38
|
+
const readBack = await store.load();
|
|
39
|
+
const readMs = Number(process.hrtime.bigint() - t1) / 1e6;
|
|
40
|
+
// Clean up before reporting, so a failure below cannot leave the entry
|
|
41
|
+
// behind.
|
|
42
|
+
await store.clear();
|
|
43
|
+
if (readBack?.pass !== value.pass) {
|
|
44
|
+
report({
|
|
45
|
+
ok: false,
|
|
46
|
+
reason: 'the keyring accepted a value but returned something else when read back.',
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
report({ ok: true, writeMs, readMs });
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
report({ ok: false, reason: error?.message ?? String(error) });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
void main();
|
|
57
|
+
//# sourceMappingURL=probe-keyring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-keyring.js","sourceRoot":"","sources":["../../src/credentials/probe-keyring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAG9D,MAAM,OAAO,GAAG,qBAAqB,CAAA;AACrC,MAAM,OAAO,GAAG,oBAAoB,CAAA;AAEpC,SAAS,MAAM,CAAC,MAAmB;IACjC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAA;IACtB,sEAAsE;IACtE,oCAAoC;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAA;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EACJ,2FAA2F;gBAC3F,gBAAgB;SACnB,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IAED,sEAAsE;IACtE,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAChD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,OAAO,CAAC,GAAG,EAAE,EAAE,CAAA;IAE7D,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;QAClC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAA;QAE1D,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;QAClC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAA;QAEzD,uEAAuE;QACvE,UAAU;QACV,MAAM,KAAK,CAAC,KAAK,EAAE,CAAA;QAEnB,IAAI,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAClC,MAAM,CAAC;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,0EAA0E;aACnF,CAAC,CAAA;YACF,OAAM;QACR,CAAC;QAED,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAG,KAAe,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC3E,CAAC;AACH,CAAC;AAED,KAAK,IAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials held in memory only.
|
|
3
|
+
*
|
|
4
|
+
* The safest option and the least convenient: nothing reaches the disk, and
|
|
5
|
+
* nothing survives the process. For most clients that means signing in again
|
|
6
|
+
* for every conversation, because they start the server anew each time.
|
|
7
|
+
*/
|
|
8
|
+
import type { CredentialStore, Credentials } from './store.js';
|
|
9
|
+
export declare class SessionStore implements CredentialStore {
|
|
10
|
+
#private;
|
|
11
|
+
readonly kind: 'session';
|
|
12
|
+
load(): Promise<Credentials | undefined>;
|
|
13
|
+
save(credentials: Credentials): Promise<void>;
|
|
14
|
+
clear(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials held in memory only.
|
|
3
|
+
*
|
|
4
|
+
* The safest option and the least convenient: nothing reaches the disk, and
|
|
5
|
+
* nothing survives the process. For most clients that means signing in again
|
|
6
|
+
* for every conversation, because they start the server anew each time.
|
|
7
|
+
*/
|
|
8
|
+
export class SessionStore {
|
|
9
|
+
kind = 'session';
|
|
10
|
+
#held;
|
|
11
|
+
async load() {
|
|
12
|
+
return this.#held;
|
|
13
|
+
}
|
|
14
|
+
async save(credentials) {
|
|
15
|
+
this.#held = { user: credentials.user, pass: credentials.pass };
|
|
16
|
+
}
|
|
17
|
+
async clear() {
|
|
18
|
+
this.#held = undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/credentials/session.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,SAAkB,CAAA;IAClC,KAAK,CAAyB;IAE9B,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAwB;QACjC,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,CAAA;IACjE,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the Bridge credentials live.
|
|
3
|
+
*
|
|
4
|
+
* The user chooses, because the trade-off is theirs to make. All four options
|
|
5
|
+
* are fully supported: none of them is a second-class fallback, and none is
|
|
6
|
+
* presented as the obvious answer. What differs is which drawback you accept.
|
|
7
|
+
*
|
|
8
|
+
* Each option therefore states both sides plus who it is actually for, and one
|
|
9
|
+
* factual property that makes them comparable without prose: what ends up on
|
|
10
|
+
* disk.
|
|
11
|
+
*
|
|
12
|
+
* One property shapes the whole module: reading from a system keyring can
|
|
13
|
+
* **block**. A locked KWallet or GNOME Keyring opens a dialog, and the calls in
|
|
14
|
+
* @napi-rs/keyring are synchronous, so there is no way to abandon one from the
|
|
15
|
+
* same thread. Anything that touches a keyring therefore has to assume it may
|
|
16
|
+
* never return, which is why the availability probe runs in a child process.
|
|
17
|
+
*/
|
|
18
|
+
export interface Credentials {
|
|
19
|
+
user: string;
|
|
20
|
+
pass: string;
|
|
21
|
+
}
|
|
22
|
+
/** The ways credentials can be kept. */
|
|
23
|
+
export type StoreKind = 'keyring' | 'session' | 'encrypted-file' | 'plain-file';
|
|
24
|
+
export interface CredentialStore {
|
|
25
|
+
readonly kind: StoreKind;
|
|
26
|
+
/** Returns the credentials, or undefined when none are stored. */
|
|
27
|
+
load(): Promise<Credentials | undefined>;
|
|
28
|
+
save(credentials: Credentials): Promise<void>;
|
|
29
|
+
/** Removes what is stored. Succeeds even when nothing was there. */
|
|
30
|
+
clear(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Whether something is stored, without unlocking it.
|
|
33
|
+
*
|
|
34
|
+
* Only the encrypted file implements this, and it is the reason the method
|
|
35
|
+
* exists: it is the one store that can hold credentials the server cannot
|
|
36
|
+
* read on its own. The interface has to tell "nothing stored" from "stored
|
|
37
|
+
* but locked", because the first calls for the full sign-in form and the
|
|
38
|
+
* second only for a master password.
|
|
39
|
+
*/
|
|
40
|
+
exists?(): Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What lands on disk. The one property that makes the options comparable
|
|
44
|
+
* without reading three paragraphs.
|
|
45
|
+
*/
|
|
46
|
+
export type DiskExposure =
|
|
47
|
+
/** Nothing at all. */
|
|
48
|
+
'none'
|
|
49
|
+
/** Encrypted with a key that is not stored anywhere. */
|
|
50
|
+
| 'encrypted'
|
|
51
|
+
/** Protected by the operating system, readable by your account. */
|
|
52
|
+
| 'os-protected'
|
|
53
|
+
/** Readable by anything that can read your files. */
|
|
54
|
+
| 'plaintext';
|
|
55
|
+
/** What the user is told about an option before choosing it. */
|
|
56
|
+
export interface StoreDescription {
|
|
57
|
+
kind: StoreKind;
|
|
58
|
+
title: string;
|
|
59
|
+
/** One sentence on what it does. */
|
|
60
|
+
summary: string;
|
|
61
|
+
/** What it is good at. */
|
|
62
|
+
benefit: string;
|
|
63
|
+
/** What it costs. Every option has something. */
|
|
64
|
+
cost: string;
|
|
65
|
+
/** Who should pick this one. */
|
|
66
|
+
bestFor: string;
|
|
67
|
+
exposure: DiskExposure;
|
|
68
|
+
/** How often you have to type something to use the server. */
|
|
69
|
+
prompts: 'never' | 'once per server start' | 'occasionally';
|
|
70
|
+
}
|
|
71
|
+
export declare const EXPOSURE_LABELS: Record<DiskExposure, string>;
|
|
72
|
+
export declare const STORE_DESCRIPTIONS: Record<StoreKind, StoreDescription>;
|
|
73
|
+
/**
|
|
74
|
+
* The order options are presented in: least exposed first.
|
|
75
|
+
*
|
|
76
|
+
* Not a ranking of quality. It puts the security-relevant difference in front,
|
|
77
|
+
* so that choosing convenience is a conscious step down the list rather than
|
|
78
|
+
* something that happens by accident.
|
|
79
|
+
*/
|
|
80
|
+
export declare const PRESENTATION_ORDER: StoreKind[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the Bridge credentials live.
|
|
3
|
+
*
|
|
4
|
+
* The user chooses, because the trade-off is theirs to make. All four options
|
|
5
|
+
* are fully supported: none of them is a second-class fallback, and none is
|
|
6
|
+
* presented as the obvious answer. What differs is which drawback you accept.
|
|
7
|
+
*
|
|
8
|
+
* Each option therefore states both sides plus who it is actually for, and one
|
|
9
|
+
* factual property that makes them comparable without prose: what ends up on
|
|
10
|
+
* disk.
|
|
11
|
+
*
|
|
12
|
+
* One property shapes the whole module: reading from a system keyring can
|
|
13
|
+
* **block**. A locked KWallet or GNOME Keyring opens a dialog, and the calls in
|
|
14
|
+
* @napi-rs/keyring are synchronous, so there is no way to abandon one from the
|
|
15
|
+
* same thread. Anything that touches a keyring therefore has to assume it may
|
|
16
|
+
* never return, which is why the availability probe runs in a child process.
|
|
17
|
+
*/
|
|
18
|
+
export const EXPOSURE_LABELS = {
|
|
19
|
+
none: 'nothing on disk',
|
|
20
|
+
encrypted: 'encrypted on disk',
|
|
21
|
+
'os-protected': 'on disk, protected by the operating system',
|
|
22
|
+
plaintext: 'unencrypted on disk',
|
|
23
|
+
};
|
|
24
|
+
export const STORE_DESCRIPTIONS = {
|
|
25
|
+
keyring: {
|
|
26
|
+
kind: 'keyring',
|
|
27
|
+
title: 'System keyring',
|
|
28
|
+
summary: 'The password goes where the operating system keeps your other passwords: Credential Manager ' +
|
|
29
|
+
'on Windows, Keychain on macOS, GNOME Keyring or KWallet on Linux.',
|
|
30
|
+
benefit: 'Nothing to type after the first sign-in, and the password is protected by the same mechanism ' +
|
|
31
|
+
'as the rest of your logins. It is also the only option where the secret is not simply a file ' +
|
|
32
|
+
'you could accidentally copy into a backup or a shared folder.',
|
|
33
|
+
cost: 'Needs a running keyring service, which a container, a plain SSH session or WSL usually lacks. ' +
|
|
34
|
+
'On macOS the permission is tied to the code hash of the node binary, so after a Node update ' +
|
|
35
|
+
'the system asks for your login password again. Reading it can also block if the keyring is ' +
|
|
36
|
+
'locked.',
|
|
37
|
+
bestFor: 'A desktop machine you use yourself. This is the ordinary case.',
|
|
38
|
+
exposure: 'os-protected',
|
|
39
|
+
prompts: 'occasionally',
|
|
40
|
+
},
|
|
41
|
+
'encrypted-file': {
|
|
42
|
+
kind: 'encrypted-file',
|
|
43
|
+
title: 'Encrypted file',
|
|
44
|
+
summary: 'The password is encrypted with a master password of your choosing and written into your user ' +
|
|
45
|
+
'directory.',
|
|
46
|
+
benefit: 'Works everywhere, with no system service and no graphical session, so it is the one option ' +
|
|
47
|
+
'that behaves identically on every platform. Even if someone copies the file, it is useless ' +
|
|
48
|
+
'without your master password.',
|
|
49
|
+
cost: 'You type the master password every time the server starts. Forget it and the stored password ' +
|
|
50
|
+
'is gone for good, since nothing anywhere can decrypt it. And a master password you can ' +
|
|
51
|
+
'remember is usually weaker than one the operating system would have generated.',
|
|
52
|
+
bestFor: 'Servers, containers, headless machines, and anyone who would rather type something than ' +
|
|
53
|
+
'depend on a system service.',
|
|
54
|
+
exposure: 'encrypted',
|
|
55
|
+
prompts: 'once per server start',
|
|
56
|
+
},
|
|
57
|
+
session: {
|
|
58
|
+
kind: 'session',
|
|
59
|
+
title: 'This session only',
|
|
60
|
+
summary: 'Nothing is stored. The password is held in memory and forgotten when the server stops.',
|
|
61
|
+
benefit: 'Nothing to find, on disk or elsewhere. A stolen laptop, a backup or a misplaced file cannot ' +
|
|
62
|
+
'give the password away, because there is nothing to give away.',
|
|
63
|
+
cost: 'You sign in again whenever the server starts, which for most clients means every new ' +
|
|
64
|
+
'conversation. In practice this is the option people abandon after a few days.',
|
|
65
|
+
bestFor: 'A shared or untrusted machine, a one-off look at a mailbox, or anyone who genuinely wants ' +
|
|
66
|
+
'nothing left behind.',
|
|
67
|
+
exposure: 'none',
|
|
68
|
+
prompts: 'once per server start',
|
|
69
|
+
},
|
|
70
|
+
'plain-file': {
|
|
71
|
+
kind: 'plain-file',
|
|
72
|
+
title: 'Plain file',
|
|
73
|
+
summary: 'The password sits unencrypted in a file in your user directory, readable only by your ' +
|
|
74
|
+
'account.',
|
|
75
|
+
benefit: 'The simplest thing that works. No service, no master password, no prompts ever, and you can ' +
|
|
76
|
+
'read and edit it with any text editor, which makes it the easiest to script and to debug.',
|
|
77
|
+
cost: 'Anything that can read your files can read the password: a backup tool, a synchronised ' +
|
|
78
|
+
'folder, another program running as you, or anyone who gets hold of the disk. File ' +
|
|
79
|
+
'permissions are the only thing protecting it.',
|
|
80
|
+
bestFor: 'Automation and scripted setups where no one can type a password, on a machine you control ' +
|
|
81
|
+
'and whose backups you trust.',
|
|
82
|
+
exposure: 'plaintext',
|
|
83
|
+
prompts: 'never',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* The order options are presented in: least exposed first.
|
|
88
|
+
*
|
|
89
|
+
* Not a ranking of quality. It puts the security-relevant difference in front,
|
|
90
|
+
* so that choosing convenience is a conscious step down the list rather than
|
|
91
|
+
* something that happens by accident.
|
|
92
|
+
*/
|
|
93
|
+
export const PRESENTATION_ORDER = ['session', 'keyring', 'encrypted-file', 'plain-file'];
|
|
94
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/credentials/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA4DH,MAAM,CAAC,MAAM,eAAe,GAAiC;IAC3D,IAAI,EAAE,iBAAiB;IACvB,SAAS,EAAE,mBAAmB;IAC9B,cAAc,EAAE,4CAA4C;IAC5D,SAAS,EAAE,qBAAqB;CACjC,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAwC;IACrE,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,gBAAgB;QACvB,OAAO,EACL,8FAA8F;YAC9F,mEAAmE;QACrE,OAAO,EACL,+FAA+F;YAC/F,+FAA+F;YAC/F,+DAA+D;QACjE,IAAI,EACF,gGAAgG;YAChG,8FAA8F;YAC9F,6FAA6F;YAC7F,SAAS;QACX,OAAO,EAAE,gEAAgE;QACzE,QAAQ,EAAE,cAAc;QACxB,OAAO,EAAE,cAAc;KACxB;IACD,gBAAgB,EAAE;QAChB,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,gBAAgB;QACvB,OAAO,EACL,+FAA+F;YAC/F,YAAY;QACd,OAAO,EACL,6FAA6F;YAC7F,6FAA6F;YAC7F,+BAA+B;QACjC,IAAI,EACF,+FAA+F;YAC/F,yFAAyF;YACzF,gFAAgF;QAClF,OAAO,EACL,0FAA0F;YAC1F,6BAA6B;QAC/B,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,uBAAuB;KACjC;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,mBAAmB;QAC1B,OAAO,EAAE,wFAAwF;QACjG,OAAO,EACL,8FAA8F;YAC9F,gEAAgE;QAClE,IAAI,EACF,uFAAuF;YACvF,+EAA+E;QACjF,OAAO,EACL,4FAA4F;YAC5F,sBAAsB;QACxB,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,uBAAuB;KACjC;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,YAAY;QACnB,OAAO,EACL,wFAAwF;YACxF,UAAU;QACZ,OAAO,EACL,8FAA8F;YAC9F,2FAA2F;QAC7F,IAAI,EACF,yFAAyF;YACzF,oFAAoF;YACpF,+CAA+C;QACjD,OAAO,EACL,4FAA4F;YAC5F,8BAA8B;QAChC,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,OAAO;KACjB;CACF,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,CAAC,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bookkeeping for tool calls that are still running.
|
|
3
|
+
*
|
|
4
|
+
* Background: a stdio server learns that its client is gone by its input
|
|
5
|
+
* stream ending. If it exits right away it cuts off calls that are still in
|
|
6
|
+
* progress and their answer is lost. If it waits forever it hangs whenever a
|
|
7
|
+
* call never returns.
|
|
8
|
+
*
|
|
9
|
+
* Hence: keep count, and wait for a bounded grace period on shutdown.
|
|
10
|
+
*/
|
|
11
|
+
/** How many calls are currently running. For tests and diagnostics only. */
|
|
12
|
+
export declare function inFlightCount(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Wraps a tool call so that shutdown waits for it.
|
|
15
|
+
*
|
|
16
|
+
* The counter is also released on failure. Otherwise a single failed call
|
|
17
|
+
* would keep the server from ever shutting down.
|
|
18
|
+
*
|
|
19
|
+
* Also the one place every tool call passes through, so it is where the status
|
|
20
|
+
* page gets its record of what is running and what happened. Only the tool name
|
|
21
|
+
* is passed on, never the arguments: see activity.ts for why.
|
|
22
|
+
*/
|
|
23
|
+
export declare function track<T>(fn: () => Promise<T>, tool?: string): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Waits until no call is running any more, but at most `maxMs`.
|
|
26
|
+
*
|
|
27
|
+
* Returns true when things really went quiet and false when the deadline
|
|
28
|
+
* passed. The return value lets the caller record that it gave up instead of
|
|
29
|
+
* hiding it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function waitForIdle(maxMs?: number): Promise<boolean>;
|
|
32
|
+
/** Resets the state. For tests only. */
|
|
33
|
+
export declare function _reset(): void;
|