@rubytech/create-maxy-code 0.1.474 → 0.1.475
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/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -7
- package/payload/platform/plugins/docs/references/google-guide.md +15 -6
- package/payload/platform/plugins/google/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/google/PLUGIN.md +60 -12
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.js +229 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-audit.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js +9 -9
- package/payload/platform/plugins/google/mcp/dist/__tests__/account-register.test.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.js +17 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/consent-url-redirect.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.js +146 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/loopback-listener.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.js +19 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/pending-redirect.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.js +41 -0
- package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.d.ts +44 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.js +112 -0
- package/payload/platform/plugins/google/mcp/dist/auth/account-audit.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.d.ts +17 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.js +143 -0
- package/payload/platform/plugins/google/mcp/dist/auth/loopback-listener.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts +9 -0
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/auth/pending-store.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/index.js +73 -5
- package/payload/platform/plugins/google/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.d.ts +2 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.d.ts.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.js +37 -0
- package/payload/platform/plugins/google/mcp/dist/scripts/account-audit.js.map +1 -0
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts +20 -4
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.d.ts.map +1 -1
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js +19 -9
- package/payload/platform/plugins/google/mcp/dist/tools/account-register.js.map +1 -1
- package/payload/platform/plugins/google/references/auth.md +93 -14
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +21 -3
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js +6 -2
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +43 -5
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js +78 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-enumerator.js.map +1 -1
- package/payload/server/public/activity.html +1 -1
- package/payload/server/public/assets/activity-Mz1BhgB1.js +1 -0
- package/payload/server/server.js +368 -300
- package/payload/server/public/assets/activity-CdipMTDp.js +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { startLoopbackListener } from "../auth/loopback-listener.js";
|
|
5
|
+
async function get(url) {
|
|
6
|
+
const res = await fetch(url);
|
|
7
|
+
return { status: res.status, body: await res.text() };
|
|
8
|
+
}
|
|
9
|
+
/** A port is only proven released by rebinding it. A boolean the code under
|
|
10
|
+
* test sets would prove the code ran, not that the socket closed. */
|
|
11
|
+
async function isFree(port) {
|
|
12
|
+
return await new Promise((resolve) => {
|
|
13
|
+
const s = createServer();
|
|
14
|
+
s.once("error", () => resolve(false));
|
|
15
|
+
s.listen(port, "127.0.0.1", () => s.close(() => resolve(true)));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
test("a matching state completes and the page names the account", async () => {
|
|
19
|
+
let seen = "";
|
|
20
|
+
const l = await startLoopbackListener({
|
|
21
|
+
accountId: "a",
|
|
22
|
+
state: "st-1",
|
|
23
|
+
onCode: async (code) => {
|
|
24
|
+
seen = code;
|
|
25
|
+
return { email: "kit@example.com" };
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
assert.ok(l);
|
|
29
|
+
const r = await get(`${l.redirectUri}/?state=st-1&code=xyz`);
|
|
30
|
+
assert.equal(r.status, 200);
|
|
31
|
+
assert.equal(seen, "xyz");
|
|
32
|
+
assert.match(r.body, /kit@example\.com/);
|
|
33
|
+
await l.close("test");
|
|
34
|
+
});
|
|
35
|
+
test("a mismatched state is refused and onCode never runs", async () => {
|
|
36
|
+
let ran = false;
|
|
37
|
+
const l = await startLoopbackListener({
|
|
38
|
+
accountId: "a",
|
|
39
|
+
state: "st-2",
|
|
40
|
+
onCode: async () => {
|
|
41
|
+
ran = true;
|
|
42
|
+
return { email: "x@y.z" };
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
assert.ok(l);
|
|
46
|
+
const r = await get(`${l.redirectUri}/?state=WRONG&code=xyz`);
|
|
47
|
+
assert.equal(r.status, 400);
|
|
48
|
+
assert.equal(ran, false);
|
|
49
|
+
await l.close("test");
|
|
50
|
+
});
|
|
51
|
+
test("an onCode failure renders Google's error rather than claiming success", async () => {
|
|
52
|
+
const l = await startLoopbackListener({
|
|
53
|
+
accountId: "a",
|
|
54
|
+
state: "st-3",
|
|
55
|
+
onCode: async () => {
|
|
56
|
+
throw new Error("token exchange failed (400): invalid_grant");
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
assert.ok(l);
|
|
60
|
+
const r = await get(`${l.redirectUri}/?state=st-3&code=xyz`);
|
|
61
|
+
assert.equal(r.status, 400);
|
|
62
|
+
assert.match(r.body, /invalid_grant/);
|
|
63
|
+
await l.close("test");
|
|
64
|
+
});
|
|
65
|
+
test("close releases the port", async () => {
|
|
66
|
+
const l = await startLoopbackListener({
|
|
67
|
+
accountId: "a",
|
|
68
|
+
state: "st-4",
|
|
69
|
+
onCode: async () => ({ email: "x@y.z" }),
|
|
70
|
+
});
|
|
71
|
+
assert.ok(l);
|
|
72
|
+
const port = l.port;
|
|
73
|
+
await l.close("test");
|
|
74
|
+
assert.equal(await isFree(port), true);
|
|
75
|
+
});
|
|
76
|
+
test("a completed exchange releases the port without an explicit close", async () => {
|
|
77
|
+
const l = await startLoopbackListener({
|
|
78
|
+
accountId: "a",
|
|
79
|
+
state: "st-5",
|
|
80
|
+
onCode: async () => ({ email: "x@y.z" }),
|
|
81
|
+
});
|
|
82
|
+
assert.ok(l);
|
|
83
|
+
const port = l.port;
|
|
84
|
+
await get(`${l.redirectUri}/?state=st-5&code=xyz`);
|
|
85
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
86
|
+
assert.equal(await isFree(port), true);
|
|
87
|
+
});
|
|
88
|
+
test("a reload during the exchange does not run onCode twice", async () => {
|
|
89
|
+
let calls = 0;
|
|
90
|
+
const l = await startLoopbackListener({
|
|
91
|
+
accountId: "a",
|
|
92
|
+
state: "st-7",
|
|
93
|
+
onCode: async () => {
|
|
94
|
+
calls += 1;
|
|
95
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
96
|
+
return { email: "x@y.z" };
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
assert.ok(l);
|
|
100
|
+
const first = get(`${l.redirectUri}/?state=st-7&code=xyz`);
|
|
101
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
102
|
+
const second = await get(`${l.redirectUri}/?state=st-7&code=xyz`);
|
|
103
|
+
await first;
|
|
104
|
+
assert.equal(calls, 1, "the second request must not re-exchange the code");
|
|
105
|
+
assert.equal(second.status, 409);
|
|
106
|
+
});
|
|
107
|
+
test("a denied consent says so rather than blaming the link", async () => {
|
|
108
|
+
let ran = false;
|
|
109
|
+
const l = await startLoopbackListener({
|
|
110
|
+
accountId: "a",
|
|
111
|
+
state: "st-8",
|
|
112
|
+
onCode: async () => {
|
|
113
|
+
ran = true;
|
|
114
|
+
return { email: "x@y.z" };
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
assert.ok(l);
|
|
118
|
+
const r = await get(`${l.redirectUri}/?state=st-8&error=access_denied`);
|
|
119
|
+
assert.equal(r.status, 400);
|
|
120
|
+
assert.match(r.body, /access_denied/);
|
|
121
|
+
assert.doesNotMatch(r.body, /did not match/);
|
|
122
|
+
assert.equal(ran, false);
|
|
123
|
+
});
|
|
124
|
+
test("responses keep the code out of caches and referrers", async () => {
|
|
125
|
+
const l = await startLoopbackListener({
|
|
126
|
+
accountId: "a",
|
|
127
|
+
state: "st-9",
|
|
128
|
+
onCode: async () => ({ email: "x@y.z" }),
|
|
129
|
+
});
|
|
130
|
+
assert.ok(l);
|
|
131
|
+
const res = await fetch(`${l.redirectUri}/?state=st-9&code=xyz`);
|
|
132
|
+
assert.equal(res.headers.get("cache-control"), "no-store");
|
|
133
|
+
assert.equal(res.headers.get("referrer-policy"), "no-referrer");
|
|
134
|
+
await l.close("test");
|
|
135
|
+
});
|
|
136
|
+
test("the listener binds the loopback literal, never localhost", async () => {
|
|
137
|
+
const l = await startLoopbackListener({
|
|
138
|
+
accountId: "a",
|
|
139
|
+
state: "st-6",
|
|
140
|
+
onCode: async () => ({ email: "x@y.z" }),
|
|
141
|
+
});
|
|
142
|
+
assert.ok(l);
|
|
143
|
+
assert.match(l.redirectUri, /^http:\/\/127\.0\.0\.1:\d+$/);
|
|
144
|
+
await l.close("test");
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=loopback-listener.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback-listener.test.js","sourceRoot":"","sources":["../../src/__tests__/loopback-listener.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-redirect.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pending-redirect.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { PendingStore } from "../auth/pending-store.js";
|
|
7
|
+
test("a pending entry round-trips its redirectUri", () => {
|
|
8
|
+
const dir = mkdtempSync(join(tmpdir(), "g-"));
|
|
9
|
+
const p = new PendingStore("acct-1", dir);
|
|
10
|
+
p.write({ state: "s1", verifier: "v1", createdAt: Date.now(), redirectUri: "http://127.0.0.1:54321" });
|
|
11
|
+
assert.equal(p.read()?.redirectUri, "http://127.0.0.1:54321");
|
|
12
|
+
});
|
|
13
|
+
test("a legacy pending entry with no redirectUri still reads", () => {
|
|
14
|
+
const dir = mkdtempSync(join(tmpdir(), "g-"));
|
|
15
|
+
const p = new PendingStore("acct-2", dir);
|
|
16
|
+
p.write({ state: "s2", verifier: "v2", createdAt: Date.now() });
|
|
17
|
+
assert.equal(p.read()?.redirectUri, undefined);
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=pending-redirect.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-redirect.test.js","sourceRoot":"","sources":["../../src/__tests__/pending-redirect.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC,CAAC;IACvG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,wBAAwB,CAAC,CAAC;AAChE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAClE,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC"}
|
package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-redirect-chain.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/register-redirect-chain.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { newState, runAccountRegisterStart } from "../tools/account-register.js";
|
|
7
|
+
import { PendingStore } from "../auth/pending-store.js";
|
|
8
|
+
/** The consent URL, the pending entry and therefore the exchange must all
|
|
9
|
+
* carry one redirect. Google compares the exchange's value byte-for-byte
|
|
10
|
+
* against the one that issued the code, so a divergence here is the
|
|
11
|
+
* redirect_uri_mismatch that reads like a wrong client type. */
|
|
12
|
+
test("the consent URL and the pending entry carry the same redirect", async () => {
|
|
13
|
+
const dir = mkdtempSync(join(tmpdir(), "g-"));
|
|
14
|
+
const r = await runAccountRegisterStart({
|
|
15
|
+
accountId: "acct",
|
|
16
|
+
accountsDir: dir,
|
|
17
|
+
clientId: "cid",
|
|
18
|
+
state: newState(),
|
|
19
|
+
redirectUri: "http://127.0.0.1:54321",
|
|
20
|
+
});
|
|
21
|
+
assert.equal(r.status, "pending");
|
|
22
|
+
const inUrl = new URL(r.consentUrl).searchParams.get("redirect_uri");
|
|
23
|
+
const inPending = new PendingStore("acct", dir).read()?.redirectUri;
|
|
24
|
+
assert.equal(inUrl, "http://127.0.0.1:54321");
|
|
25
|
+
assert.equal(inPending, "http://127.0.0.1:54321");
|
|
26
|
+
assert.equal(inUrl, inPending);
|
|
27
|
+
});
|
|
28
|
+
test("the state minted by the caller is the state persisted and sent", async () => {
|
|
29
|
+
const dir = mkdtempSync(join(tmpdir(), "g-"));
|
|
30
|
+
const state = newState();
|
|
31
|
+
const r = await runAccountRegisterStart({
|
|
32
|
+
accountId: "acct",
|
|
33
|
+
accountsDir: dir,
|
|
34
|
+
clientId: "cid",
|
|
35
|
+
state,
|
|
36
|
+
redirectUri: "http://127.0.0.1:1234",
|
|
37
|
+
});
|
|
38
|
+
assert.equal(new URL(r.consentUrl).searchParams.get("state"), state);
|
|
39
|
+
assert.equal(new PendingStore("acct", dir).read()?.state, state);
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=register-redirect-chain.test.js.map
|
package/payload/platform/plugins/google/mcp/dist/__tests__/register-redirect-chain.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-redirect-chain.test.js","sourceRoot":"","sources":["../../src/__tests__/register-redirect-chain.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;iEAGiE;AACjE,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;IAC/E,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,MAAM,uBAAuB,CAAC;QACtC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,GAAG;QAChB,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,QAAQ,EAAE;QACjB,WAAW,EAAE,wBAAwB;KACtC,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,UAAW,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,CAAC;IACpE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,wBAAwB,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,wBAAwB,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;IAChF,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,CAAC,GAAG,MAAM,uBAAuB,CAAC;QACtC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,GAAG;QAChB,QAAQ,EAAE,KAAK;QACf,KAAK;QACL,WAAW,EAAE,uBAAuB;KACrC,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,UAAW,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IACtE,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type TokenBlob } from "./token-store.js";
|
|
2
|
+
/** Google revokes a refresh token after six months without use. */
|
|
3
|
+
export declare const REFRESH_INACTIVITY_LIMIT_DAYS = 180;
|
|
4
|
+
/** How much of the inactivity clock must remain before an account is merely
|
|
5
|
+
* `ok`. Thirty days is the window in which an operator can still act. */
|
|
6
|
+
export declare const REFRESH_EXPIRING_THRESHOLD_DAYS = 30;
|
|
7
|
+
export type RefreshStatus = "ok" | "expiring" | "expired";
|
|
8
|
+
export interface AccountAuditRow {
|
|
9
|
+
accountId: string;
|
|
10
|
+
email: string;
|
|
11
|
+
refreshTtlDays: number;
|
|
12
|
+
status: RefreshStatus;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Classify one token blob against the inactivity clock.
|
|
16
|
+
*
|
|
17
|
+
* A blob holding NO refresh token is `expired` with zero days regardless of how
|
|
18
|
+
* recently it was written: it works until the access token lapses, about an hour
|
|
19
|
+
* out, and then dies. Reporting it as `ok` because `lastRefresh` is recent would
|
|
20
|
+
* be precisely the false all-clear this audit exists to prevent.
|
|
21
|
+
*/
|
|
22
|
+
export declare function classifyRefresh(blob: TokenBlob, nowMs: number): {
|
|
23
|
+
refreshTtlDays: number;
|
|
24
|
+
status: RefreshStatus;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Every connected Google account under every account, classified.
|
|
28
|
+
*
|
|
29
|
+
* An unreadable blob is skipped rather than thrown: one corrupt store must never
|
|
30
|
+
* blind the audit to every other account. It is not reported as a row because it
|
|
31
|
+
* has no TTL to report — `google-account-list` is the surface that already
|
|
32
|
+
* distinguishes `unreadable` from healthy.
|
|
33
|
+
*/
|
|
34
|
+
export declare function auditConnectedAccounts(accountsDir: string, nowMs: number): AccountAuditRow[];
|
|
35
|
+
/**
|
|
36
|
+
* One audit cycle: a line per connected account, then a heartbeat.
|
|
37
|
+
*
|
|
38
|
+
* The heartbeat fires on EVERY cycle, including cycles that find nothing. An
|
|
39
|
+
* audit that has stopped running would otherwise be indistinguishable from an
|
|
40
|
+
* audit reporting all-clear, and the standing absence of this line over more
|
|
41
|
+
* than one interval is the documented signal that the audit is not running.
|
|
42
|
+
*/
|
|
43
|
+
export declare function runAccountAudit(accountsDir: string, nowMs: number, emit: (line: string) => void): AccountAuditRow[];
|
|
44
|
+
//# sourceMappingURL=account-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-audit.d.ts","sourceRoot":"","sources":["../../src/auth/account-audit.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE9D,mEAAmE;AACnE,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD;0EAC0E;AAC1E,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAMlD,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,UAAU,GAAG,SAAS,CAAC;AAW1D,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,MAAM,GACZ;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAWnD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAkB5F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC3B,eAAe,EAAE,CAYnB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standing refresh-token audit (Task 1773).
|
|
3
|
+
*
|
|
4
|
+
* A connected account's refresh token expiring emits no event and does not
|
|
5
|
+
* reproduce on demand: nothing touches the account, it simply stops working at
|
|
6
|
+
* some future date. Without a periodic reconciliation, "which connected accounts
|
|
7
|
+
* are close to falling off?" is unanswerable without reading every token store by
|
|
8
|
+
* hand. This module is that reconciliation.
|
|
9
|
+
*
|
|
10
|
+
* WHAT `refreshTtlDays` MEANS. Google issues no expiry for a refresh token and
|
|
11
|
+
* the stored blob holds none — `TokenBlob` carries `accessTokenExpiry` and
|
|
12
|
+
* `lastRefresh`, nothing more. The one bounded, locally-computable clock is
|
|
13
|
+
* Google's six-month INACTIVITY revocation: a refresh token unused for 180 days
|
|
14
|
+
* is revoked. So `refreshTtlDays` is days remaining on that inactivity clock,
|
|
15
|
+
* measured from `lastRefresh`. It is NOT a expiry Google published, and it does
|
|
16
|
+
* not predict the other revocation causes (the user revoking access, a password
|
|
17
|
+
* change, or the app being unpublished), none of which are locally observable.
|
|
18
|
+
*
|
|
19
|
+
* Runs against local stores only — no network. It reads what is on disk and
|
|
20
|
+
* classifies it, so it cannot itself break a working connection.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { TokenStore } from "./token-store.js";
|
|
25
|
+
/** Google revokes a refresh token after six months without use. */
|
|
26
|
+
export const REFRESH_INACTIVITY_LIMIT_DAYS = 180;
|
|
27
|
+
/** How much of the inactivity clock must remain before an account is merely
|
|
28
|
+
* `ok`. Thirty days is the window in which an operator can still act. */
|
|
29
|
+
export const REFRESH_EXPIRING_THRESHOLD_DAYS = 30;
|
|
30
|
+
const DAY_MS = 86_400_000;
|
|
31
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32
|
+
/** The audit line is space-delimited `k=v`, so a value carrying whitespace would
|
|
33
|
+
* split into bogus fields and a reader would mis-parse the record for exactly
|
|
34
|
+
* the account most likely to need attention. The directory name is already
|
|
35
|
+
* folded by `sanitizeAccountKey`, but `blob.email` comes from Google's
|
|
36
|
+
* primary-calendar id and is never sanitized on the way in. */
|
|
37
|
+
function logSafe(value) {
|
|
38
|
+
return value.replace(/\s+/g, "_");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Classify one token blob against the inactivity clock.
|
|
42
|
+
*
|
|
43
|
+
* A blob holding NO refresh token is `expired` with zero days regardless of how
|
|
44
|
+
* recently it was written: it works until the access token lapses, about an hour
|
|
45
|
+
* out, and then dies. Reporting it as `ok` because `lastRefresh` is recent would
|
|
46
|
+
* be precisely the false all-clear this audit exists to prevent.
|
|
47
|
+
*/
|
|
48
|
+
export function classifyRefresh(blob, nowMs) {
|
|
49
|
+
if (blob.refreshToken === null)
|
|
50
|
+
return { refreshTtlDays: 0, status: "expired" };
|
|
51
|
+
// Clamp idleDays at 0 so a lastRefresh in the future — a device whose clock ran
|
|
52
|
+
// ahead, or a blob restored from a host whose clock did — cannot report more
|
|
53
|
+
// days remaining than the limit this module documents as the maximum.
|
|
54
|
+
// `PendingStore.ageSec` clamps its analogous value for the same reason.
|
|
55
|
+
const idleDays = Math.max(0, Math.floor((nowMs - blob.lastRefresh) / DAY_MS));
|
|
56
|
+
const refreshTtlDays = REFRESH_INACTIVITY_LIMIT_DAYS - idleDays;
|
|
57
|
+
if (refreshTtlDays <= 0)
|
|
58
|
+
return { refreshTtlDays, status: "expired" };
|
|
59
|
+
if (refreshTtlDays <= REFRESH_EXPIRING_THRESHOLD_DAYS)
|
|
60
|
+
return { refreshTtlDays, status: "expiring" };
|
|
61
|
+
return { refreshTtlDays, status: "ok" };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Every connected Google account under every account, classified.
|
|
65
|
+
*
|
|
66
|
+
* An unreadable blob is skipped rather than thrown: one corrupt store must never
|
|
67
|
+
* blind the audit to every other account. It is not reported as a row because it
|
|
68
|
+
* has no TTL to report — `google-account-list` is the surface that already
|
|
69
|
+
* distinguishes `unreadable` from healthy.
|
|
70
|
+
*/
|
|
71
|
+
export function auditConnectedAccounts(accountsDir, nowMs) {
|
|
72
|
+
if (!existsSync(accountsDir))
|
|
73
|
+
return [];
|
|
74
|
+
const rows = [];
|
|
75
|
+
for (const accountId of readdirSync(accountsDir)) {
|
|
76
|
+
if (!UUID_RE.test(accountId))
|
|
77
|
+
continue;
|
|
78
|
+
const connectedDir = join(accountsDir, accountId, "secrets", "google", "accounts");
|
|
79
|
+
if (!existsSync(connectedDir))
|
|
80
|
+
continue;
|
|
81
|
+
for (const key of readdirSync(connectedDir)) {
|
|
82
|
+
try {
|
|
83
|
+
const blob = new TokenStore(accountId, accountsDir, key).read();
|
|
84
|
+
if (!blob)
|
|
85
|
+
continue;
|
|
86
|
+
rows.push({ accountId, email: logSafe(blob.email ?? key), ...classifyRefresh(blob, nowMs) });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* unreadable store — skip, never blind the whole scan */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return rows;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* One audit cycle: a line per connected account, then a heartbeat.
|
|
97
|
+
*
|
|
98
|
+
* The heartbeat fires on EVERY cycle, including cycles that find nothing. An
|
|
99
|
+
* audit that has stopped running would otherwise be indistinguishable from an
|
|
100
|
+
* audit reporting all-clear, and the standing absence of this line over more
|
|
101
|
+
* than one interval is the documented signal that the audit is not running.
|
|
102
|
+
*/
|
|
103
|
+
export function runAccountAudit(accountsDir, nowMs, emit) {
|
|
104
|
+
const rows = auditConnectedAccounts(accountsDir, nowMs);
|
|
105
|
+
for (const r of rows) {
|
|
106
|
+
emit(`[google-account-audit] account=${r.accountId} email=${r.email} refreshTtlDays=${r.refreshTtlDays} status=${r.status}`);
|
|
107
|
+
}
|
|
108
|
+
const count = (s) => rows.filter((r) => r.status === s).length;
|
|
109
|
+
emit(`[google-account-audit] op=heartbeat accounts=${rows.length} ok=${count("ok")} expiring=${count("expiring")} expired=${count("expired")}`);
|
|
110
|
+
return rows;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=account-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-audit.js","sourceRoot":"","sources":["../../src/auth/account-audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface LoopbackListener {
|
|
2
|
+
readonly redirectUri: string;
|
|
3
|
+
readonly port: number;
|
|
4
|
+
close(reason: string): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
/** Resolves null when the bind fails, so the caller falls back to the fixed
|
|
7
|
+
* redirect and the paste path still completes the registration. A refusal
|
|
8
|
+
* here would turn an optimisation into a regression on any install that
|
|
9
|
+
* cannot bind. */
|
|
10
|
+
export declare function startLoopbackListener(args: {
|
|
11
|
+
accountId: string;
|
|
12
|
+
state: string;
|
|
13
|
+
onCode: (code: string) => Promise<{
|
|
14
|
+
email: string;
|
|
15
|
+
}>;
|
|
16
|
+
}): Promise<LoopbackListener | null>;
|
|
17
|
+
//# sourceMappingURL=loopback-listener.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback-listener.d.ts","sourceRoot":"","sources":["../../src/auth/loopback-listener.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AA0BD;;;mBAGmB;AACnB,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6HnC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-shot loopback listener for the Desktop-client consent redirect.
|
|
3
|
+
*
|
|
4
|
+
* Bound to 127.0.0.1 on an OS-assigned port. RFC 8252 §7.3 makes the port
|
|
5
|
+
* irrelevant when Google matches a loopback redirect, so binding :0 costs
|
|
6
|
+
* nothing and avoids the contention two co-resident brands would have on a
|
|
7
|
+
* fixed port. The IP literal is mandatory — that port-agnostic matching does
|
|
8
|
+
* NOT extend to the hostname `localhost`.
|
|
9
|
+
*
|
|
10
|
+
* This is reached only when consent happens on the install's own host. When
|
|
11
|
+
* the user consents on a phone, nothing reaches it and the paste path
|
|
12
|
+
* completes the registration instead. That is the designed cross-device route,
|
|
13
|
+
* not a failure of this listener.
|
|
14
|
+
*
|
|
15
|
+
* The module knows nothing about Google or tokens: it captures a code,
|
|
16
|
+
* verifies `state`, hands off, renders, and closes.
|
|
17
|
+
*/
|
|
18
|
+
import { createServer } from "node:http";
|
|
19
|
+
import { log } from "../lib/log.js";
|
|
20
|
+
function escapeHtml(s) {
|
|
21
|
+
return s
|
|
22
|
+
.replace(/&/g, "&")
|
|
23
|
+
.replace(/</g, "<")
|
|
24
|
+
.replace(/>/g, ">")
|
|
25
|
+
.replace(/"/g, """);
|
|
26
|
+
}
|
|
27
|
+
/** The URL of these responses carries an authorization code. RFC 8252 §8.1
|
|
28
|
+
* wants it kept out of caches and out of any Referer. The page is
|
|
29
|
+
* self-contained today so no request can carry one, but a later edit adding a
|
|
30
|
+
* link or an image would turn that into a code leak. */
|
|
31
|
+
const HEADERS = {
|
|
32
|
+
"content-type": "text/html",
|
|
33
|
+
"cache-control": "no-store",
|
|
34
|
+
"referrer-policy": "no-referrer",
|
|
35
|
+
};
|
|
36
|
+
function page(title, body) {
|
|
37
|
+
return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
|
|
38
|
+
<style>body{font-family:system-ui,sans-serif;max-width:34rem;margin:4rem auto;padding:0 1rem;line-height:1.5}
|
|
39
|
+
h1{font-size:1.3rem}</style></head><body><h1>${title}</h1>${body}</body></html>`;
|
|
40
|
+
}
|
|
41
|
+
/** Resolves null when the bind fails, so the caller falls back to the fixed
|
|
42
|
+
* redirect and the paste path still completes the registration. A refusal
|
|
43
|
+
* here would turn an optimisation into a regression on any install that
|
|
44
|
+
* cannot bind. */
|
|
45
|
+
export async function startLoopbackListener(args) {
|
|
46
|
+
const server = createServer();
|
|
47
|
+
const port = await new Promise((resolve) => {
|
|
48
|
+
server.once("error", (err) => {
|
|
49
|
+
log({
|
|
50
|
+
tag: "[google-oauth]",
|
|
51
|
+
op: "listen-failed",
|
|
52
|
+
account: args.accountId,
|
|
53
|
+
reg: args.state,
|
|
54
|
+
reason: err.code ?? err.message,
|
|
55
|
+
});
|
|
56
|
+
resolve(null);
|
|
57
|
+
});
|
|
58
|
+
server.listen(0, "127.0.0.1", () => {
|
|
59
|
+
const addr = server.address();
|
|
60
|
+
resolve(typeof addr === "object" && addr !== null ? addr.port : null);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
if (port === null)
|
|
64
|
+
return null;
|
|
65
|
+
const redirectUri = `http://127.0.0.1:${port}`;
|
|
66
|
+
let closed = false;
|
|
67
|
+
const close = (reason) => new Promise((resolve) => {
|
|
68
|
+
if (closed)
|
|
69
|
+
return resolve();
|
|
70
|
+
closed = true;
|
|
71
|
+
server.close(() => {
|
|
72
|
+
log({ tag: "[google-oauth]", op: "close", reg: args.state, account: args.accountId, reason });
|
|
73
|
+
resolve();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
/** Single-shot is enforced at accept time, not by closing after the
|
|
77
|
+
* exchange. `server.close()` stops new connections but the socket keeps
|
|
78
|
+
* accepting until the in-flight response ends, so a browser reload during
|
|
79
|
+
* the ~1s exchange would otherwise call `onCode` twice with the same code
|
|
80
|
+
* and paint `invalid_grant` over a connection that had just succeeded. */
|
|
81
|
+
let consumed = false;
|
|
82
|
+
server.on("request", (req, res) => {
|
|
83
|
+
const url = new URL(req.url ?? "/", redirectUri);
|
|
84
|
+
const state = url.searchParams.get("state");
|
|
85
|
+
const code = url.searchParams.get("code");
|
|
86
|
+
const denied = url.searchParams.get("error");
|
|
87
|
+
const matched = state === args.state;
|
|
88
|
+
log({
|
|
89
|
+
tag: "[google-oauth]",
|
|
90
|
+
op: "callback",
|
|
91
|
+
reg: args.state,
|
|
92
|
+
account: args.accountId,
|
|
93
|
+
"state-match": matched ? "true" : "false",
|
|
94
|
+
...(denied ? { error: denied } : {}),
|
|
95
|
+
});
|
|
96
|
+
if (consumed) {
|
|
97
|
+
res.writeHead(409, HEADERS);
|
|
98
|
+
res.end(page("Already handled", "<p>This link has already been used. Check the chat for the result.</p>"));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// A cancelled consent arrives with `error=access_denied` and no code, and
|
|
102
|
+
// its state matches. Reporting that as a mismatched link would contradict
|
|
103
|
+
// the log line and misdescribe what the user did.
|
|
104
|
+
if (matched && denied) {
|
|
105
|
+
consumed = true;
|
|
106
|
+
res.writeHead(400, HEADERS);
|
|
107
|
+
res.end(page("Consent was not granted", `<p>Google reported <code>${escapeHtml(denied)}</code>. Nothing was connected. Start again from the chat if that was not deliberate.</p>`));
|
|
108
|
+
void close("denied");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!matched || !code) {
|
|
112
|
+
res.writeHead(400, HEADERS);
|
|
113
|
+
res.end(page("That link did not match", "<p>This page was opened with a link that does not match the connection in progress. Start again from the chat.</p>"));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
consumed = true;
|
|
117
|
+
void args
|
|
118
|
+
.onCode(code)
|
|
119
|
+
.then(({ email }) => {
|
|
120
|
+
res.writeHead(200, HEADERS);
|
|
121
|
+
res.end(page(`Connected ${escapeHtml(email)}`, "<p>Your calendar is connected. Return to the chat and say you're done.</p>"));
|
|
122
|
+
})
|
|
123
|
+
.catch((err) => {
|
|
124
|
+
// Never claim success on a failed exchange, and never swallow Google's
|
|
125
|
+
// own words: invalid_grant means expired or reused, and the operator
|
|
126
|
+
// needs to read that rather than a generic apology.
|
|
127
|
+
res.writeHead(400, HEADERS);
|
|
128
|
+
res.end(page("Could not finish connecting", `<p>${escapeHtml(err.message)}</p><p>Return to the chat and try again.</p>`));
|
|
129
|
+
})
|
|
130
|
+
// Writing the response can itself throw — a socket Node destroyed at
|
|
131
|
+
// requestTimeout makes the catch branch's writeHead raise
|
|
132
|
+
// ERR_HTTP_HEADERS_SENT. Without this the rejection is unhandled, which
|
|
133
|
+
// under Node's default takes the whole MCP server down.
|
|
134
|
+
.catch(() => {
|
|
135
|
+
if (!res.writableEnded)
|
|
136
|
+
res.destroy();
|
|
137
|
+
})
|
|
138
|
+
.finally(() => void close("exchanged"));
|
|
139
|
+
});
|
|
140
|
+
log({ tag: "[google-oauth]", op: "listen", account: args.accountId, reg: args.state, port });
|
|
141
|
+
return { redirectUri, port, close };
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=loopback-listener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback-listener.js","sourceRoot":"","sources":["../../src/auth/loopback-listener.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,15 @@ export interface PendingOAuth {
|
|
|
2
2
|
state: string;
|
|
3
3
|
verifier: string;
|
|
4
4
|
createdAt: number;
|
|
5
|
+
/** The redirect_uri the consent URL carried. Google compares the exchange's
|
|
6
|
+
* redirect_uri byte-for-byte against it, and since Task 1799 the port is
|
|
7
|
+
* whatever the loopback listener bound, so the constant can no longer be
|
|
8
|
+
* assumed.
|
|
9
|
+
*
|
|
10
|
+
* Optional because an entry written before 1799 carries no such field and
|
|
11
|
+
* may still be mid-flight across an upgrade; readers fall back to the fixed
|
|
12
|
+
* loopback constant for those. */
|
|
13
|
+
redirectUri?: string;
|
|
5
14
|
}
|
|
6
15
|
export declare class PendingStore {
|
|
7
16
|
readonly accountId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-store.d.ts","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"pending-store.d.ts","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;uCAOmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,YAAY;IAOrB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM;IAP9B,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGtB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM;IAO9B,wDAAwD;IACxD,OAAO,CAAC,OAAO;IAcf,KAAK,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI;IAoB5B,IAAI,IAAI,YAAY,GAAG,IAAI;IAS3B,KAAK,IAAI,IAAI;IAIb;;;gFAG4E;IAC5E,MAAM,IAAI,MAAM,GAAG,IAAI;CAIxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-store.js","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,MAAM,EACN,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"pending-store.js","sourceRoot":"","sources":["../../src/auth/pending-store.ts"],"names":[],"mappings":"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"}
|