@kamilio/wire 0.1.3 → 0.1.5
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 +57 -18
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +3 -10
- package/dist/auth.js.map +1 -1
- package/dist/executable.d.ts.map +1 -1
- package/dist/executable.js +33 -8
- package/dist/executable.js.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +18 -7
- package/dist/hooks.js.map +1 -1
- package/node_modules/provider-chatgpt/dist/chatgpt.d.ts.map +1 -1
- package/node_modules/provider-chatgpt/dist/chatgpt.js +22 -3
- package/node_modules/provider-chatgpt/dist/chatgpt.js.map +1 -1
- package/node_modules/provider-chatgpt/src/chatgpt.ts +20 -3
- package/node_modules/provider-gmail/dist/gmail.d.ts.map +1 -1
- package/node_modules/provider-gmail/dist/gmail.js +2 -0
- package/node_modules/provider-gmail/dist/gmail.js.map +1 -1
- package/node_modules/provider-gmail/src/gmail.ts +1 -0
- package/node_modules/provider-google-docs/dist/google-docs.d.ts.map +1 -1
- package/node_modules/provider-google-docs/dist/google-docs.js +9 -6
- package/node_modules/provider-google-docs/dist/google-docs.js.map +1 -1
- package/node_modules/provider-google-docs/src/google-docs.ts +10 -7
- package/node_modules/provider-notion/dist/notion-sync.d.ts +1 -1
- package/node_modules/provider-notion/dist/notion-sync.d.ts.map +1 -1
- package/node_modules/provider-notion/dist/notion-sync.js +41 -12
- package/node_modules/provider-notion/dist/notion-sync.js.map +1 -1
- package/node_modules/provider-notion/dist/notion.js +1 -1
- package/node_modules/provider-notion/dist/notion.js.map +1 -1
- package/node_modules/provider-notion/src/notion-sync.ts +41 -12
- package/node_modules/provider-notion/src/notion.ts +1 -1
- package/node_modules/wire-core/dist/core/transform.d.ts +1 -0
- package/node_modules/wire-core/dist/core/transform.d.ts.map +1 -1
- package/node_modules/wire-core/dist/core/transform.js +4 -0
- package/node_modules/wire-core/dist/core/transform.js.map +1 -1
- package/node_modules/wire-core/dist/operations.d.ts.map +1 -1
- package/node_modules/wire-core/dist/operations.js +8 -6
- package/node_modules/wire-core/dist/operations.js.map +1 -1
- package/node_modules/wire-core/dist/runtime/chrome.d.ts +1 -0
- package/node_modules/wire-core/dist/runtime/chrome.d.ts.map +1 -1
- package/node_modules/wire-core/dist/runtime/chrome.js +64 -79
- package/node_modules/wire-core/dist/runtime/chrome.js.map +1 -1
- package/node_modules/wire-core/src/core/transform.ts +5 -1
- package/node_modules/wire-core/src/operations.ts +8 -6
- package/node_modules/wire-core/src/runtime/chrome.ts +69 -75
- package/package.json +3 -3
- package/src/auth.ts +3 -9
- package/src/executable.ts +33 -8
- package/src/hooks.ts +16 -6
|
@@ -28,6 +28,8 @@ export async function chromeLaunchArguments(environment: NodeEnvironment, startU
|
|
|
28
28
|
]);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const chromeExecutable = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
32
|
+
|
|
31
33
|
interface ChromeCookie {
|
|
32
34
|
readonly domain: string;
|
|
33
35
|
readonly expires: number;
|
|
@@ -36,6 +38,10 @@ interface ChromeCookie {
|
|
|
36
38
|
readonly path: string;
|
|
37
39
|
readonly secure: boolean;
|
|
38
40
|
readonly value: string;
|
|
41
|
+
readonly sameSite?: string;
|
|
42
|
+
readonly sourceScheme?: string;
|
|
43
|
+
readonly sourcePort?: number;
|
|
44
|
+
readonly partitionKey?: Readonly<Record<string, unknown>>;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
interface PendingRequest {
|
|
@@ -133,7 +139,7 @@ function devtoolsUrl(stderr: NodeJS.ReadableStream): Promise<string> {
|
|
|
133
139
|
const match = /DevTools listening on (ws:\/\/[^\s]+)/.exec(contents);
|
|
134
140
|
if (match !== null) resolve(match[1]!);
|
|
135
141
|
});
|
|
136
|
-
stderr.on("end", () => reject(new Error(contents)));
|
|
142
|
+
stderr.on("end", () => reject(new Error(contents === "" ? "Chrome exited before DevTools started. A Wire Chrome window is already open from a previous login; close it and retry." : contents)));
|
|
137
143
|
});
|
|
138
144
|
}
|
|
139
145
|
|
|
@@ -155,54 +161,6 @@ async function pageDevtoolsUrl(browserUrl: string, domains: readonly string[]):
|
|
|
155
161
|
return targets.find((target) => target.type === "page" && domains.some((domain) => new URL(target.url).hostname === domain || new URL(target.url).hostname.endsWith(`.${domain}`)))!.webSocketDebuggerUrl;
|
|
156
162
|
}
|
|
157
163
|
|
|
158
|
-
async function chatgptBrowserMetadata(pageConnection: ChromeConnection): Promise<Readonly<Record<string, string>> | null> {
|
|
159
|
-
const evaluation = await pageConnection.request("Runtime.evaluate", {
|
|
160
|
-
expression: `new Promise((resolve) => {
|
|
161
|
-
(async () => {
|
|
162
|
-
if (location.hostname !== "chatgpt.com") {
|
|
163
|
-
resolve({ ok: false });
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
const response = await fetch("/api/auth/session", { cache: "no-store" });
|
|
167
|
-
const text = await response.text();
|
|
168
|
-
if (!text.startsWith("{")) {
|
|
169
|
-
resolve({ ok: false });
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const session = JSON.parse(text);
|
|
173
|
-
if (!("account" in session) || !("id" in session.account) || !("accessToken" in session) || "error" in session) {
|
|
174
|
-
resolve({ ok: false });
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const backend = await fetch("/backend-api/conversations?offset=0&limit=1&order=updated&is_archived=false&is_starred=false", {
|
|
178
|
-
cache: "no-store",
|
|
179
|
-
headers: {
|
|
180
|
-
authorization: \`Bearer \${session.accessToken}\`,
|
|
181
|
-
"chatgpt-account-id": session.account.id
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
resolve({ ok: backend.ok, account_id: session.account.id });
|
|
185
|
-
})().catch(() => resolve({ ok: false }));
|
|
186
|
-
})`,
|
|
187
|
-
awaitPromise: true,
|
|
188
|
-
returnByValue: true,
|
|
189
|
-
}) as { result: { value?: { ok?: boolean; account_id?: string } } };
|
|
190
|
-
const value = evaluation.result.value!;
|
|
191
|
-
if (value.ok !== true) return null;
|
|
192
|
-
return Object.freeze({ account_id: value.account_id! });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function chatgptChallengeUrl(pageConnection: ChromeConnection): Promise<string | null> {
|
|
196
|
-
const evaluation = await pageConnection.request("Runtime.evaluate", {
|
|
197
|
-
expression: `(() => ({ href: location.href, title: document.title, text: document.body.innerText }))()`,
|
|
198
|
-
returnByValue: true,
|
|
199
|
-
}) as { result: { value?: { href: string; title: string; text: string } } };
|
|
200
|
-
const value = evaluation.result.value!;
|
|
201
|
-
const pageText = `${value.href}\n${value.title}\n${value.text}`;
|
|
202
|
-
if (/cdn-cgi\/challenge-platform|cf_chl|Just a moment|Verify you are human|Cloudflare/i.test(pageText)) return value.href;
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
164
|
function chromeCookies(values: readonly ChromeCookie[], domains: readonly string[]): readonly Cookie[] {
|
|
207
165
|
return Object.freeze(values.filter((cookie) => domains.some((domain) => cookie.domain === domain || cookie.domain.endsWith(`.${domain}`))).map((cookie) => Object.freeze({
|
|
208
166
|
domain: cookie.domain,
|
|
@@ -222,6 +180,7 @@ export interface ChromeCookieExtraction {
|
|
|
222
180
|
readonly domains: readonly string[];
|
|
223
181
|
readonly ready: (cookies: readonly Cookie[]) => boolean;
|
|
224
182
|
readonly metadataExpression?: string;
|
|
183
|
+
readonly freshSession?: true;
|
|
225
184
|
readonly verify: (cookies: readonly Cookie[], metadata: Readonly<Record<string, string>>) => Promise<boolean>;
|
|
226
185
|
}
|
|
227
186
|
|
|
@@ -242,10 +201,66 @@ function sleep(milliseconds: number): Promise<void> {
|
|
|
242
201
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
243
202
|
}
|
|
244
203
|
|
|
204
|
+
async function headlessChromeConnection(environment: NodeEnvironment): Promise<Readonly<{ chrome: ReturnType<typeof spawn>; connection: ChromeConnection }>> {
|
|
205
|
+
const chrome = spawn(chromeExecutable, [`--user-data-dir=${chromeUserDataDir(environment)}`, "--remote-debugging-port=0", "--headless=new", "--no-first-run", "--no-default-browser-check", "about:blank"], { detached: true, stdio: ["ignore", "ignore", "pipe"] });
|
|
206
|
+
const connection = new ChromeConnection(await devtoolsUrl(chrome.stderr!));
|
|
207
|
+
await connection.opened();
|
|
208
|
+
return Object.freeze({ chrome, connection });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function expireServiceCookies(connection: ChromeConnection, domains: readonly string[]): Promise<void> {
|
|
212
|
+
const serviceDomain = (domain: string) => { const bare = domain.startsWith(".") ? domain.slice(1) : domain; return domains.some((value) => bare === value || bare.endsWith(`.${value}`)); };
|
|
213
|
+
const stored = await connection.request("Storage.getCookies") as { cookies: readonly ChromeCookie[] };
|
|
214
|
+
const expiredCopies = stored.cookies.filter((cookie) => serviceDomain(cookie.domain)).map((cookie) => ({
|
|
215
|
+
name: cookie.name,
|
|
216
|
+
value: "",
|
|
217
|
+
domain: cookie.domain,
|
|
218
|
+
path: cookie.path,
|
|
219
|
+
secure: cookie.secure,
|
|
220
|
+
httpOnly: cookie.httpOnly,
|
|
221
|
+
expires: 1,
|
|
222
|
+
...(cookie.sameSite === undefined ? {} : { sameSite: cookie.sameSite }),
|
|
223
|
+
...(cookie.sourceScheme === undefined ? {} : { sourceScheme: cookie.sourceScheme }),
|
|
224
|
+
...(cookie.sourcePort === undefined ? {} : { sourcePort: cookie.sourcePort }),
|
|
225
|
+
...(cookie.partitionKey === undefined ? {} : { partitionKey: cookie.partitionKey }),
|
|
226
|
+
}));
|
|
227
|
+
if (expiredCopies.length > 0) await connection.request("Storage.setCookies", { cookies: expiredCopies });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function extractDetachedChromeCookies(environment: NodeEnvironment, extraction: ChromeCookieExtraction): Promise<ChromeCookieResult> {
|
|
231
|
+
await mkdir(chromeUserDataDir(environment), { recursive: true });
|
|
232
|
+
const prep = await headlessChromeConnection(environment);
|
|
233
|
+
await expireServiceCookies(prep.connection, extraction.domains);
|
|
234
|
+
await closeChrome(prep.chrome, prep.connection);
|
|
235
|
+
const chrome = spawn(chromeExecutable, [`--user-data-dir=${chromeUserDataDir(environment)}`, "--no-first-run", "--no-default-browser-check", extraction.startUrl], { detached: true, stdio: ["ignore", "ignore", "ignore"] });
|
|
236
|
+
stderr.write(`\nLog in to ${extraction.service} in the opened Chrome window, then quit Chrome (Cmd+Q). Wire picks up the session after Chrome exits.\n`);
|
|
237
|
+
let interrupted = false;
|
|
238
|
+
const interrupt = () => {
|
|
239
|
+
interrupted = true;
|
|
240
|
+
process.kill(-chrome.pid!, "SIGTERM");
|
|
241
|
+
};
|
|
242
|
+
process.on("SIGINT", interrupt);
|
|
243
|
+
await new Promise((resolve) => chrome.once("exit", resolve));
|
|
244
|
+
process.off("SIGINT", interrupt);
|
|
245
|
+
await sleep(500);
|
|
246
|
+
const harvest = await headlessChromeConnection(environment);
|
|
247
|
+
const stored = await harvest.connection.request("Storage.getCookies") as { cookies: readonly ChromeCookie[] };
|
|
248
|
+
await closeChrome(harvest.chrome, harvest.connection);
|
|
249
|
+
const cookies = chromeCookies(stored.cookies, extraction.domains);
|
|
250
|
+
if (interrupted) {
|
|
251
|
+
const save = await confirmSaveLogin();
|
|
252
|
+
if (!save) throw new Error("Login not saved");
|
|
253
|
+
return Object.freeze({ cookies, metadata: Object.freeze({}), manual: true });
|
|
254
|
+
}
|
|
255
|
+
if (!extraction.ready(cookies)) throw new Error(`${extraction.service} login did not complete: the Chrome window was quit before login finished.`);
|
|
256
|
+
return Object.freeze({ cookies, metadata: Object.freeze({}) });
|
|
257
|
+
}
|
|
258
|
+
|
|
245
259
|
export async function extractChromeCookies(environment: NodeEnvironment, extraction: ChromeCookieExtraction): Promise<ChromeCookieResult> {
|
|
260
|
+
if (extraction.freshSession === true) return extractDetachedChromeCookies(environment, extraction);
|
|
246
261
|
const profile = chromeUserDataDir(environment);
|
|
247
262
|
await mkdir(profile, { recursive: true });
|
|
248
|
-
const chrome = spawn(
|
|
263
|
+
const chrome = spawn(chromeExecutable, [...await chromeLaunchArguments(environment, extraction.startUrl)], { detached: true, stdio: ["ignore", "ignore", "pipe"] });
|
|
249
264
|
let interrupted = false;
|
|
250
265
|
let interrupting = false;
|
|
251
266
|
let interruptResult: ChromeCookieResult | null | undefined;
|
|
@@ -268,7 +283,7 @@ export async function extractChromeCookies(environment: NodeEnvironment, extract
|
|
|
268
283
|
const browserUrl = await devtoolsUrl(chrome.stderr!);
|
|
269
284
|
const connection = new ChromeConnection(browserUrl);
|
|
270
285
|
await connection.opened();
|
|
271
|
-
const pageConnection = extraction.metadataExpression === undefined
|
|
286
|
+
const pageConnection = extraction.metadataExpression === undefined ? undefined : new ChromeConnection(await pageDevtoolsUrl(browserUrl, extraction.domains));
|
|
272
287
|
if (pageConnection !== undefined) await pageConnection.opened();
|
|
273
288
|
if (pageConnection !== undefined) await Promise.race([sleep(2000), interruptPromise]);
|
|
274
289
|
const currentResult = async (): Promise<ChromeCookieResult> => {
|
|
@@ -291,33 +306,12 @@ export async function extractChromeCookies(environment: NodeEnvironment, extract
|
|
|
291
306
|
return interruptResult;
|
|
292
307
|
};
|
|
293
308
|
for (;;) {
|
|
294
|
-
if (extraction.service === "chatgpt") {
|
|
295
|
-
const challengeUrl = await chatgptChallengeUrl(pageConnection!);
|
|
296
|
-
const manualResult = await interruptedResult();
|
|
297
|
-
if (manualResult !== undefined) return manualResult;
|
|
298
|
-
if (challengeUrl !== null) {
|
|
299
|
-
await closeChrome(chrome, connection, pageConnection);
|
|
300
|
-
process.off("SIGINT", interrupt);
|
|
301
|
-
throw new Error(`ChatGPT login is stuck on an HTML challenge: ${challengeUrl}`);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
309
|
const result = await currentResult();
|
|
305
310
|
const manualResult = await interruptedResult();
|
|
306
311
|
if (manualResult !== undefined) return manualResult;
|
|
307
312
|
if (extraction.ready(result.cookies)) {
|
|
308
|
-
|
|
309
|
-
if (extraction.
|
|
310
|
-
const chatgptMetadata = await Promise.race([chatgptBrowserMetadata(pageConnection!), interruptPromise.then(() => undefined)]);
|
|
311
|
-
const interruptedChatgptResult = await interruptedResult();
|
|
312
|
-
if (interruptedChatgptResult !== undefined) return interruptedChatgptResult;
|
|
313
|
-
if (chatgptMetadata === undefined) continue;
|
|
314
|
-
if (chatgptMetadata === null) {
|
|
315
|
-
await closeChrome(chrome, connection, pageConnection);
|
|
316
|
-
process.off("SIGINT", interrupt);
|
|
317
|
-
throw new Error("ChatGPT login is blocked by an HTML challenge. Complete `wire chatgpt login` in the opened Chrome window, then retry.");
|
|
318
|
-
}
|
|
319
|
-
metadata = chatgptMetadata;
|
|
320
|
-
} else if (extraction.metadataExpression !== undefined) {
|
|
313
|
+
const metadata: Readonly<Record<string, string>> = result.metadata;
|
|
314
|
+
if (extraction.metadataExpression !== undefined) {
|
|
321
315
|
if (Object.keys(metadata).length === 0) {
|
|
322
316
|
await Promise.race([sleep(1000), interruptPromise]);
|
|
323
317
|
const interruptedMetadataResult = await interruptedResult();
|
|
@@ -325,7 +319,7 @@ export async function extractChromeCookies(environment: NodeEnvironment, extract
|
|
|
325
319
|
continue;
|
|
326
320
|
}
|
|
327
321
|
}
|
|
328
|
-
if (
|
|
322
|
+
if (!(await Promise.race([extraction.verify(result.cookies, metadata), interruptPromise.then(() => false)]))) {
|
|
329
323
|
const interruptedVerifyResult = await interruptedResult();
|
|
330
324
|
if (interruptedVerifyResult !== undefined) return interruptedVerifyResult;
|
|
331
325
|
await Promise.race([sleep(1000), interruptPromise]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kamilio/wire",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Attach Notion, Google Docs, Slack, Gmail, Asana, ChatGPT, and Zoom resources as local Markdown you can sync.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "tsc -p tsconfig.json",
|
|
89
89
|
"dev-link": "npm link",
|
|
90
|
-
"prepack": "rm -rf node_modules && mkdir -p node_modules && for package in provider-asana provider-chatgpt provider-gmail provider-google-docs provider-notion provider-slack provider-zoom wire-core; do cp -R ../$package node_modules/$package; done && for package in ajv commander fast-deep-equal fast-string-truncated-width fast-string-width fast-wrap-ansi fast-uri ignore jose jsonc-parser json-schema-traverse pct-encode require-from-string sisteransi smol-toml tiny-stdio-mcp-server toolcraft toolcraft-schema uri-template uri-template-lite yaml; do cp -R ../../node_modules/$package node_modules/$package; done",
|
|
91
|
-
"postpack": "rm -rf node_modules",
|
|
90
|
+
"prepack": "cp ../../README.md README.md && rm -rf node_modules && mkdir -p node_modules && for package in provider-asana provider-chatgpt provider-gmail provider-google-docs provider-notion provider-slack provider-zoom wire-core; do cp -R ../$package node_modules/$package; done && for package in ajv commander fast-deep-equal fast-string-truncated-width fast-string-width fast-wrap-ansi fast-uri ignore jose jsonc-parser json-schema-traverse pct-encode require-from-string sisteransi smol-toml tiny-stdio-mcp-server toolcraft toolcraft-schema uri-template uri-template-lite yaml; do cp -R ../../node_modules/$package node_modules/$package; done",
|
|
91
|
+
"postpack": "rm -rf node_modules README.md",
|
|
92
92
|
"test": "node --test --test-concurrency=1 test/**/*.test.mjs",
|
|
93
93
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
94
94
|
"check": "npm run typecheck && npm run build && npm test"
|
package/src/auth.ts
CHANGED
|
@@ -267,19 +267,13 @@ export function composeAuth(runtime: RuntimeCapabilities, environment: NodeEnvir
|
|
|
267
267
|
await saveCookies(service, state.cookies, state.metadata);
|
|
268
268
|
return state.result;
|
|
269
269
|
};
|
|
270
|
-
const extract = async (service: AuthService, startUrl: string, domains: readonly string[], ready: (values: readonly Cookie[]) => boolean, metadataExpression?: string) => {
|
|
271
|
-
const extraction = { service, startUrl, domains, ready, verify: async (values: readonly Cookie[], metadata: Readonly<Record<string, string>>) => (await verifyCookieState(service, values, metadata)).result !== null, ...(metadataExpression === undefined ? {} : { metadataExpression }) };
|
|
270
|
+
const extract = async (service: AuthService, startUrl: string, domains: readonly string[], ready: (values: readonly Cookie[]) => boolean, metadataExpression?: string, freshSession?: true) => {
|
|
271
|
+
const extraction = { service, startUrl, domains, ready, verify: async (values: readonly Cookie[], metadata: Readonly<Record<string, string>>) => (await verifyCookieState(service, values, metadata)).result !== null, ...(metadataExpression === undefined ? {} : { metadataExpression }), ...(freshSession === undefined ? {} : { freshSession }) };
|
|
272
272
|
const result = await extractCookies(environment, extraction);
|
|
273
273
|
if (result.manual === true) {
|
|
274
274
|
await saveCookies(service, result.cookies, result.metadata);
|
|
275
275
|
return Object.freeze({ service, identity: Object.freeze({ saved: true }) });
|
|
276
276
|
}
|
|
277
|
-
if (service === "chatgpt") {
|
|
278
|
-
await saveCookies(service, result.cookies, result.metadata);
|
|
279
|
-
const accountId = result.metadata["account_id"];
|
|
280
|
-
if (accountId === undefined) throw new Error("chatgpt cookie authentication failed. Run `wire chatgpt login` once; other commands reuse saved cookies.");
|
|
281
|
-
return Object.freeze({ service, identity: Object.freeze({ account_id: accountId }) });
|
|
282
|
-
}
|
|
283
277
|
const verified = await verifyCookieState(service, result.cookies, result.metadata);
|
|
284
278
|
if (verified.result === null) throw new Error(`${service} cookie authentication failed. Run \`wire ${service} login\` once; other commands reuse saved cookies.`);
|
|
285
279
|
await saveCookies(service, verified.cookies, verified.metadata);
|
|
@@ -293,7 +287,7 @@ export function composeAuth(runtime: RuntimeCapabilities, environment: NodeEnvir
|
|
|
293
287
|
return Object.freeze({ service, deleted: true });
|
|
294
288
|
},
|
|
295
289
|
extractAsana: () => extract("asana", "https://app.asana.com/", ["asana.com"], (values) => values.some((cookie) => cookie.domain === ".asana.com" || cookie.domain.endsWith(".asana.com"))),
|
|
296
|
-
extractChatgpt: () => extract("chatgpt", "https://chatgpt.com/", ["chatgpt.com", "openai.com"],
|
|
290
|
+
extractChatgpt: () => extract("chatgpt", "https://chatgpt.com/", ["chatgpt.com", "openai.com"], (values) => values.some((cookie) => cookie.name === "oai-did") && values.some((cookie) => cookie.name === "__Secure-next-auth.session-token" || cookie.name === "__Secure-next-auth.session-token.0"), undefined, true),
|
|
297
291
|
extractGmail: () => extract("gmail", "https://mail.google.com/mail/u/0/", ["google.com"], googleReady),
|
|
298
292
|
extractGoogleDocs: () => extract("google-docs", "https://docs.google.com/", ["google.com"], googleReady),
|
|
299
293
|
extractNotion: () => extract("notion", "https://www.notion.so/login", ["notion.so", "notion.com"], requiredReady(["token_v2", "notion_user_id", "notion_users"])),
|
package/src/executable.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { createRoot } from "./adapters/root.js";
|
|
6
6
|
import { composeAuth } from "./auth.js";
|
|
@@ -52,15 +52,21 @@ function loadConfiguredEnvironment(environment: NodeEnvironment, currentDirector
|
|
|
52
52
|
return config.env === undefined ? environment : Object.freeze({ ...environment, ...config.env });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function operationPath(value: string, path: string): string {
|
|
56
|
+
if (value.startsWith("http://") || value.startsWith("https://")) return path;
|
|
57
|
+
return isAbsolute(value) ? value : resolve(path, value);
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
export function createExecutableRoot(environment: NodeEnvironment, currentDirectory: string, options = { allowPaste: true }) {
|
|
56
61
|
const repositoryRoot = environment["WIRE_REPOSITORY_ROOT"] ?? discoverRepositoryRoot(currentDirectory);
|
|
57
62
|
const baseEnvironment = repositoryRoot === undefined ? environment : Object.freeze({ ...environment, WIRE_REPOSITORY_ROOT: repositoryRoot });
|
|
58
|
-
|
|
63
|
+
let activeConfigurationPath = currentDirectory;
|
|
64
|
+
const configuredEnvironment = () => loadConfiguredEnvironment(baseEnvironment, activeConfigurationPath);
|
|
59
65
|
const http = createNodeHttp();
|
|
60
66
|
const filesystem = createNodeFilesystem();
|
|
61
67
|
const processCapability = createNodeProcess();
|
|
62
68
|
const clock = createNodeClock();
|
|
63
|
-
const configuration = createNodeConfiguration(
|
|
69
|
+
const configuration = Object.freeze({ get: (name: string) => createNodeConfiguration(configuredEnvironment()).get(name) });
|
|
64
70
|
const home = configuration.get("HOME");
|
|
65
71
|
const runtimeBase = {
|
|
66
72
|
http,
|
|
@@ -69,14 +75,14 @@ export function createExecutableRoot(environment: NodeEnvironment, currentDirect
|
|
|
69
75
|
clock,
|
|
70
76
|
openFiles: createNodeOpenFiles(processCapability),
|
|
71
77
|
configuration,
|
|
72
|
-
secrets: createNodeSecrets(filesystem,
|
|
78
|
+
secrets: createNodeSecrets(filesystem, configuredEnvironment()),
|
|
73
79
|
gmailTokens: Object.freeze({ load: () => createGoogleTokensCapability(filesystem, http, clock, configuration.get("GOOGLE_CREDENTIALS_FILE"), configuration.get("GOOGLE_TOKEN_FILE")).load(), refresh: () => createGoogleTokensCapability(filesystem, http, clock, configuration.get("GOOGLE_CREDENTIALS_FILE"), configuration.get("GOOGLE_TOKEN_FILE")).refresh() }),
|
|
74
80
|
googleFormsTokens: Object.freeze({ load: () => createGoogleTokensCapability(filesystem, http, clock, configuration.get("GOOGLE_CREDENTIALS_FILE"), configuration.get("GOOGLE_FORMS_TOKEN_FILE")).load(), refresh: () => createGoogleTokensCapability(filesystem, http, clock, configuration.get("GOOGLE_CREDENTIALS_FILE"), configuration.get("GOOGLE_FORMS_TOKEN_FILE")).refresh() }),
|
|
75
81
|
};
|
|
76
82
|
let auth: ReturnType<typeof composeAuth>;
|
|
77
|
-
const runtime = Object.freeze({ ...runtimeBase, cookies: createCookiesCapability(filesystem, () => home, () =>
|
|
78
|
-
auth = composeAuth(runtime,
|
|
79
|
-
const
|
|
83
|
+
const runtime = Object.freeze({ ...runtimeBase, cookies: createCookiesCapability(filesystem, () => home, () => configuredEnvironment()["WIRE_REPOSITORY_ROOT"]) });
|
|
84
|
+
auth = composeAuth(runtime, configuredEnvironment(), extractChromeCookies);
|
|
85
|
+
const baseWire = composeWire({
|
|
80
86
|
home,
|
|
81
87
|
fetchInput: runtime,
|
|
82
88
|
catalog: serviceCatalog,
|
|
@@ -98,6 +104,25 @@ export function createExecutableRoot(environment: NodeEnvironment, currentDirect
|
|
|
98
104
|
watch: createNodeWatch(),
|
|
99
105
|
now: runtime.clock.now,
|
|
100
106
|
open: runtime.openFiles.open,
|
|
101
|
-
})
|
|
107
|
+
});
|
|
108
|
+
const configuredWire = Object.freeze({
|
|
109
|
+
...baseWire,
|
|
110
|
+
attach: async (url: string, path: string) => { activeConfigurationPath = path; return baseWire.attach(url, path); },
|
|
111
|
+
create: async (url: string, path: string) => { activeConfigurationPath = path; return baseWire.create(url, path); },
|
|
112
|
+
view: async (url: string) => { activeConfigurationPath = currentDirectory; return baseWire.view(url); },
|
|
113
|
+
downloadSource: async (url: string, path: string) => { activeConfigurationPath = path; return baseWire.downloadSource(url, path); },
|
|
114
|
+
sync: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.sync(value, path); },
|
|
115
|
+
download: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.download(value, path); },
|
|
116
|
+
detach: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.detach(value, path); },
|
|
117
|
+
unlink: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.unlink(value, path); },
|
|
118
|
+
watch: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.watch(value, path); },
|
|
119
|
+
openResource: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.openResource(value, path); },
|
|
120
|
+
syncAll: async (path: string) => { activeConfigurationPath = path; return baseWire.syncAll(path); },
|
|
121
|
+
listResources: async (path: string) => { activeConfigurationPath = path; return baseWire.listResources(path); },
|
|
122
|
+
showResource: async (value: string, path: string) => { activeConfigurationPath = operationPath(value, path); return baseWire.showResource(value, path); },
|
|
123
|
+
init: baseWire.init,
|
|
124
|
+
switchBackend: baseWire.switchBackend,
|
|
125
|
+
});
|
|
126
|
+
const wire = withWireHooks(configuredWire, { currentDirectory, home, environment: configuredEnvironment() });
|
|
102
127
|
return createRoot(wire, currentDirectory, auth, readStandardInput, options);
|
|
103
128
|
}
|
package/src/hooks.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
|
|
7
7
|
import type { Resource, Wire, WireResult } from "wire-core";
|
|
8
|
-
import { configuredWireRoot, openWireRegistry, wireRelativePath } from "wire-core";
|
|
8
|
+
import { configuredWireRoot, loadWireConfig, openWireRegistry, wireRelativePath } from "wire-core";
|
|
9
9
|
import type { WireWatchSyncHook } from "wire-core/internal/operations";
|
|
10
10
|
import { wireWatchHooks } from "wire-core/internal/operations";
|
|
11
11
|
|
|
@@ -118,10 +118,18 @@ async function updateMovedResult(options: WireHookOptions, wireRoot: string, res
|
|
|
118
118
|
{ ...primary, path: relativePath },
|
|
119
119
|
],
|
|
120
120
|
};
|
|
121
|
-
if (result.summary.action !== "detached")
|
|
121
|
+
if (result.summary.action !== "detached") {
|
|
122
|
+
const registry = await openWireRegistry(wireRoot, options.home);
|
|
123
|
+
if ((await registry.listResources()).some((item) => item.id === result.resource.id)) await registry.put(resource);
|
|
124
|
+
}
|
|
122
125
|
return { ...result, resource, path: outputPath, summary: { ...result.summary, local: outputPath } };
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
async function hookEnvironment(options: WireHookOptions, wireRoot: string): Promise<Record<string, string>> {
|
|
129
|
+
const config = await loadWireConfig(wireRoot);
|
|
130
|
+
return stringEnvironment({ ...options.environment, ...config.env });
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
async function runHookCommands(wireRoot: string, event: WireHookEvent, environment: Readonly<Record<string, string>>): Promise<Record<string, string>> {
|
|
126
134
|
let current = { ...environment };
|
|
127
135
|
for (const path of await hookCommandPaths(wireRoot, event)) {
|
|
@@ -132,10 +140,11 @@ async function runHookCommands(wireRoot: string, event: WireHookEvent, environme
|
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
async function runResourceHooks(options: WireHookOptions, command: string, result: WireResult): Promise<HookState> {
|
|
135
|
-
const wireRoot =
|
|
143
|
+
const wireRoot = await configuredWireRoot(result.path, options.home);
|
|
144
|
+
if (wireRoot === null) return { result, environment: {} };
|
|
136
145
|
let currentResult = result;
|
|
137
146
|
let environment = {
|
|
138
|
-
...
|
|
147
|
+
...await hookEnvironment(options, wireRoot),
|
|
139
148
|
...resourceEnvironment(command, options.currentDirectory, wireRoot, currentResult),
|
|
140
149
|
};
|
|
141
150
|
for (const path of await hookCommandPaths(wireRoot, "post-resource")) {
|
|
@@ -152,9 +161,10 @@ async function runResourceHooks(options: WireHookOptions, command: string, resul
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
async function runSummaryHooks(options: WireHookOptions, event: WireHookEvent, command: string, rootPath: string, results: readonly WireResult[], environment: Readonly<Record<string, string>>): Promise<Record<string, string>> {
|
|
155
|
-
const wireRoot =
|
|
164
|
+
const wireRoot = await configuredWireRoot(rootPath, options.home);
|
|
165
|
+
if (wireRoot === null) return {};
|
|
156
166
|
return runHookCommands(wireRoot, event, {
|
|
157
|
-
...
|
|
167
|
+
...await hookEnvironment(options, wireRoot),
|
|
158
168
|
...environment,
|
|
159
169
|
...summaryEnvironment(event, command, options.currentDirectory, wireRoot, results),
|
|
160
170
|
});
|