@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
package/dist/pkce.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OAuthError = exports.PROVIDER = exports.CLIENT_ID = void 0;
|
|
4
|
+
exports.createVerifier = createVerifier;
|
|
5
|
+
exports.challengeFor = challengeFor;
|
|
6
|
+
exports.createState = createState;
|
|
7
|
+
exports.authorizeUrl = authorizeUrl;
|
|
8
|
+
exports.parseCallback = parseCallback;
|
|
9
|
+
exports.exchangeCode = exchangeCode;
|
|
10
|
+
exports.refreshTokens = refreshTokens;
|
|
11
|
+
exports.readAccessToken = readAccessToken;
|
|
12
|
+
exports.needsRefresh = needsRefresh;
|
|
13
|
+
/**
|
|
14
|
+
* Authorization code + PKCE against the Scribe issuer (INFRA-ITD-004,
|
|
15
|
+
* SPEC-008 "Fallback & Testing") — the protocol half, with no IO except
|
|
16
|
+
* the two token calls, both of which take an injectable `fetch`.
|
|
17
|
+
*
|
|
18
|
+
* PKCE and not a client secret because this is a public client: the CLI
|
|
19
|
+
* ships on engineers' laptops, so anything embedded in it is public. The
|
|
20
|
+
* verifier never leaves the process, the challenge is what travels, and
|
|
21
|
+
* the loopback redirect (which the issuer's allowlist already permits, see
|
|
22
|
+
* packages/core/src/auth/redirect.ts) keeps the code off the network entirely.
|
|
23
|
+
*/
|
|
24
|
+
const node_crypto_1 = require("node:crypto");
|
|
25
|
+
/** Identifies the CLI to the issuer; public by construction. */
|
|
26
|
+
exports.CLIENT_ID = 'scribe-cli';
|
|
27
|
+
/** The only provider the issuer has (INFRA-ITD-004). */
|
|
28
|
+
exports.PROVIDER = 'google';
|
|
29
|
+
const b64url = (buffer) => buffer.toString('base64url');
|
|
30
|
+
/** 32 random bytes, base64url — RFC 7636 §4.1 length rules satisfied. */
|
|
31
|
+
function createVerifier() {
|
|
32
|
+
return b64url((0, node_crypto_1.randomBytes)(32));
|
|
33
|
+
}
|
|
34
|
+
/** S256: the challenge is the SHA-256 of the verifier, base64url. */
|
|
35
|
+
function challengeFor(verifier) {
|
|
36
|
+
return b64url((0, node_crypto_1.createHash)('sha256').update(verifier).digest());
|
|
37
|
+
}
|
|
38
|
+
function createState() {
|
|
39
|
+
return b64url((0, node_crypto_1.randomBytes)(16));
|
|
40
|
+
}
|
|
41
|
+
function authorizeUrl({ issuer, redirectUri, challenge, state, clientId = exports.CLIENT_ID, provider = exports.PROVIDER, }) {
|
|
42
|
+
const url = new URL(`${issuer.replace(/\/+$/, '')}/authorize`);
|
|
43
|
+
url.searchParams.set('client_id', clientId);
|
|
44
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
45
|
+
url.searchParams.set('response_type', 'code');
|
|
46
|
+
url.searchParams.set('state', state);
|
|
47
|
+
url.searchParams.set('provider', provider);
|
|
48
|
+
url.searchParams.set('code_challenge', challenge);
|
|
49
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
50
|
+
return url.toString();
|
|
51
|
+
}
|
|
52
|
+
/** Reads the query off the loopback request the browser lands on. */
|
|
53
|
+
function parseCallback(requestUrl) {
|
|
54
|
+
const url = new URL(requestUrl, 'http://127.0.0.1');
|
|
55
|
+
const error = url.searchParams.get('error');
|
|
56
|
+
const description = url.searchParams.get('error_description');
|
|
57
|
+
return {
|
|
58
|
+
code: url.searchParams.get('code') ?? undefined,
|
|
59
|
+
state: url.searchParams.get('state') ?? undefined,
|
|
60
|
+
error: error ? (description ? `${error}: ${description}` : error) : undefined,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
class OAuthError extends Error {
|
|
64
|
+
status;
|
|
65
|
+
constructor(message, status = 0) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.name = 'OAuthError';
|
|
68
|
+
this.status = status;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.OAuthError = OAuthError;
|
|
72
|
+
async function tokenCall(issuer, body, fetchImpl, now) {
|
|
73
|
+
const response = await fetchImpl(`${issuer.replace(/\/+$/, '')}/token`, {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
76
|
+
body: new URLSearchParams(body).toString(),
|
|
77
|
+
});
|
|
78
|
+
const text = await response.text();
|
|
79
|
+
let payload = {};
|
|
80
|
+
try {
|
|
81
|
+
payload = JSON.parse(text);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Leave payload empty; the status and raw text carry the message.
|
|
85
|
+
}
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const detail = typeof payload.error === 'string' ? payload.error : text.slice(0, 200);
|
|
88
|
+
throw new OAuthError(`issuer rejected the token request (${response.status}): ${detail}`, response.status);
|
|
89
|
+
}
|
|
90
|
+
const access = payload.access_token;
|
|
91
|
+
const refresh = payload.refresh_token;
|
|
92
|
+
if (typeof access !== 'string' || typeof refresh !== 'string') {
|
|
93
|
+
throw new OAuthError('issuer returned no access/refresh token pair');
|
|
94
|
+
}
|
|
95
|
+
const expiresIn = typeof payload.expires_in === 'number' ? payload.expires_in : null;
|
|
96
|
+
return {
|
|
97
|
+
access,
|
|
98
|
+
refresh,
|
|
99
|
+
expiresAt: expiresIn === null ? null : now() + expiresIn * 1000,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function exchangeCode({ issuer, code, redirectUri, verifier, clientId = exports.CLIENT_ID, fetchImpl = fetch, now = Date.now, }) {
|
|
103
|
+
return tokenCall(issuer, {
|
|
104
|
+
grant_type: 'authorization_code',
|
|
105
|
+
code,
|
|
106
|
+
redirect_uri: redirectUri,
|
|
107
|
+
client_id: clientId,
|
|
108
|
+
code_verifier: verifier,
|
|
109
|
+
}, fetchImpl, now);
|
|
110
|
+
}
|
|
111
|
+
function refreshTokens({ issuer, refresh, clientId = exports.CLIENT_ID, fetchImpl = fetch, now = Date.now, }) {
|
|
112
|
+
return tokenCall(issuer, { grant_type: 'refresh_token', refresh_token: refresh, client_id: clientId }, fetchImpl, now);
|
|
113
|
+
}
|
|
114
|
+
function readAccessToken(token) {
|
|
115
|
+
const parts = token.split('.');
|
|
116
|
+
if (parts.length !== 3)
|
|
117
|
+
return { email: null, expiresAt: null };
|
|
118
|
+
try {
|
|
119
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
120
|
+
const email = payload.properties?.email;
|
|
121
|
+
return {
|
|
122
|
+
email: typeof email === 'string' ? email : null,
|
|
123
|
+
expiresAt: typeof payload.exp === 'number' ? payload.exp * 1000 : null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return { email: null, expiresAt: null };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** True when the access token is gone, expired, or about to be. */
|
|
131
|
+
function needsRefresh(tokens, nowMs, skewSeconds = 60) {
|
|
132
|
+
const expiresAt = tokens.expiresAt ?? readAccessToken(tokens.access).expiresAt;
|
|
133
|
+
if (expiresAt === null)
|
|
134
|
+
return false;
|
|
135
|
+
return expiresAt - skewSeconds * 1000 <= nowMs;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=pkce.js.map
|
package/dist/pkce.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"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"}
|
package/dist/queue.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NETWORK_VERDICT = exports.MAX_COMMITS_PER_BATCH = exports.MAX_ENTRY_AGE_DAYS = exports.BACKOFF_MS = void 0;
|
|
4
|
+
exports.dayOf = dayOf;
|
|
5
|
+
exports.newEntry = newEntry;
|
|
6
|
+
exports.backoffMs = backoffMs;
|
|
7
|
+
exports.isExpired = isExpired;
|
|
8
|
+
exports.isDue = isDue;
|
|
9
|
+
exports.batches = batches;
|
|
10
|
+
exports.classifyStatus = classifyStatus;
|
|
11
|
+
exports.applyFailure = applyFailure;
|
|
12
|
+
exports.mergeQueue = mergeQueue;
|
|
13
|
+
exports.readQueue = readQueue;
|
|
14
|
+
exports.appendEntry = appendEntry;
|
|
15
|
+
exports.writeQueue = writeQueue;
|
|
16
|
+
exports.appendOutcome = appendOutcome;
|
|
17
|
+
exports.readOutcomes = readOutcomes;
|
|
18
|
+
/**
|
|
19
|
+
* The offline queue: one JSONL file of captured commits, and the retry
|
|
20
|
+
* rules that get them to `/amend` eventually.
|
|
21
|
+
*
|
|
22
|
+
* Capture stays local and offline-safe (SPEC-002): a commit on a plane
|
|
23
|
+
* appends a line and returns, and an unsent queue simply carries over to
|
|
24
|
+
* the next flush. Only flushing touches the network. The file is JSONL
|
|
25
|
+
* rather than JSON because appending one line is atomic enough for two
|
|
26
|
+
* commits landing in the same second, while rewriting a whole array is not.
|
|
27
|
+
*
|
|
28
|
+
* Everything that decides *when* and *what* to send is a pure function
|
|
29
|
+
* here; the IO is the four thin readers/writers at the bottom.
|
|
30
|
+
*/
|
|
31
|
+
const node_fs_1 = require("node:fs");
|
|
32
|
+
const node_path_1 = require("node:path");
|
|
33
|
+
const node_crypto_1 = require("node:crypto");
|
|
34
|
+
/**
|
|
35
|
+
* The report day is the *local* date of the commit: `git log %aI` carries
|
|
36
|
+
* the author's own offset, so the first ten characters are already the day
|
|
37
|
+
* the engineer would say they did the work.
|
|
38
|
+
*/
|
|
39
|
+
function dayOf(isoWithOffset) {
|
|
40
|
+
const match = isoWithOffset.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
41
|
+
if (match)
|
|
42
|
+
return match[1];
|
|
43
|
+
return new Date(isoWithOffset).toISOString().slice(0, 10);
|
|
44
|
+
}
|
|
45
|
+
function newEntry(input) {
|
|
46
|
+
const now = input.now ?? new Date();
|
|
47
|
+
return {
|
|
48
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
49
|
+
day: dayOf(input.commit.at),
|
|
50
|
+
project: input.project,
|
|
51
|
+
commit: input.commit,
|
|
52
|
+
queuedAt: now.toISOString(),
|
|
53
|
+
attempts: 0,
|
|
54
|
+
nextAttemptAt: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Backoff schedule: minutes at first, a day at worst. */
|
|
58
|
+
exports.BACKOFF_MS = [
|
|
59
|
+
60_000, // 1 minute
|
|
60
|
+
5 * 60_000,
|
|
61
|
+
15 * 60_000,
|
|
62
|
+
60 * 60_000,
|
|
63
|
+
6 * 60 * 60_000,
|
|
64
|
+
24 * 60 * 60_000,
|
|
65
|
+
];
|
|
66
|
+
function backoffMs(attempts) {
|
|
67
|
+
if (attempts <= 0)
|
|
68
|
+
return 0;
|
|
69
|
+
return exports.BACKOFF_MS[Math.min(attempts, exports.BACKOFF_MS.length) - 1];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* How old an entry may get before sending it is hopeless — the endpoint
|
|
73
|
+
* refuses a date past its backfill window, so retrying forever would only
|
|
74
|
+
* hide the queue behind permanent 400s.
|
|
75
|
+
*/
|
|
76
|
+
exports.MAX_ENTRY_AGE_DAYS = 30;
|
|
77
|
+
function isExpired(entry, now) {
|
|
78
|
+
const day = new Date(`${entry.day}T00:00:00Z`).getTime();
|
|
79
|
+
return (now.getTime() - day) / 86_400_000 > exports.MAX_ENTRY_AGE_DAYS;
|
|
80
|
+
}
|
|
81
|
+
function isDue(entry, now) {
|
|
82
|
+
return entry.nextAttemptAt === null || entry.nextAttemptAt <= now.getTime();
|
|
83
|
+
}
|
|
84
|
+
/** How many commits ride in one POST — the endpoint's own cap. */
|
|
85
|
+
exports.MAX_COMMITS_PER_BATCH = 100;
|
|
86
|
+
/**
|
|
87
|
+
* Groups due entries into one POST per (day, project): the endpoint writes
|
|
88
|
+
* one amendment row per call, and one row per engagement-day is what the
|
|
89
|
+
* evening draft wants to merge.
|
|
90
|
+
*/
|
|
91
|
+
function batches(entries, maxPerBatch = exports.MAX_COMMITS_PER_BATCH) {
|
|
92
|
+
const groups = new Map();
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const key = `${entry.day}\u0000${entry.project}`;
|
|
95
|
+
const group = groups.get(key);
|
|
96
|
+
if (group)
|
|
97
|
+
group.push(entry);
|
|
98
|
+
else
|
|
99
|
+
groups.set(key, [entry]);
|
|
100
|
+
}
|
|
101
|
+
return [...groups.values()].flatMap((group) => {
|
|
102
|
+
const chunks = [];
|
|
103
|
+
for (let i = 0; i < group.length; i += maxPerBatch) {
|
|
104
|
+
chunks.push(group.slice(i, i + maxPerBatch));
|
|
105
|
+
}
|
|
106
|
+
return chunks;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* What an HTTP status means for the queue. 401 is its own verdict because
|
|
111
|
+
* it is the one failure a refresh can fix without the engineer; 4xx
|
|
112
|
+
* otherwise means the payload will never be accepted, and keeping it in the
|
|
113
|
+
* queue would only bury the working entries behind it.
|
|
114
|
+
*/
|
|
115
|
+
function classifyStatus(status) {
|
|
116
|
+
if (status >= 200 && status < 300)
|
|
117
|
+
return 'sent';
|
|
118
|
+
if (status === 401)
|
|
119
|
+
return 'reauth';
|
|
120
|
+
if (status === 408 || status === 429)
|
|
121
|
+
return 'retry';
|
|
122
|
+
if (status >= 400 && status < 500)
|
|
123
|
+
return 'rejected';
|
|
124
|
+
return 'retry';
|
|
125
|
+
}
|
|
126
|
+
/** A network error (no status at all) is always worth another try. */
|
|
127
|
+
exports.NETWORK_VERDICT = 'retry';
|
|
128
|
+
function applyFailure(entries, verdict, error, now) {
|
|
129
|
+
if (verdict === 'rejected')
|
|
130
|
+
return { keep: [], drop: entries.map((e) => ({ ...e, lastError: error })) };
|
|
131
|
+
const keep = entries.map((entry) => {
|
|
132
|
+
const attempts = entry.attempts + 1;
|
|
133
|
+
return {
|
|
134
|
+
...entry,
|
|
135
|
+
attempts,
|
|
136
|
+
// A re-auth failure waits on the engineer, not on a timer, but it
|
|
137
|
+
// still backs off so a broken refresh cannot spin on every commit.
|
|
138
|
+
nextAttemptAt: now.getTime() + backoffMs(attempts),
|
|
139
|
+
lastError: error,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
return { keep, drop: [] };
|
|
143
|
+
}
|
|
144
|
+
/** Replaces `original` entries with `replacement` (or removes them). */
|
|
145
|
+
function mergeQueue(queue, original, replacement = []) {
|
|
146
|
+
const removed = new Set(original.map((entry) => entry.id));
|
|
147
|
+
const kept = queue.filter((entry) => !removed.has(entry.id));
|
|
148
|
+
return [...kept, ...replacement];
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Files
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
function readJsonl(path) {
|
|
154
|
+
let raw;
|
|
155
|
+
try {
|
|
156
|
+
raw = (0, node_fs_1.readFileSync)(path, 'utf8');
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
return raw
|
|
162
|
+
.split('\n')
|
|
163
|
+
.filter((line) => line.trim().length > 0)
|
|
164
|
+
.flatMap((line) => {
|
|
165
|
+
try {
|
|
166
|
+
return [JSON.parse(line)];
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// A torn line (power loss mid-append) is one lost commit, not a
|
|
170
|
+
// broken queue.
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function readQueue(path) {
|
|
176
|
+
return readJsonl(path);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* True when the file's last byte is a newline — false for a file whose last
|
|
180
|
+
* append was cut short, which is the one case where appending blindly would
|
|
181
|
+
* corrupt a *second* line instead of just the torn one.
|
|
182
|
+
*/
|
|
183
|
+
function endsWithNewline(path) {
|
|
184
|
+
let handle;
|
|
185
|
+
try {
|
|
186
|
+
const size = (0, node_fs_1.statSync)(path).size;
|
|
187
|
+
if (size === 0)
|
|
188
|
+
return true;
|
|
189
|
+
handle = (0, node_fs_1.openSync)(path, 'r');
|
|
190
|
+
const last = Buffer.alloc(1);
|
|
191
|
+
(0, node_fs_1.readSync)(handle, last, 0, 1, size - 1);
|
|
192
|
+
return last[0] === 0x0a;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
if (handle !== undefined)
|
|
199
|
+
(0, node_fs_1.closeSync)(handle);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function appendEntry(path, entry) {
|
|
203
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
204
|
+
const prefix = endsWithNewline(path) ? '' : '\n';
|
|
205
|
+
(0, node_fs_1.appendFileSync)(path, `${prefix}${JSON.stringify(entry)}\n`, { mode: 0o600 });
|
|
206
|
+
}
|
|
207
|
+
/** Rewrites the queue atomically — a crash leaves the old file intact. */
|
|
208
|
+
function writeQueue(path, entries) {
|
|
209
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
210
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
211
|
+
(0, node_fs_1.writeFileSync)(temp, entries.map((entry) => `${JSON.stringify(entry)}\n`).join(''), {
|
|
212
|
+
mode: 0o600,
|
|
213
|
+
});
|
|
214
|
+
(0, node_fs_1.renameSync)(temp, path);
|
|
215
|
+
}
|
|
216
|
+
function appendOutcome(path, outcome) {
|
|
217
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
218
|
+
(0, node_fs_1.appendFileSync)(path, `${JSON.stringify(outcome)}\n`, { mode: 0o600 });
|
|
219
|
+
}
|
|
220
|
+
function readOutcomes(path) {
|
|
221
|
+
return readJsonl(path);
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/send.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.payloadForBatch = payloadForBatch;
|
|
4
|
+
exports.payloadForNote = payloadForNote;
|
|
5
|
+
exports.postAmendment = postAmendment;
|
|
6
|
+
exports.flush = flush;
|
|
7
|
+
const queue_1 = require("./queue");
|
|
8
|
+
/** One batch of queued commits as one `/amend` request body. */
|
|
9
|
+
function payloadForBatch(entries) {
|
|
10
|
+
return {
|
|
11
|
+
date: entries[0].day,
|
|
12
|
+
source: 'cli',
|
|
13
|
+
project: entries[0].project,
|
|
14
|
+
commits: entries.map((entry) => entry.commit),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function payloadForNote(note, day, project) {
|
|
18
|
+
return {
|
|
19
|
+
date: day,
|
|
20
|
+
source: 'manual',
|
|
21
|
+
...(project && { project }),
|
|
22
|
+
bullets: [note],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** One POST. Network failures come back as status 0, not as throws. */
|
|
26
|
+
async function postAmendment(amendUrl, payload, accessToken, fetchImpl = fetch) {
|
|
27
|
+
let response;
|
|
28
|
+
try {
|
|
29
|
+
response = await fetchImpl(amendUrl, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: {
|
|
32
|
+
authorization: `Bearer ${accessToken}`,
|
|
33
|
+
'content-type': 'application/json',
|
|
34
|
+
},
|
|
35
|
+
body: JSON.stringify(payload),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return { status: 0, error: err instanceof Error ? err.message : String(err) };
|
|
40
|
+
}
|
|
41
|
+
const text = await response.text();
|
|
42
|
+
let body = {};
|
|
43
|
+
try {
|
|
44
|
+
body = JSON.parse(text);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Non-JSON error bodies (a proxy, a 502 page) still carry a status.
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
status: response.status,
|
|
51
|
+
amendmentId: typeof body.id === 'string' ? body.id : undefined,
|
|
52
|
+
error: response.ok
|
|
53
|
+
? undefined
|
|
54
|
+
: typeof body.error === 'string'
|
|
55
|
+
? body.error
|
|
56
|
+
: text.slice(0, 200) || `HTTP ${response.status}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Attempts every due batch once (twice for a 401), persisting the queue
|
|
61
|
+
* after each one so a crash mid-flush cannot resend or lose a batch.
|
|
62
|
+
*/
|
|
63
|
+
async function flush({ paths, amendUrl, session, fetchImpl = fetch, now = () => new Date(), log = () => { }, }) {
|
|
64
|
+
const report = {
|
|
65
|
+
sent: 0,
|
|
66
|
+
commitsSent: 0,
|
|
67
|
+
deferred: 0,
|
|
68
|
+
rejected: 0,
|
|
69
|
+
expired: 0,
|
|
70
|
+
needsLogin: false,
|
|
71
|
+
errors: [],
|
|
72
|
+
};
|
|
73
|
+
let queue = (0, queue_1.readQueue)(paths.queue);
|
|
74
|
+
if (queue.length === 0)
|
|
75
|
+
return report;
|
|
76
|
+
const stale = queue.filter((entry) => (0, queue_1.isExpired)(entry, now()));
|
|
77
|
+
if (stale.length > 0) {
|
|
78
|
+
queue = (0, queue_1.mergeQueue)(queue, stale);
|
|
79
|
+
(0, queue_1.writeQueue)(paths.queue, queue);
|
|
80
|
+
(0, queue_1.appendOutcome)(paths.dead, {
|
|
81
|
+
entries: stale,
|
|
82
|
+
at: now().toISOString(),
|
|
83
|
+
status: 0,
|
|
84
|
+
error: `older than the endpoint's ${queue_1.MAX_ENTRY_AGE_DAYS}-day window — never sent`,
|
|
85
|
+
});
|
|
86
|
+
report.expired = stale.length;
|
|
87
|
+
}
|
|
88
|
+
if (!session.isLoggedIn) {
|
|
89
|
+
report.needsLogin = true;
|
|
90
|
+
report.deferred = (0, queue_1.batches)(queue.filter((entry) => (0, queue_1.isDue)(entry, now()))).length;
|
|
91
|
+
return report;
|
|
92
|
+
}
|
|
93
|
+
for (const batch of (0, queue_1.batches)(queue.filter((entry) => (0, queue_1.isDue)(entry, now())))) {
|
|
94
|
+
const payload = payloadForBatch(batch);
|
|
95
|
+
let token;
|
|
96
|
+
try {
|
|
97
|
+
token = await session.accessToken();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
report.needsLogin = true;
|
|
101
|
+
report.deferred += 1;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
let result = await postAmendment(amendUrl, payload, token, fetchImpl);
|
|
105
|
+
let verdict = result.status === 0 ? 'retry' : (0, queue_1.classifyStatus)(result.status);
|
|
106
|
+
if (verdict === 'reauth') {
|
|
107
|
+
// The one failure the CLI can fix by itself — once per batch.
|
|
108
|
+
const refreshed = await session.refresh();
|
|
109
|
+
if (refreshed) {
|
|
110
|
+
result = await postAmendment(amendUrl, payload, refreshed, fetchImpl);
|
|
111
|
+
verdict = result.status === 0 ? 'retry' : (0, queue_1.classifyStatus)(result.status);
|
|
112
|
+
}
|
|
113
|
+
if (verdict === 'reauth')
|
|
114
|
+
report.needsLogin = true;
|
|
115
|
+
}
|
|
116
|
+
if (verdict === 'sent') {
|
|
117
|
+
queue = (0, queue_1.mergeQueue)(queue, batch);
|
|
118
|
+
(0, queue_1.writeQueue)(paths.queue, queue);
|
|
119
|
+
(0, queue_1.appendOutcome)(paths.sent, {
|
|
120
|
+
entries: batch,
|
|
121
|
+
at: now().toISOString(),
|
|
122
|
+
status: result.status,
|
|
123
|
+
amendmentId: result.amendmentId,
|
|
124
|
+
});
|
|
125
|
+
report.sent += 1;
|
|
126
|
+
report.commitsSent += batch.length;
|
|
127
|
+
log(`sent ${batch.length} commit(s) for ${payload.date} (${payload.project})`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const error = result.error ?? `HTTP ${result.status}`;
|
|
131
|
+
const { keep, drop } = (0, queue_1.applyFailure)(batch, verdict, error, now());
|
|
132
|
+
queue = (0, queue_1.mergeQueue)(queue, batch, keep);
|
|
133
|
+
(0, queue_1.writeQueue)(paths.queue, queue);
|
|
134
|
+
if (drop.length > 0) {
|
|
135
|
+
(0, queue_1.appendOutcome)(paths.dead, {
|
|
136
|
+
entries: drop,
|
|
137
|
+
at: now().toISOString(),
|
|
138
|
+
status: result.status,
|
|
139
|
+
error,
|
|
140
|
+
});
|
|
141
|
+
report.rejected += drop.length;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
report.deferred += 1;
|
|
145
|
+
}
|
|
146
|
+
report.errors.push(error);
|
|
147
|
+
log(`deferred ${batch.length} commit(s) for ${payload.date}: ${error}`);
|
|
148
|
+
}
|
|
149
|
+
return report;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=send.js.map
|
package/dist/send.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Session = exports.NotLoggedInError = void 0;
|
|
4
|
+
exports.storedFrom = storedFrom;
|
|
5
|
+
/**
|
|
6
|
+
* The access token every send needs, refreshed when it has aged out.
|
|
7
|
+
*
|
|
8
|
+
* Refresh happens in two places on purpose: proactively when the stored
|
|
9
|
+
* expiry has passed (cheap, one round trip), and reactively when the
|
|
10
|
+
* endpoint answers 401 anyway — because the CLI's clock, the stored
|
|
11
|
+
* `expires_in` and the issuer's opinion can disagree, and a queue that
|
|
12
|
+
* silently stops sending is exactly the failure mode DATA-ITD-005 is
|
|
13
|
+
* trying to avoid.
|
|
14
|
+
*
|
|
15
|
+
* A refresh that succeeds writes the rotated pair straight back to the
|
|
16
|
+
* keychain: OpenAuth issues a new refresh token on every exchange, so
|
|
17
|
+
* losing the rotated one means the engineer has to sign in again.
|
|
18
|
+
*/
|
|
19
|
+
const pkce_1 = require("./pkce");
|
|
20
|
+
class NotLoggedInError extends Error {
|
|
21
|
+
constructor(message = 'not signed in — run `scribe login`') {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = 'NotLoggedInError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.NotLoggedInError = NotLoggedInError;
|
|
27
|
+
function storedFrom(tokens, issuer, now) {
|
|
28
|
+
return {
|
|
29
|
+
...tokens,
|
|
30
|
+
issuer,
|
|
31
|
+
email: (0, pkce_1.readAccessToken)(tokens.access).email,
|
|
32
|
+
savedAt: new Date(now()).toISOString(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Holds the credentials for one process run: reads them once, refreshes at
|
|
37
|
+
* most once per reason, and persists whatever it rotates.
|
|
38
|
+
*/
|
|
39
|
+
class Session {
|
|
40
|
+
issuer;
|
|
41
|
+
store;
|
|
42
|
+
fetchImpl;
|
|
43
|
+
now;
|
|
44
|
+
current;
|
|
45
|
+
constructor({ issuer, store, fetchImpl = fetch, now = Date.now }) {
|
|
46
|
+
this.issuer = issuer;
|
|
47
|
+
this.store = store;
|
|
48
|
+
this.fetchImpl = fetchImpl;
|
|
49
|
+
this.now = now;
|
|
50
|
+
this.current = store.read();
|
|
51
|
+
// Tokens from another stage's issuer are not credentials here.
|
|
52
|
+
if (this.current && this.current.issuer !== issuer)
|
|
53
|
+
this.current = null;
|
|
54
|
+
}
|
|
55
|
+
get email() {
|
|
56
|
+
return this.current?.email ?? null;
|
|
57
|
+
}
|
|
58
|
+
get isLoggedIn() {
|
|
59
|
+
return this.current !== null;
|
|
60
|
+
}
|
|
61
|
+
get expiresAt() {
|
|
62
|
+
return this.current?.expiresAt ?? null;
|
|
63
|
+
}
|
|
64
|
+
/** The token to send, refreshing first if it has expired. */
|
|
65
|
+
async accessToken() {
|
|
66
|
+
if (!this.current)
|
|
67
|
+
throw new NotLoggedInError();
|
|
68
|
+
if ((0, pkce_1.needsRefresh)(this.current, this.now())) {
|
|
69
|
+
const refreshed = await this.refresh();
|
|
70
|
+
if (refreshed)
|
|
71
|
+
return refreshed;
|
|
72
|
+
}
|
|
73
|
+
return this.current.access;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Forces a refresh — what a 401 means — and returns the new access token.
|
|
77
|
+
* Null means the refresh token itself is gone or rejected, the only state
|
|
78
|
+
* that needs the engineer back at a browser.
|
|
79
|
+
*/
|
|
80
|
+
async refresh() {
|
|
81
|
+
if (!this.current)
|
|
82
|
+
return null;
|
|
83
|
+
try {
|
|
84
|
+
const rotated = await (0, pkce_1.refreshTokens)({
|
|
85
|
+
issuer: this.issuer,
|
|
86
|
+
refresh: this.current.refresh,
|
|
87
|
+
fetchImpl: this.fetchImpl,
|
|
88
|
+
now: this.now,
|
|
89
|
+
});
|
|
90
|
+
this.current = storedFrom(rotated, this.issuer, this.now);
|
|
91
|
+
this.store.write(this.current);
|
|
92
|
+
return this.current.access;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Records a fresh login. */
|
|
99
|
+
save(tokens) {
|
|
100
|
+
this.current = storedFrom(tokens, this.issuer, this.now);
|
|
101
|
+
this.store.write(this.current);
|
|
102
|
+
return this.current;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.Session = Session;
|
|
106
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":";;;AA+BA,gCAOC;AAtCD;;;;;;;;;;;;;GAaG;AACH,iCAA+F;AAG/F,MAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,GAAG,oCAAoC;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AASD,SAAgB,UAAU,CAAC,MAAc,EAAE,MAAc,EAAE,GAAiB;IAC1E,OAAO;QACL,GAAG,MAAM;QACT,MAAM;QACN,KAAK,EAAE,IAAA,sBAAe,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK;QAC3C,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;KACvC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAa,OAAO;IACD,MAAM,CAAS;IACf,KAAK,CAAa;IAClB,SAAS,CAAQ;IACjB,GAAG,CAAe;IAC3B,OAAO,CAAsB;IAErC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAkB;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5B,+DAA+D;QAC/D,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IAC1E,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;IACzC,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,WAAW;QACf,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,gBAAgB,EAAE,CAAC;QAChD,IAAI,IAAA,mBAAY,EAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC;QAClC,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAA,oBAAa,EAAC;gBAClC,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC,CAAC;YACH,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;YAC1D,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,6BAA6B;IAC7B,IAAI,CAAC,MAAc;QACjB,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAnED,0BAmEC"}
|