synomem 0.8.0 → 0.9.1
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/CHANGELOG.md +50 -0
- package/README.md +47 -68
- package/dist/backend.d.ts +18 -6
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +55 -41
- package/dist/backend.js.map +1 -1
- package/dist/cli.d.ts +20 -33
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1391 -1321
- package/dist/cli.js.map +1 -1
- package/dist/configure.d.ts +12 -46
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +51 -192
- package/dist/configure.js.map +1 -1
- package/dist/credentials.d.ts +82 -33
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +270 -44
- package/dist/credentials.js.map +1 -1
- package/dist/discover.d.ts +8 -35
- package/dist/discover.d.ts.map +1 -1
- package/dist/discover.js +42 -38
- package/dist/discover.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/import.d.ts +3 -0
- package/dist/import.d.ts.map +1 -1
- package/dist/import.js +3 -0
- package/dist/import.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +18 -18
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +402 -231
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp-server.d.ts +5 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +27 -105
- package/dist/mcp-server.js.map +1 -1
- package/dist/oauth.d.ts +32 -33
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +196 -126
- package/dist/oauth.js.map +1 -1
- package/dist/profiles.d.ts +243 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +479 -0
- package/dist/profiles.js.map +1 -0
- package/dist/project.d.ts +8 -39
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +36 -94
- package/dist/project.js.map +1 -1
- package/dist/remote.d.ts +24 -17
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +54 -52
- package/dist/remote.js.map +1 -1
- package/dist/resolvers.d.ts +54 -0
- package/dist/resolvers.d.ts.map +1 -0
- package/dist/resolvers.js +336 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/service.d.ts +3 -2
- package/dist/service.d.ts.map +1 -1
- package/dist/skill-install.d.ts +4 -6
- package/dist/skill-install.d.ts.map +1 -1
- package/dist/skill-install.js +13 -12
- package/dist/skill-install.js.map +1 -1
- package/dist/types.d.ts +45 -15
- package/dist/types.d.ts.map +1 -1
- package/docs/cli.md +173 -196
- package/docs/mcp.md +69 -65
- package/package.json +1 -1
- package/skills/synomem/SKILL.md +29 -26
- package/skills/synomem/references/examples.md +13 -11
- package/src/backend.ts +66 -64
- package/src/cli.ts +2123 -2219
- package/src/configure.ts +62 -241
- package/src/credentials.ts +316 -85
- package/src/discover.ts +53 -59
- package/src/errors.ts +4 -0
- package/src/import.ts +5 -0
- package/src/index.ts +15 -19
- package/src/mcp/index.ts +473 -277
- package/src/mcp-server.ts +32 -114
- package/src/oauth.ts +249 -131
- package/src/profiles.ts +661 -0
- package/src/project.ts +42 -108
- package/src/remote.ts +69 -63
- package/src/resolvers.ts +385 -0
- package/src/service.ts +2 -7
- package/src/skill-install.ts +17 -18
- package/src/types.ts +40 -15
package/dist/configure.js
CHANGED
|
@@ -1,220 +1,79 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Setup helpers shared by `connection` and `setup`.
|
|
3
3
|
*
|
|
4
4
|
* Two rules shape this file. Every interactive step has a non-interactive
|
|
5
|
-
* counterpart, so an agent can configure a machine without a terminal. And
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* counterpart, so an agent can configure a machine without a terminal. And a
|
|
6
|
+
* secret is never accepted as a command-line argument: an argument is kept by
|
|
7
|
+
* the shell history and visible in the process list, so access keys arrive on
|
|
8
|
+
* stdin.
|
|
9
9
|
*/
|
|
10
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
import { cloudApiUrl } from './cloud.js';
|
|
13
10
|
import { SynomemError } from './errors.js';
|
|
14
|
-
import {
|
|
15
|
-
import { ask, askSecret, confirm, select } from './prompt.js';
|
|
11
|
+
import { askSecret } from './prompt.js';
|
|
16
12
|
/**
|
|
17
|
-
* Where a credential can
|
|
13
|
+
* Where a credential can be kept on this platform, recommended first.
|
|
18
14
|
*
|
|
19
|
-
* Reported rather than assumed: offering macOS Keychain on Linux, or a
|
|
20
|
-
* Service that is not running, produces a setup that appears to succeed
|
|
21
|
-
* then cannot read its own credential back.
|
|
15
|
+
* Reported rather than assumed: offering the macOS Keychain on Linux, or a
|
|
16
|
+
* Secret Service that is not running, produces a setup that appears to succeed
|
|
17
|
+
* and then cannot read its own credential back.
|
|
22
18
|
*/
|
|
23
19
|
export function credentialStoreChoices(platform = process.platform) {
|
|
24
20
|
const native = platform === 'darwin'
|
|
25
|
-
? { value: 'keychain', label: 'macOS Keychain', detail: 'Recommended.' }
|
|
26
|
-
: platform === '
|
|
27
|
-
?
|
|
28
|
-
* Windows has no native option here yet, so the restricted file is
|
|
29
|
-
* the recommendation rather than Credential Manager. Offering a
|
|
30
|
-
* store the credential layer cannot actually read back would fail
|
|
31
|
-
* at the first use, after the wizard had already told the person
|
|
32
|
-
* their credential was safely stored.
|
|
33
|
-
*/
|
|
21
|
+
? [{ value: 'keychain', label: 'macOS Keychain', detail: 'Recommended.' }]
|
|
22
|
+
: platform === 'linux'
|
|
23
|
+
? [
|
|
34
24
|
{
|
|
35
|
-
value: '
|
|
36
|
-
label: '
|
|
37
|
-
|
|
38
|
-
// the read-only bit, so the protection is the user profile
|
|
39
|
-
// directory's access control, not a mode of 0600.
|
|
40
|
-
detail: 'Protected by your user profile. Recommended until Credential Manager lands.',
|
|
41
|
-
}
|
|
42
|
-
: {
|
|
43
|
-
value: 'keychain',
|
|
44
|
-
label: 'Secret Service (libsecret)',
|
|
45
|
-
detail: 'Recommended where a desktop keyring is running.',
|
|
46
|
-
};
|
|
47
|
-
return [
|
|
48
|
-
native,
|
|
49
|
-
...(native.value === 'file'
|
|
50
|
-
? []
|
|
51
|
-
: [
|
|
52
|
-
{
|
|
53
|
-
value: 'file',
|
|
54
|
-
label: 'A restricted file in the Synomem home',
|
|
55
|
-
detail: 'Mode 0600. Use on headless machines with no keyring.',
|
|
25
|
+
value: 'keychain',
|
|
26
|
+
label: 'Secret Service (libsecret)',
|
|
27
|
+
detail: 'Recommended where a desktop keyring is running.',
|
|
56
28
|
},
|
|
57
|
-
]
|
|
29
|
+
]
|
|
30
|
+
: [];
|
|
31
|
+
return [
|
|
32
|
+
...native,
|
|
33
|
+
{
|
|
34
|
+
value: 'file',
|
|
35
|
+
label: 'A restricted file in the Synomem home',
|
|
36
|
+
detail: 'Mode 0600. Use on headless machines with no keyring.',
|
|
37
|
+
},
|
|
58
38
|
{
|
|
59
39
|
value: 'environment',
|
|
60
|
-
label: '
|
|
61
|
-
detail: 'Nothing is stored
|
|
40
|
+
label: 'SYNOMEM_ACCESS_TOKEN (access keys only)',
|
|
41
|
+
detail: 'Nothing is stored; the key is read from the environment each run.',
|
|
62
42
|
},
|
|
63
43
|
];
|
|
64
44
|
}
|
|
45
|
+
/** The default store: the OS keychain where one exists, otherwise refused. */
|
|
46
|
+
export function defaultCredentialBackend(platform = process.platform) {
|
|
47
|
+
if (platform === 'darwin' || platform === 'linux')
|
|
48
|
+
return 'keychain';
|
|
49
|
+
throw new SynomemError('CONFIG_INVALID', 'This platform has no supported credential store. Pass --store file (a mode-0600 file), or --store environment for an access key.');
|
|
50
|
+
}
|
|
51
|
+
export function parseCredentialBackend(value, platform = process.platform) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return defaultCredentialBackend(platform);
|
|
54
|
+
if (value === 'keychain' || value === 'file' || value === 'environment')
|
|
55
|
+
return value;
|
|
56
|
+
throw new SynomemError('INVALID_INPUT', '--store must be keychain, file, or environment.');
|
|
57
|
+
}
|
|
65
58
|
/** Refuses to guess when there is nobody to ask. */
|
|
66
|
-
export function assertInteractive(io) {
|
|
59
|
+
export function assertInteractive(io, alternative) {
|
|
67
60
|
if (io.interactive)
|
|
68
61
|
return;
|
|
69
|
-
throw new SynomemError('INVALID_INPUT',
|
|
70
|
-
'synomem config needs an interactive terminal.',
|
|
71
|
-
'',
|
|
72
|
-
'For automation, use the deterministic form instead:',
|
|
73
|
-
'',
|
|
74
|
-
' synomem config init --backend local --yes',
|
|
75
|
-
'',
|
|
76
|
-
' synomem config init --backend remote --auth access-key \\',
|
|
77
|
-
' --workspace <workspace-id> --access-token-stdin --yes',
|
|
78
|
-
'',
|
|
79
|
-
'Pipe the token in rather than passing it as an argument: an argument is',
|
|
80
|
-
'kept by both the shell history and the process list.',
|
|
81
|
-
].join('\n'));
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Stores an access key in a restricted file.
|
|
85
|
-
*
|
|
86
|
-
* Separate from `config.json` so a configuration file can be read, copied or
|
|
87
|
-
* pasted into an issue without carrying a secret with it.
|
|
88
|
-
*/
|
|
89
|
-
function credentialFilePath(home) {
|
|
90
|
-
return join(home, 'credentials', 'installation.json');
|
|
91
|
-
}
|
|
92
|
-
export function writeCredentialFile(home, token) {
|
|
93
|
-
const directory = join(home, 'credentials');
|
|
94
|
-
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
95
|
-
chmodSync(directory, 0o700);
|
|
96
|
-
const path = credentialFilePath(home);
|
|
97
|
-
writeFileSync(path, `${JSON.stringify({ accessToken: token }, null, 2)}\n`, { mode: 0o600 });
|
|
98
|
-
chmodSync(path, 0o600);
|
|
99
|
-
return path;
|
|
62
|
+
throw new SynomemError('INVALID_INPUT', `This step needs an interactive terminal. For automation: ${alternative}`);
|
|
100
63
|
}
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*/
|
|
109
|
-
export function readCredentialFile(home) {
|
|
110
|
-
const path = credentialFilePath(home);
|
|
111
|
-
if (!existsSync(path))
|
|
112
|
-
return undefined;
|
|
113
|
-
try {
|
|
114
|
-
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
115
|
-
return typeof parsed.accessToken === 'string' ? parsed.accessToken : undefined;
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
return undefined;
|
|
64
|
+
/** Reads an access key from stdin (or a prompt), never from an argument. */
|
|
65
|
+
export async function readAccessKey(io) {
|
|
66
|
+
const key = await askSecret(io, 'Access key');
|
|
67
|
+
if (!key)
|
|
68
|
+
throw new SynomemError('INVALID_INPUT', 'No access key was provided on stdin.');
|
|
69
|
+
if (!key.startsWith('syn_')) {
|
|
70
|
+
throw new SynomemError('INVALID_INPUT', 'That does not look like a Synomem access key (syn_…).');
|
|
119
71
|
}
|
|
72
|
+
return key;
|
|
120
73
|
}
|
|
121
74
|
/** A key's identifying prefix. Never the key. */
|
|
122
75
|
export function credentialFingerprint(token) {
|
|
123
76
|
const head = token.slice(0, 12);
|
|
124
|
-
return `${head}${token.length > 12 ? '
|
|
125
|
-
}
|
|
126
|
-
export function environmentInstructions(token) {
|
|
127
|
-
return [
|
|
128
|
-
'Set this value for the current shell:',
|
|
129
|
-
'',
|
|
130
|
-
` export SYNOMEM_ACCESS_TOKEN='${token}'`,
|
|
131
|
-
'',
|
|
132
|
-
'To persist it, add that to a secret-aware shell configuration or to your',
|
|
133
|
-
'agent runtime environment. Synomem will not edit your shell profile for',
|
|
134
|
-
'you: silently rewriting a dotfile is not a thing a setup program should do.',
|
|
135
|
-
].join('\n');
|
|
136
|
-
}
|
|
137
|
-
/** The interactive flow, returning the plan it settled on. */
|
|
138
|
-
export async function runConfigWizard(io, options = {}) {
|
|
139
|
-
assertInteractive(io);
|
|
140
|
-
const env = options.env ?? process.env;
|
|
141
|
-
io.output.write([
|
|
142
|
-
'',
|
|
143
|
-
'Welcome to Synomem',
|
|
144
|
-
'',
|
|
145
|
-
'Synomem gives agents durable notes, messages, tasks, todos, kudos,',
|
|
146
|
-
'and shared coordination.',
|
|
147
|
-
'',
|
|
148
|
-
].join('\n'));
|
|
149
|
-
const backend = await select(io, 'Where should Synomem store canonical state?', [
|
|
150
|
-
{
|
|
151
|
-
value: 'local',
|
|
152
|
-
label: 'Local \u2014 SQLite on this machine',
|
|
153
|
-
detail: 'Nothing is uploaded. One implicit workspace.',
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
value: 'remote',
|
|
157
|
-
label: 'Synomem Cloud \u2014 shared across machines and agents',
|
|
158
|
-
detail: 'Organizations, workspaces, roles and administration.',
|
|
159
|
-
},
|
|
160
|
-
]);
|
|
161
|
-
const home = await ask(io, 'Where should Synomem store its data?', resolveHome(options.home));
|
|
162
|
-
if (backend === 'local') {
|
|
163
|
-
return { backend, home };
|
|
164
|
-
}
|
|
165
|
-
const serviceUrl = cloudApiUrl(env);
|
|
166
|
-
io.output.write(`\nConnecting to Synomem Cloud at ${serviceUrl}\n`);
|
|
167
|
-
const auth = await select(io, 'How would you like to sign in?', [
|
|
168
|
-
{
|
|
169
|
-
value: 'browser',
|
|
170
|
-
label: 'Sign in with your browser',
|
|
171
|
-
detail: 'Opens the authorization server and returns through a loopback callback.',
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
value: 'access-key',
|
|
175
|
-
label: 'Use an access key',
|
|
176
|
-
detail: 'Create one at https://portal.synomem.ai/access-keys/new',
|
|
177
|
-
},
|
|
178
|
-
]);
|
|
179
|
-
/*
|
|
180
|
-
* Deliberately no workspace prompt here.
|
|
181
|
-
*
|
|
182
|
-
* A hosted workspace ID looks like `ws-04psqx2rkt8ttft7a1t2z69r97`, and the
|
|
183
|
-
* credential authorized in the next step already knows the answer -- an
|
|
184
|
-
* access key can list every workspace its organization has, and a browser
|
|
185
|
-
* sign-in can list the ones the account belongs to across every
|
|
186
|
-
* organization. Asking first means asking a person to go and look something
|
|
187
|
-
* up that we are about to be told.
|
|
188
|
-
*/
|
|
189
|
-
const credentialStore = auth === 'access-key'
|
|
190
|
-
? await select(io, 'Where should Synomem store this credential?', credentialStoreChoices())
|
|
191
|
-
: 'auto';
|
|
192
|
-
return { backend, home, serviceUrl, auth, credentialStore };
|
|
193
|
-
}
|
|
194
|
-
/** Reads an access key without ever accepting it as an argument. */
|
|
195
|
-
export async function readAccessToken(io) {
|
|
196
|
-
const token = await askSecret(io, 'Access key');
|
|
197
|
-
if (!token)
|
|
198
|
-
throw new SynomemError('INVALID_INPUT', 'No access key was provided.');
|
|
199
|
-
return token;
|
|
200
|
-
}
|
|
201
|
-
export async function confirmPlan(io, plan) {
|
|
202
|
-
io.output.write([
|
|
203
|
-
'',
|
|
204
|
-
'Synomem will be configured as:',
|
|
205
|
-
'',
|
|
206
|
-
` Backend: ${plan.backend === 'local' ? 'Local SQLite' : 'Synomem Cloud'}`,
|
|
207
|
-
` Home: ${plan.home}`,
|
|
208
|
-
...(plan.serviceUrl ? [` Service: ${plan.serviceUrl}`] : []),
|
|
209
|
-
...(plan.workspaceId ? [` Workspace: ${plan.workspaceId}`] : []),
|
|
210
|
-
...(plan.credentialStore && plan.credentialStore !== 'auto'
|
|
211
|
-
? [` Credential: ${plan.credentialStore}`]
|
|
212
|
-
: []),
|
|
213
|
-
'',
|
|
214
|
-
].join('\n'));
|
|
215
|
-
return await confirm(io, 'Apply this?');
|
|
216
|
-
}
|
|
217
|
-
export function homeExists(home) {
|
|
218
|
-
return existsSync(home);
|
|
77
|
+
return `${head}${token.length > 12 ? '…' : ''}`;
|
|
219
78
|
}
|
|
220
79
|
//# sourceMappingURL=configure.js.map
|
package/dist/configure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AAGvD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAA4B,OAAO,CAAC,QAAQ;IAE5C,MAAM,MAAM,GACV,QAAQ,KAAK,QAAQ;QACnB,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAmB,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACnF,CAAC,CAAC,QAAQ,KAAK,OAAO;YACpB,CAAC,CAAC;gBACE;oBACE,KAAK,EAAE,UAAmB;oBAC1B,KAAK,EAAE,4BAA4B;oBACnC,MAAM,EAAE,iDAAiD;iBAC1D;aACF;YACH,CAAC,CAAC,EAAE,CAAC;IACX,OAAO;QACL,GAAG,MAAM;QACT;YACE,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,uCAAuC;YAC9C,MAAM,EAAE,sDAAsD;SAC/D;QACD;YACE,KAAK,EAAE,aAAa;YACpB,KAAK,EAAE,yCAAyC;YAChD,MAAM,EAAE,mEAAmE;SAC5E;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,wBAAwB,CACtC,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,UAAU,CAAC;IACrE,MAAM,IAAI,YAAY,CACpB,gBAAgB,EAChB,kIAAkI,CACnI,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAyB,EACzB,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACnE,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,aAAa;QAAE,OAAO,KAAK,CAAC;IACtF,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,iDAAiD,CAAC,CAAC;AAC7F,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,iBAAiB,CAAC,EAAY,EAAE,WAAmB;IACjE,IAAI,EAAE,CAAC,WAAW;QAAE,OAAO;IAC3B,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,4DAA4D,WAAW,EAAE,CAC1E,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAAY;IAC9C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,sCAAsC,CAAC,CAAC;IAC1F,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,uDAAuD,CACxD,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChC,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAClD,CAAC"}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -1,45 +1,55 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const storedOAuthCredentialSchema: z.ZodObject<{
|
|
3
|
+
kind: z.ZodLiteral<"oauth">;
|
|
4
|
+
issuer: z.ZodString;
|
|
5
|
+
resource: z.ZodString;
|
|
6
|
+
clientId: z.ZodString;
|
|
7
|
+
tokenEndpoint: z.ZodString;
|
|
8
|
+
scope: z.ZodString;
|
|
9
|
+
accessToken: z.ZodString;
|
|
10
|
+
refreshToken: z.ZodOptional<z.ZodString>;
|
|
11
|
+
expiresAt: z.ZodNumber;
|
|
12
|
+
generation: z.ZodNumber;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export declare const storedAccessKeySchema: z.ZodObject<{
|
|
15
|
+
kind: z.ZodLiteral<"access-key">;
|
|
16
|
+
secret: z.ZodString;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export declare const storedCredentialSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
19
|
+
kind: z.ZodLiteral<"oauth">;
|
|
20
|
+
issuer: z.ZodString;
|
|
21
|
+
resource: z.ZodString;
|
|
22
|
+
clientId: z.ZodString;
|
|
23
|
+
tokenEndpoint: z.ZodString;
|
|
24
|
+
scope: z.ZodString;
|
|
25
|
+
accessToken: z.ZodString;
|
|
26
|
+
refreshToken: z.ZodOptional<z.ZodString>;
|
|
27
|
+
expiresAt: z.ZodNumber;
|
|
28
|
+
generation: z.ZodNumber;
|
|
29
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
30
|
+
kind: z.ZodLiteral<"access-key">;
|
|
31
|
+
secret: z.ZodString;
|
|
32
|
+
}, z.core.$strip>], "kind">;
|
|
33
|
+
export type StoredOAuthCredential = z.infer<typeof storedOAuthCredentialSchema>;
|
|
34
|
+
export type StoredAccessKey = z.infer<typeof storedAccessKeySchema>;
|
|
35
|
+
export type StoredCredential = z.infer<typeof storedCredentialSchema>;
|
|
36
|
+
/** Where a connection's secret lives. `environment` stores nothing. */
|
|
37
|
+
export type CredentialBackendKind = 'keychain' | 'file' | 'environment';
|
|
31
38
|
export interface CredentialStore {
|
|
32
39
|
get(reference: string): Promise<StoredCredential | undefined>;
|
|
33
40
|
set(reference: string, credential: StoredCredential): Promise<void>;
|
|
34
41
|
delete(reference: string): Promise<boolean>;
|
|
35
42
|
}
|
|
36
|
-
|
|
43
|
+
/** An opaque, non-secret handle for a stored secret. Never derived from names. */
|
|
44
|
+
export declare function newSecretReference(): string;
|
|
45
|
+
export declare function parseStoredCredential(value: string): StoredCredential;
|
|
37
46
|
interface CommandResult {
|
|
38
47
|
stdout: string;
|
|
39
48
|
stderr: string;
|
|
40
49
|
code: number;
|
|
41
50
|
}
|
|
42
51
|
export type CredentialCommandRunner = (executable: string, arguments_: string[], input?: string) => Promise<CommandResult>;
|
|
52
|
+
/** macOS Keychain or Linux Secret Service. */
|
|
43
53
|
export declare class OsCredentialStore implements CredentialStore {
|
|
44
54
|
private readonly platform;
|
|
45
55
|
private readonly run;
|
|
@@ -47,10 +57,49 @@ export declare class OsCredentialStore implements CredentialStore {
|
|
|
47
57
|
platform?: NodeJS.Platform;
|
|
48
58
|
run?: CredentialCommandRunner;
|
|
49
59
|
});
|
|
50
|
-
get(reference: string): Promise<
|
|
51
|
-
set(reference: string, credential:
|
|
60
|
+
get(reference: string): Promise<StoredCredential | undefined>;
|
|
61
|
+
set(reference: string, credential: StoredCredential): Promise<void>;
|
|
52
62
|
delete(reference: string): Promise<boolean>;
|
|
53
63
|
private unsupported;
|
|
54
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* One restricted file per secret under `<home>/credentials/`, mode 0600 in a
|
|
67
|
+
* 0700 directory. Chosen explicitly (`--store file`), never as a fallback.
|
|
68
|
+
*/
|
|
69
|
+
export declare class FileCredentialStore implements CredentialStore {
|
|
70
|
+
private readonly directory;
|
|
71
|
+
constructor(home: string);
|
|
72
|
+
private path;
|
|
73
|
+
get(reference: string): Promise<StoredCredential | undefined>;
|
|
74
|
+
set(reference: string, credential: StoredCredential): Promise<void>;
|
|
75
|
+
delete(reference: string): Promise<boolean>;
|
|
76
|
+
}
|
|
77
|
+
export interface CredentialStores {
|
|
78
|
+
keychain: CredentialStore;
|
|
79
|
+
file: CredentialStore;
|
|
80
|
+
}
|
|
81
|
+
export declare function defaultCredentialStores(home: string): CredentialStores;
|
|
82
|
+
/**
|
|
83
|
+
* A cross-process lock around one credential's refresh.
|
|
84
|
+
*
|
|
85
|
+
* `O_EXCL` creation is the mutual exclusion. The lock file records its owner
|
|
86
|
+
* (pid, host) and a random token. A lock is broken only when its owner is
|
|
87
|
+
* provably gone — a dead pid on this host — or, for an owner on another host
|
|
88
|
+
* or an unreadable file, once it is older than `staleMs`. A live owner is never
|
|
89
|
+
* preempted however long its refresh takes (the refresh request itself times
|
|
90
|
+
* out well inside `staleMs`).
|
|
91
|
+
*
|
|
92
|
+
* Breaking renames the file aside first and checks the token: if another
|
|
93
|
+
* process already broke and re-acquired it in between, the fresh lock is put
|
|
94
|
+
* back rather than deleted. Correctness against a concurrent writer is still
|
|
95
|
+
* the generation compare-and-swap in the refresh path; the lock exists so that
|
|
96
|
+
* only one process ever spends a given refresh token. Locks assume one host's
|
|
97
|
+
* filesystem; a home shared over a network filesystem is not supported.
|
|
98
|
+
*/
|
|
99
|
+
export declare function withCredentialLock<T>(home: string, name: string, operation: () => Promise<T>, options?: {
|
|
100
|
+
staleMs?: number;
|
|
101
|
+
timeoutMs?: number;
|
|
102
|
+
pollMs?: number;
|
|
103
|
+
}): Promise<T>;
|
|
55
104
|
export {};
|
|
56
105
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|