@losthex/scribe 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/README.md +33 -0
- package/dist/config.js +102 -0
- package/dist/config.js.map +1 -0
- package/dist/git.js +110 -0
- package/dist/git.js.map +1 -0
- package/dist/hook.js +115 -0
- package/dist/hook.js.map +1 -0
- package/dist/index.js +491 -0
- package/dist/index.js.map +1 -0
- package/dist/install.js +140 -0
- package/dist/install.js.map +1 -0
- package/dist/keychain.js +181 -0
- package/dist/keychain.js.map +1 -0
- package/dist/lock.js +59 -0
- package/dist/lock.js.map +1 -0
- package/dist/login.js +99 -0
- package/dist/login.js.map +1 -0
- package/dist/mcp.js +138 -0
- package/dist/mcp.js.map +1 -0
- package/dist/paths.js +28 -0
- package/dist/paths.js.map +1 -0
- package/dist/pkce.js +137 -0
- package/dist/pkce.js.map +1 -0
- package/dist/queue.js +223 -0
- package/dist/queue.js.map +1 -0
- package/dist/send.js +151 -0
- package/dist/send.js.map +1 -0
- package/dist/tokens.js +106 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
package/dist/keychain.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.accountFor = accountFor;
|
|
4
|
+
exports.macKeychainStore = macKeychainStore;
|
|
5
|
+
exports.secretToolStore = secretToolStore;
|
|
6
|
+
exports.fileStore = fileStore;
|
|
7
|
+
exports.tokenStore = tokenStore;
|
|
8
|
+
/**
|
|
9
|
+
* Token storage — the OS keychain, because a refresh token is a standing
|
|
10
|
+
* credential for a person's Scribe identity (INFRA-ITD-004: "`scribe
|
|
11
|
+
* login` runs PKCE loopback once and keeps the refresh token in the OS
|
|
12
|
+
* keychain").
|
|
13
|
+
*
|
|
14
|
+
* macOS gets the real thing through `/usr/bin/security`: a generic
|
|
15
|
+
* password item, per issuer, that the login keychain encrypts at rest and
|
|
16
|
+
* unlocks with the engineer's login session. Linux gets `secret-tool`
|
|
17
|
+
* (libsecret) when it is installed. Anything else falls back to a 0600
|
|
18
|
+
* file with a printed warning — a fallback that says so is better than a
|
|
19
|
+
* CLI that refuses to run on a machine without a keyring.
|
|
20
|
+
*
|
|
21
|
+
* Known tradeoff on the macOS path: `security add-generic-password` takes
|
|
22
|
+
* the secret as an argv value, so it is briefly visible to `ps` on the
|
|
23
|
+
* same machine. The alternative is a native Keychain binding, which means
|
|
24
|
+
* a compiled dependency in a tool that otherwise has none; on a
|
|
25
|
+
* single-user laptop, for a token the same user can read from the keychain
|
|
26
|
+
* anyway, the tradeoff is worth naming rather than paying for.
|
|
27
|
+
*/
|
|
28
|
+
const node_child_process_1 = require("node:child_process");
|
|
29
|
+
const node_fs_1 = require("node:fs");
|
|
30
|
+
const node_path_1 = require("node:path");
|
|
31
|
+
const SERVICE = 'scribe';
|
|
32
|
+
/** One keychain item per issuer, so stages never share credentials. */
|
|
33
|
+
function accountFor(issuer) {
|
|
34
|
+
try {
|
|
35
|
+
return new URL(issuer).host;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return issuer;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parse(raw) {
|
|
42
|
+
if (!raw)
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(raw);
|
|
46
|
+
return parsed.access && parsed.refresh ? parsed : null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function commandExists(command) {
|
|
53
|
+
try {
|
|
54
|
+
(0, node_child_process_1.execFileSync)('command', ['-v', command], { shell: '/bin/sh', stdio: 'ignore' });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function macKeychainStore(issuer) {
|
|
62
|
+
const account = accountFor(issuer);
|
|
63
|
+
const run = (args) => (0, node_child_process_1.execFileSync)('/usr/bin/security', args, {
|
|
64
|
+
encoding: 'utf8',
|
|
65
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
read() {
|
|
69
|
+
try {
|
|
70
|
+
return parse(run(['find-generic-password', '-s', SERVICE, '-a', account, '-w']).trim());
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
write(tokens) {
|
|
77
|
+
// -U updates in place; without it a second login is a duplicate item.
|
|
78
|
+
run([
|
|
79
|
+
'add-generic-password',
|
|
80
|
+
'-U',
|
|
81
|
+
'-s',
|
|
82
|
+
SERVICE,
|
|
83
|
+
'-a',
|
|
84
|
+
account,
|
|
85
|
+
'-D',
|
|
86
|
+
'Scribe issuer tokens',
|
|
87
|
+
'-j',
|
|
88
|
+
'Written by `scribe login`',
|
|
89
|
+
'-w',
|
|
90
|
+
JSON.stringify(tokens),
|
|
91
|
+
]);
|
|
92
|
+
},
|
|
93
|
+
clear() {
|
|
94
|
+
try {
|
|
95
|
+
run(['delete-generic-password', '-s', SERVICE, '-a', account]);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Nothing stored — clearing is idempotent.
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
describe() {
|
|
102
|
+
return `macOS keychain (service ${SERVICE}, account ${account})`;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function secretToolStore(issuer) {
|
|
107
|
+
const account = accountFor(issuer);
|
|
108
|
+
const attrs = ['service', SERVICE, 'account', account];
|
|
109
|
+
return {
|
|
110
|
+
read() {
|
|
111
|
+
try {
|
|
112
|
+
return parse((0, node_child_process_1.execFileSync)('secret-tool', ['lookup', ...attrs], {
|
|
113
|
+
encoding: 'utf8',
|
|
114
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
115
|
+
}).trim());
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
write(tokens) {
|
|
122
|
+
(0, node_child_process_1.execFileSync)('secret-tool', ['store', '--label=Scribe issuer tokens', ...attrs], {
|
|
123
|
+
input: JSON.stringify(tokens),
|
|
124
|
+
stdio: ['pipe', 'ignore', 'pipe'],
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
clear() {
|
|
128
|
+
try {
|
|
129
|
+
(0, node_child_process_1.execFileSync)('secret-tool', ['clear', ...attrs], { stdio: 'ignore' });
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Idempotent.
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
describe() {
|
|
136
|
+
return `libsecret via secret-tool (service ${SERVICE}, account ${account})`;
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/** Last resort, and it says so. */
|
|
141
|
+
function fileStore(path, warn = () => { }) {
|
|
142
|
+
let warned = false;
|
|
143
|
+
const warnOnce = () => {
|
|
144
|
+
if (warned)
|
|
145
|
+
return;
|
|
146
|
+
warned = true;
|
|
147
|
+
warn(`no OS keyring found — storing tokens in ${path} (mode 0600)`);
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
read() {
|
|
151
|
+
try {
|
|
152
|
+
return parse((0, node_fs_1.readFileSync)(path, 'utf8'));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
write(tokens) {
|
|
159
|
+
warnOnce();
|
|
160
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
161
|
+
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(tokens)}\n`, { mode: 0o600 });
|
|
162
|
+
(0, node_fs_1.chmodSync)(path, 0o600);
|
|
163
|
+
},
|
|
164
|
+
clear() {
|
|
165
|
+
(0, node_fs_1.rmSync)(path, { force: true });
|
|
166
|
+
},
|
|
167
|
+
describe() {
|
|
168
|
+
return `file ${path} (no OS keyring available)`;
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function tokenStore(issuer, { fallbackPath, platform = process.platform, env = process.env, warn }) {
|
|
173
|
+
if (env.SCRIBE_TOKEN_STORE === 'file')
|
|
174
|
+
return fileStore(fallbackPath, warn);
|
|
175
|
+
if (platform === 'darwin')
|
|
176
|
+
return macKeychainStore(issuer);
|
|
177
|
+
if (platform === 'linux' && commandExists('secret-tool'))
|
|
178
|
+
return secretToolStore(issuer);
|
|
179
|
+
return fileStore(fallbackPath, warn);
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"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"}
|
package/dist/lock.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.STALE_LOCK_MS = void 0;
|
|
4
|
+
exports.withLock = withLock;
|
|
5
|
+
exports.lockHolder = lockHolder;
|
|
6
|
+
/**
|
|
7
|
+
* One flush at a time. Two commits in the same second each spawn a flush,
|
|
8
|
+
* and two flushes sharing a queue file would resend or lose a batch, so the
|
|
9
|
+
* loser simply skips — the queue is durable and the next commit flushes it.
|
|
10
|
+
*
|
|
11
|
+
* `wx` is the whole mechanism: an exclusive create is atomic on every
|
|
12
|
+
* filesystem the CLI runs on. A stale lock (crashed process, killed
|
|
13
|
+
* terminal) times out rather than wedging the collector forever.
|
|
14
|
+
*/
|
|
15
|
+
const node_fs_1 = require("node:fs");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
17
|
+
exports.STALE_LOCK_MS = 5 * 60_000;
|
|
18
|
+
async function withLock(path, fn, { staleMs = exports.STALE_LOCK_MS, now = Date.now } = {}) {
|
|
19
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
20
|
+
try {
|
|
21
|
+
(0, node_fs_1.writeFileSync)(path, `${process.pid}\n`, { flag: 'wx', mode: 0o600 });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
let age = Infinity;
|
|
25
|
+
try {
|
|
26
|
+
// Clamped: a filesystem timestamp a hair ahead of our clock is not a
|
|
27
|
+
// lock from the future.
|
|
28
|
+
age = Math.max(0, now() - (0, node_fs_1.statSync)(path).mtimeMs);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Vanished between the failed create and the stat — treat as stale.
|
|
32
|
+
}
|
|
33
|
+
if (age < staleMs)
|
|
34
|
+
return { acquired: false };
|
|
35
|
+
// Stale: take it over, recording who we are for the next reader.
|
|
36
|
+
try {
|
|
37
|
+
(0, node_fs_1.writeFileSync)(path, `${process.pid}\n`, { mode: 0o600 });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { acquired: false };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return { acquired: true, value: await fn() };
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
(0, node_fs_1.rmSync)(path, { force: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Who holds the lock, for `scribe status`. */
|
|
51
|
+
function lockHolder(path) {
|
|
52
|
+
try {
|
|
53
|
+
return (0, node_fs_1.readFileSync)(path, 'utf8').trim() || null;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# 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":";;;AAoBA,4BA8BC;AAGD,gCAMC;AA3DD;;;;;;;;GAQG;AACH,qCAAmF;AACnF,yCAAoC;AAEvB,QAAA,aAAa,GAAG,CAAC,GAAG,MAAM,CAAC;AAQjC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,EAAwB,EACxB,EAAE,OAAO,GAAG,qBAAa,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,KAA+C,EAAE;IAE1F,IAAA,mBAAS,EAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,IAAA,uBAAa,EAAC,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,GAAG,QAAQ,CAAC;QACnB,IAAI,CAAC;YACH,qEAAqE;YACrE,wBAAwB;YACxB,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,IAAA,kBAAQ,EAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;QACD,IAAI,GAAG,GAAG,OAAO;YAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC9C,iEAAiE;QACjE,IAAI,CAAC;YACH,IAAA,uBAAa,EAAC,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC/C,CAAC;YAAS,CAAC;QACT,IAAA,gBAAM,EAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,SAAgB,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,IAAA,sBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/login.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openInBrowser = openInBrowser;
|
|
4
|
+
exports.login = login;
|
|
5
|
+
/**
|
|
6
|
+
* `scribe login` — one browser sign-in, then the CLI never asks again.
|
|
7
|
+
*
|
|
8
|
+
* The loopback half of PKCE: a throwaway HTTP server on 127.0.0.1 with an
|
|
9
|
+
* OS-assigned port receives the authorization code, so the code never
|
|
10
|
+
* leaves the machine and no client secret exists to leak. The issuer's
|
|
11
|
+
* redirect allowlist permits loopback already (packages/core/src/auth/redirect.ts), so
|
|
12
|
+
* this needs no per-stage registration — unlike the Google leg, which is
|
|
13
|
+
* registered once per issuer.
|
|
14
|
+
*/
|
|
15
|
+
const node_http_1 = require("node:http");
|
|
16
|
+
const node_child_process_1 = require("node:child_process");
|
|
17
|
+
const pkce_1 = require("./pkce");
|
|
18
|
+
const DONE_PAGE = (message) => `<!doctype html>
|
|
19
|
+
<html><head><meta charset="utf-8"><title>Scribe</title></head>
|
|
20
|
+
<body style="font:16px/1.5 -apple-system,system-ui,sans-serif;margin:4rem auto;max-width:32rem">
|
|
21
|
+
<h1 style="font-size:1.25rem">Scribe</h1><p>${message}</p>
|
|
22
|
+
<p style="color:#666">You can close this tab and go back to your terminal.</p>
|
|
23
|
+
</body></html>`;
|
|
24
|
+
function openInBrowser(url) {
|
|
25
|
+
const command = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
26
|
+
try {
|
|
27
|
+
(0, node_child_process_1.spawn)(command, [url], { stdio: 'ignore', detached: true }).unref();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Headless machine — the printed URL is the fallback.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Runs the whole flow and returns the tokens; the caller stores them.
|
|
35
|
+
* Rejects on timeout, on a state mismatch, or on an error from the issuer.
|
|
36
|
+
*/
|
|
37
|
+
async function login({ issuer, open = openInBrowser, log = () => { }, fetchImpl = fetch, timeoutMs = 5 * 60_000, }) {
|
|
38
|
+
const verifier = (0, pkce_1.createVerifier)();
|
|
39
|
+
const state = (0, pkce_1.createState)();
|
|
40
|
+
// Assigned once the OS has picked the port; the exchange must send the
|
|
41
|
+
// identical value, so it is captured rather than rebuilt.
|
|
42
|
+
let redirect = '';
|
|
43
|
+
const code = await new Promise((resolve, reject) => {
|
|
44
|
+
const server = (0, node_http_1.createServer)((req, res) => {
|
|
45
|
+
const result = (0, pkce_1.parseCallback)(req.url ?? '/');
|
|
46
|
+
const finish = (status, message) => {
|
|
47
|
+
res.writeHead(status, { 'content-type': 'text/html; charset=utf-8' });
|
|
48
|
+
res.end(DONE_PAGE(message));
|
|
49
|
+
};
|
|
50
|
+
if (result.error) {
|
|
51
|
+
finish(400, `Sign-in failed: ${result.error}`);
|
|
52
|
+
close();
|
|
53
|
+
reject(new Error(result.error));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!result.code) {
|
|
57
|
+
// Favicon and friends: not the callback, not an error.
|
|
58
|
+
res.writeHead(404).end();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (result.state !== state) {
|
|
62
|
+
finish(400, 'Sign-in failed: state mismatch.');
|
|
63
|
+
close();
|
|
64
|
+
reject(new Error('state mismatch — the callback did not come from this login'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
finish(200, 'Signed in. Scribe has your token.');
|
|
68
|
+
close();
|
|
69
|
+
resolve(result.code);
|
|
70
|
+
});
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
close();
|
|
73
|
+
reject(new Error(`timed out after ${Math.round(timeoutMs / 1000)}s waiting for sign-in`));
|
|
74
|
+
}, timeoutMs);
|
|
75
|
+
function close() {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
server.close();
|
|
78
|
+
}
|
|
79
|
+
server.on('error', (err) => {
|
|
80
|
+
close();
|
|
81
|
+
reject(err);
|
|
82
|
+
});
|
|
83
|
+
server.listen(0, '127.0.0.1', () => {
|
|
84
|
+
const { port } = server.address();
|
|
85
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
86
|
+
const url = (0, pkce_1.authorizeUrl)({
|
|
87
|
+
issuer,
|
|
88
|
+
redirectUri,
|
|
89
|
+
challenge: (0, pkce_1.challengeFor)(verifier),
|
|
90
|
+
state,
|
|
91
|
+
});
|
|
92
|
+
redirect = redirectUri;
|
|
93
|
+
log(`Opening your browser to sign in with Google:\n ${url}`);
|
|
94
|
+
open(url);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
return (0, pkce_1.exchangeCode)({ issuer, code, redirectUri: redirect, verifier, fetchImpl });
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROTOCOL_VERSION_HEADER = void 0;
|
|
4
|
+
exports.proxyError = proxyError;
|
|
5
|
+
exports.protocolVersionOf = protocolVersionOf;
|
|
6
|
+
exports.forward = forward;
|
|
7
|
+
exports.runProxy = runProxy;
|
|
8
|
+
/** The MCP protocol revision this proxy announces on every request. */
|
|
9
|
+
exports.PROTOCOL_VERSION_HEADER = 'mcp-protocol-version';
|
|
10
|
+
async function post(deps, line, token, protocolVersion) {
|
|
11
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
12
|
+
let response;
|
|
13
|
+
try {
|
|
14
|
+
response = await fetchImpl(deps.url, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
authorization: `Bearer ${token}`,
|
|
18
|
+
'content-type': 'application/json',
|
|
19
|
+
accept: 'application/json, text/event-stream',
|
|
20
|
+
...(protocolVersion ? { [exports.PROTOCOL_VERSION_HEADER]: protocolVersion } : {}),
|
|
21
|
+
},
|
|
22
|
+
body: line,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return { status: 0, body: null, error: err instanceof Error ? err.message : String(err) };
|
|
27
|
+
}
|
|
28
|
+
const text = await response.text();
|
|
29
|
+
return { status: response.status, body: text.trim().length > 0 ? text.trim() : null };
|
|
30
|
+
}
|
|
31
|
+
/** A JSON-RPC error the proxy itself has to answer, keeping the id. */
|
|
32
|
+
function proxyError(line, message) {
|
|
33
|
+
let id = null;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(line);
|
|
36
|
+
// Notifications carry no id and expect no answer, not even an error.
|
|
37
|
+
if (parsed.id === undefined || parsed.id === null)
|
|
38
|
+
return null;
|
|
39
|
+
id = parsed.id;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32000, message } });
|
|
45
|
+
}
|
|
46
|
+
/** Remembers the negotiated protocol version across the session. */
|
|
47
|
+
function protocolVersionOf(line) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(line);
|
|
50
|
+
const version = parsed.result?.protocolVersion;
|
|
51
|
+
return typeof version === 'string' ? version : undefined;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Forwards one message. A 401 buys exactly one refresh and one retry —
|
|
59
|
+
* the same contract the amend flush has, and for the same reason: the
|
|
60
|
+
* CLI's clock and the issuer's opinion of expiry can disagree.
|
|
61
|
+
*/
|
|
62
|
+
async function forward(deps, line, protocolVersion) {
|
|
63
|
+
let token;
|
|
64
|
+
try {
|
|
65
|
+
token = await deps.token();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
return {
|
|
69
|
+
status: 401,
|
|
70
|
+
body: null,
|
|
71
|
+
error: err instanceof Error ? err.message : 'not signed in — run `scribe login`',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
let result = await post(deps, line, token, protocolVersion);
|
|
75
|
+
if (result.status === 401) {
|
|
76
|
+
const refreshed = await deps.refresh();
|
|
77
|
+
if (refreshed)
|
|
78
|
+
result = await post(deps, line, refreshed, protocolVersion);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Splits a stdio stream into newline-delimited JSON-RPC messages and
|
|
84
|
+
* pumps each one through. Resolves when stdin closes, which is how a
|
|
85
|
+
* harness tells the server to shut down.
|
|
86
|
+
*/
|
|
87
|
+
async function runProxy(input, deps) {
|
|
88
|
+
const log = deps.log ?? (() => { });
|
|
89
|
+
let buffer = '';
|
|
90
|
+
let protocolVersion;
|
|
91
|
+
// Messages are handled one at a time: the harness expects replies in
|
|
92
|
+
// request order, and a Lambda has no session to interleave against.
|
|
93
|
+
let chain = Promise.resolve();
|
|
94
|
+
const handle = async (line) => {
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (trimmed.length === 0)
|
|
97
|
+
return;
|
|
98
|
+
const result = await forward(deps, trimmed, protocolVersion);
|
|
99
|
+
if (result.status === 0 || result.error) {
|
|
100
|
+
log(`scribe mcp: ${result.error ?? 'no response'}`);
|
|
101
|
+
const reply = proxyError(trimmed, result.error ?? 'the Scribe MCP endpoint is unreachable');
|
|
102
|
+
if (reply)
|
|
103
|
+
deps.write(reply);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (result.status === 202 || !result.body)
|
|
107
|
+
return;
|
|
108
|
+
if (result.status >= 400) {
|
|
109
|
+
log(`scribe mcp: endpoint answered ${result.status}`);
|
|
110
|
+
// The endpoint's body is already a JSON-RPC error object when it
|
|
111
|
+
// knows the protocol; pass it through rather than paraphrasing.
|
|
112
|
+
deps.write(result.body);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
protocolVersion = protocolVersionOf(result.body) ?? protocolVersion;
|
|
116
|
+
deps.write(result.body);
|
|
117
|
+
};
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
input.setEncoding('utf8');
|
|
120
|
+
input.on('data', (chunk) => {
|
|
121
|
+
buffer += chunk;
|
|
122
|
+
let index = buffer.indexOf('\n');
|
|
123
|
+
while (index >= 0) {
|
|
124
|
+
const line = buffer.slice(0, index);
|
|
125
|
+
buffer = buffer.slice(index + 1);
|
|
126
|
+
chain = chain.then(() => handle(line));
|
|
127
|
+
index = buffer.indexOf('\n');
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
input.on('error', reject);
|
|
131
|
+
input.on('end', () => {
|
|
132
|
+
if (buffer.trim().length > 0)
|
|
133
|
+
chain = chain.then(() => handle(buffer));
|
|
134
|
+
chain.then(resolve, reject);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePaths = resolvePaths;
|
|
4
|
+
/**
|
|
5
|
+
* Where the CLI keeps its state on the engineer's machine.
|
|
6
|
+
*
|
|
7
|
+
* Everything lives under one directory so `scribe status` can show it and
|
|
8
|
+
* `scribe uninstall` can remove it. `SCRIBE_HOME` relocates the whole lot,
|
|
9
|
+
* which is how the tests and the scratch-repo demo run without touching a
|
|
10
|
+
* real home directory — and how the installed git hook keeps pointing at
|
|
11
|
+
* the same state it was installed with.
|
|
12
|
+
*/
|
|
13
|
+
const node_os_1 = require("node:os");
|
|
14
|
+
const node_path_1 = require("node:path");
|
|
15
|
+
/** Honours XDG_CONFIG_HOME, and SCRIBE_HOME above everything. */
|
|
16
|
+
function resolvePaths(env = process.env) {
|
|
17
|
+
const home = env.SCRIBE_HOME ?? (0, node_path_1.join)(env.XDG_CONFIG_HOME ?? (0, node_path_1.join)((0, node_os_1.homedir)(), '.config'), 'scribe');
|
|
18
|
+
return {
|
|
19
|
+
home,
|
|
20
|
+
config: (0, node_path_1.join)(home, 'config.json'),
|
|
21
|
+
queue: (0, node_path_1.join)(home, 'queue.jsonl'),
|
|
22
|
+
sent: (0, node_path_1.join)(home, 'sent.jsonl'),
|
|
23
|
+
dead: (0, node_path_1.join)(home, 'rejected.jsonl'),
|
|
24
|
+
lock: (0, node_path_1.join)(home, 'flush.lock'),
|
|
25
|
+
hooks: (0, node_path_1.join)(home, 'git-hooks'),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":";;AA6BA,oCAWC;AAxCD;;;;;;;;GAQG;AACH,qCAAkC;AAClC,yCAAiC;AAkBjC,iEAAiE;AACjE,SAAgB,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,IAAI,IAAA,gBAAI,EAAC,GAAG,CAAC,eAAe,IAAI,IAAA,gBAAI,EAAC,IAAA,iBAAO,GAAE,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClG,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,aAAa,CAAC;QACjC,KAAK,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,aAAa,CAAC;QAChC,IAAI,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,YAAY,CAAC;QAC9B,IAAI,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,gBAAgB,CAAC;QAClC,IAAI,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,YAAY,CAAC;QAC9B,KAAK,EAAE,IAAA,gBAAI,EAAC,IAAI,EAAE,WAAW,CAAC;KAC/B,CAAC;AACJ,CAAC"}
|