@zenalexa/unicli 0.224.0 → 0.225.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/AGENTS.md +5 -5
- package/README.md +92 -92
- package/README.zh-CN.md +69 -69
- package/dist/adapters/marxists-cn/archive.d.ts +61 -0
- package/dist/adapters/marxists-cn/archive.d.ts.map +1 -0
- package/dist/adapters/marxists-cn/archive.js +861 -0
- package/dist/adapters/marxists-cn/archive.js.map +1 -0
- package/dist/adapters/twitter/lists-extra.d.ts +17 -1
- package/dist/adapters/twitter/lists-extra.d.ts.map +1 -1
- package/dist/adapters/twitter/lists-extra.js +123 -21
- package/dist/adapters/twitter/lists-extra.js.map +1 -1
- package/dist/adapters/twitter/post.js +1 -0
- package/dist/adapters/twitter/post.js.map +1 -1
- package/dist/adapters/twitter/thread.d.ts +13 -1
- package/dist/adapters/twitter/thread.d.ts.map +1 -1
- package/dist/adapters/twitter/thread.js +76 -33
- package/dist/adapters/twitter/thread.js.map +1 -1
- package/dist/adapters/xueqiu/extra.js +2 -2
- package/dist/adapters/xueqiu/extra.js.map +1 -1
- package/dist/browser/cdp-client.d.ts +10 -0
- package/dist/browser/cdp-client.d.ts.map +1 -1
- package/dist/browser/cdp-client.js +21 -0
- package/dist/browser/cdp-client.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/architecture.d.ts +3 -1
- package/dist/commands/architecture.d.ts.map +1 -1
- package/dist/commands/architecture.js +13 -5
- package/dist/commands/architecture.js.map +1 -1
- package/dist/commands/delivery.d.ts.map +1 -1
- package/dist/commands/delivery.js.map +1 -1
- package/dist/commands/describe.d.ts.map +1 -1
- package/dist/commands/describe.js +104 -9
- package/dist/commands/describe.js.map +1 -1
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/dispatch.js +3 -13
- package/dist/commands/dispatch.js.map +1 -1
- package/dist/commands/do.d.ts +2 -1
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +43 -4
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/social.d.ts.map +1 -1
- package/dist/commands/social.js +3 -13
- package/dist/commands/social.js.map +1 -1
- package/dist/core/architecture-tree.d.ts +26 -6
- package/dist/core/architecture-tree.d.ts.map +1 -1
- package/dist/core/architecture-tree.js +184 -40
- package/dist/core/architecture-tree.js.map +1 -1
- package/dist/core/capability-matrix.d.ts +63 -0
- package/dist/core/capability-matrix.d.ts.map +1 -0
- package/dist/core/capability-matrix.js +316 -0
- package/dist/core/capability-matrix.js.map +1 -0
- package/dist/core/command-contract-lint.d.ts.map +1 -1
- package/dist/core/command-contract-lint.js +3 -1
- package/dist/core/command-contract-lint.js.map +1 -1
- package/dist/core/command-contract.d.ts +10 -3
- package/dist/core/command-contract.d.ts.map +1 -1
- package/dist/core/command-contract.js +87 -5
- package/dist/core/command-contract.js.map +1 -1
- package/dist/discovery/aliases.d.ts.map +1 -1
- package/dist/discovery/aliases.js +93 -0
- package/dist/discovery/aliases.js.map +1 -1
- package/dist/discovery/core-catalog.d.ts +1 -0
- package/dist/discovery/core-catalog.d.ts.map +1 -1
- package/dist/discovery/core-catalog.js +22 -5
- package/dist/discovery/core-catalog.js.map +1 -1
- package/dist/discovery/intents.d.ts.map +1 -1
- package/dist/discovery/intents.js +124 -0
- package/dist/discovery/intents.js.map +1 -1
- package/dist/engine/cascade.d.ts.map +1 -1
- package/dist/engine/cascade.js +21 -25
- package/dist/engine/cascade.js.map +1 -1
- package/dist/engine/chromium-cookies.d.ts +27 -0
- package/dist/engine/chromium-cookies.d.ts.map +1 -1
- package/dist/engine/chromium-cookies.js +53 -14
- package/dist/engine/chromium-cookies.js.map +1 -1
- package/dist/engine/cookie-capture.d.ts +30 -0
- package/dist/engine/cookie-capture.d.ts.map +1 -0
- package/dist/engine/cookie-capture.js +104 -0
- package/dist/engine/cookie-capture.js.map +1 -0
- package/dist/engine/cookie-extractor.d.ts.map +1 -1
- package/dist/engine/cookie-extractor.js +4 -12
- package/dist/engine/cookie-extractor.js.map +1 -1
- package/dist/engine/cookie-refresh.d.ts +59 -8
- package/dist/engine/cookie-refresh.d.ts.map +1 -1
- package/dist/engine/cookie-refresh.js +80 -58
- package/dist/engine/cookie-refresh.js.map +1 -1
- package/dist/engine/cookie-source.d.ts +110 -0
- package/dist/engine/cookie-source.d.ts.map +1 -0
- package/dist/engine/cookie-source.js +207 -0
- package/dist/engine/cookie-source.js.map +1 -0
- package/dist/engine/cookies.d.ts +37 -25
- package/dist/engine/cookies.d.ts.map +1 -1
- package/dist/engine/cookies.js +58 -157
- package/dist/engine/cookies.js.map +1 -1
- package/dist/engine/delivery/index.d.ts +2 -1
- package/dist/engine/delivery/index.d.ts.map +1 -1
- package/dist/engine/delivery/index.js +2 -1
- package/dist/engine/delivery/index.js.map +1 -1
- package/dist/engine/delivery/spec.d.ts +44 -0
- package/dist/engine/delivery/spec.d.ts.map +1 -0
- package/dist/engine/delivery/spec.js +82 -0
- package/dist/engine/delivery/spec.js.map +1 -0
- package/dist/engine/executor.d.ts +17 -6
- package/dist/engine/executor.d.ts.map +1 -1
- package/dist/engine/executor.js +131 -68
- package/dist/engine/executor.js.map +1 -1
- package/dist/engine/runtime.d.ts.map +1 -1
- package/dist/engine/runtime.js +9 -4
- package/dist/engine/runtime.js.map +1 -1
- package/dist/engine/step-observer.d.ts +62 -0
- package/dist/engine/step-observer.d.ts.map +1 -0
- package/dist/engine/step-observer.js +38 -0
- package/dist/engine/step-observer.js.map +1 -0
- package/dist/engine/steps/browser-helpers.d.ts.map +1 -1
- package/dist/engine/steps/browser-helpers.js +2 -8
- package/dist/engine/steps/browser-helpers.js.map +1 -1
- package/dist/engine/steps/fetch-text.d.ts.map +1 -1
- package/dist/engine/steps/fetch-text.js +106 -33
- package/dist/engine/steps/fetch-text.js.map +1 -1
- package/dist/engine/steps/fetch.d.ts +20 -0
- package/dist/engine/steps/fetch.d.ts.map +1 -1
- package/dist/engine/steps/fetch.js.map +1 -1
- package/dist/engine/steps/index.d.ts +2 -0
- package/dist/engine/steps/index.d.ts.map +1 -1
- package/dist/engine/steps/index.js +2 -0
- package/dist/engine/steps/index.js.map +1 -1
- package/dist/engine/steps/split-text.d.ts +26 -0
- package/dist/engine/steps/split-text.d.ts.map +1 -0
- package/dist/engine/steps/split-text.js +89 -0
- package/dist/engine/steps/split-text.js.map +1 -0
- package/dist/engine/steps/to-entries.d.ts +9 -0
- package/dist/engine/steps/to-entries.d.ts.map +1 -0
- package/dist/engine/steps/to-entries.js +27 -0
- package/dist/engine/steps/to-entries.js.map +1 -0
- package/dist/engine/text-normalize.d.ts +14 -0
- package/dist/engine/text-normalize.d.ts.map +1 -1
- package/dist/engine/text-normalize.js +64 -0
- package/dist/engine/text-normalize.js.map +1 -1
- package/dist/fast-path/handlers/discovery.d.ts.map +1 -1
- package/dist/fast-path/handlers/discovery.js +7 -7
- package/dist/fast-path/handlers/discovery.js.map +1 -1
- package/dist/manifest-compact.txt +3 -3
- package/dist/manifest.json +493 -3
- package/dist/mcp/tools.js +1 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/output/auth-guidance.d.ts +17 -3
- package/dist/output/auth-guidance.d.ts.map +1 -1
- package/dist/output/auth-guidance.js +27 -3
- package/dist/output/auth-guidance.js.map +1 -1
- package/dist/output/error-map.d.ts.map +1 -1
- package/dist/output/error-map.js +4 -0
- package/dist/output/error-map.js.map +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
- package/skills/unicli/SKILL.md +1 -1
- package/skills/unicli-claude-code/SKILL.md +1 -1
- package/skills/unicli-hermes/SKILL.md +1 -1
- package/src/adapters/12306/price.yaml +91 -0
- package/src/adapters/12306/stations.yaml +52 -0
- package/src/adapters/12306/trains.yaml +129 -0
- package/src/adapters/marxists-cn/archive.test.ts +173 -0
- package/src/adapters/marxists-cn/archive.ts +1049 -0
- package/src/adapters/twitter/lists-extra.test.ts +115 -0
- package/src/adapters/twitter/lists-extra.ts +146 -26
- package/src/adapters/twitter/post.ts +1 -0
- package/src/adapters/twitter/thread.test.ts +25 -1
- package/src/adapters/twitter/thread.ts +99 -47
- package/src/adapters/xueqiu/extra.ts +5 -2
|
@@ -1,71 +1,93 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* @owner src::engine::cookie-refresh
|
|
3
|
+
* @does Refreshes a site's login session by navigating Chrome to it over
|
|
4
|
+
* CDP and re-reading cookies, surfacing the real cause of a miss
|
|
5
|
+
* (no browser / no cookies / error) instead of a bare boolean.
|
|
6
|
+
* @needs ./cookie-extractor (saveCookies), ../browser/page (lazy),
|
|
7
|
+
* ../browser/cdp-client (resolveCdpPort, lazy)
|
|
8
|
+
* @feeds src::engine::runtime (maybeRefreshCookies — the auto 401/403 path)
|
|
9
|
+
* @breaks never throws — every failure becomes a typed SessionRefreshOutcome
|
|
10
|
+
* @invariants navigation (page.goto) happens BEFORE reading cookies so the live
|
|
11
|
+
* server session is rebuilt; this is the navigate-then-read refresh,
|
|
12
|
+
* deliberately distinct from cookies.ts acquireCookies(skipDisk)
|
|
13
|
+
* which is a pure DB/CDP read with no navigation
|
|
14
|
+
* @side-effects connects to Chrome via CDP, navigates a page, persists cookies to disk
|
|
15
|
+
* @concurrency stateless; the page is opened and closed within one call
|
|
16
|
+
* @test tests/unit/engine/cookie-refresh.test.ts
|
|
17
|
+
* @stability stable
|
|
18
|
+
* @since 2026-05-30
|
|
7
19
|
*/
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
import { saveCookies } from "./cookie-extractor.js";
|
|
21
|
+
async function defaultConnect(port) {
|
|
22
|
+
const { BrowserPage } = await import("../browser/page.js");
|
|
23
|
+
return BrowserPage.connect(port);
|
|
24
|
+
}
|
|
25
|
+
export const defaultSessionRefreshDeps = {
|
|
26
|
+
connect: defaultConnect,
|
|
27
|
+
persist: saveCookies,
|
|
28
|
+
};
|
|
11
29
|
/**
|
|
12
|
-
* Attempt to refresh cookies for a site by navigating Chrome to it
|
|
13
|
-
*
|
|
30
|
+
* Attempt to refresh cookies for a site by navigating Chrome to it and reading
|
|
31
|
+
* the resulting session cookies. Returns a typed outcome; never throws.
|
|
32
|
+
*
|
|
33
|
+
* Persistence goes through the canonical writer (saveCookies) so the on-disk
|
|
34
|
+
* format always matches what the loaders expect ({name: value}); hand-rolling
|
|
35
|
+
* the write here once emitted an array format the loaders silently rejected,
|
|
36
|
+
* leaving the adapter unauthenticated after a "successful" refresh.
|
|
14
37
|
*/
|
|
15
|
-
export async function refreshCookies(site, domain) {
|
|
38
|
+
export async function refreshCookies(site, domain, deps = defaultSessionRefreshDeps) {
|
|
39
|
+
let port;
|
|
40
|
+
try {
|
|
41
|
+
const { resolveCdpPort } = await import("../browser/cdp-client.js");
|
|
42
|
+
port = resolveCdpPort();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return {
|
|
46
|
+
status: "error",
|
|
47
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
let page;
|
|
16
51
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
page = await deps.connect(port);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
return {
|
|
56
|
+
status: "no-browser",
|
|
57
|
+
detail: `Could not connect to Chrome on CDP port ${port}: ${err instanceof Error ? err.message : String(err)}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const targetUrl = domain ? `https://${domain}` : `https://www.${site}.com`;
|
|
62
|
+
await page.goto(targetUrl, { settleMs: 3000 });
|
|
63
|
+
const cookies = await page.cookies();
|
|
64
|
+
if (Object.keys(cookies).length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
status: "no-cookies",
|
|
67
|
+
detail: `No cookies present for ${site} after navigating to ${targetUrl}`,
|
|
68
|
+
};
|
|
26
69
|
}
|
|
27
|
-
|
|
70
|
+
deps.persist(site, cookies);
|
|
71
|
+
return {
|
|
72
|
+
status: "refreshed",
|
|
73
|
+
site,
|
|
74
|
+
cookieCount: Object.keys(cookies).length,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return {
|
|
79
|
+
status: "error",
|
|
80
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
28
84
|
try {
|
|
29
|
-
|
|
85
|
+
await page.close();
|
|
30
86
|
}
|
|
31
87
|
catch {
|
|
32
|
-
//
|
|
33
|
-
|
|
88
|
+
// REASON: page close is best-effort cleanup; a close failure must not
|
|
89
|
+
// override the refresh outcome already determined above.
|
|
34
90
|
}
|
|
35
|
-
try {
|
|
36
|
-
// Navigate to the site's main page to refresh session
|
|
37
|
-
const targetUrl = domain
|
|
38
|
-
? `https://${domain}`
|
|
39
|
-
: `https://www.${site}.com`;
|
|
40
|
-
await page.goto(targetUrl, { settleMs: 3000 });
|
|
41
|
-
// Extract cookies
|
|
42
|
-
const cookies = await page.cookies();
|
|
43
|
-
if (Object.keys(cookies).length === 0) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
// Write cookies to file
|
|
47
|
-
const cookiesDir = join(homedir(), ".unicli", "cookies");
|
|
48
|
-
mkdirSync(cookiesDir, { recursive: true });
|
|
49
|
-
const cookieArray = Object.entries(cookies).map(([name, value]) => ({
|
|
50
|
-
name,
|
|
51
|
-
value,
|
|
52
|
-
domain: domain ?? `${site}.com`,
|
|
53
|
-
}));
|
|
54
|
-
const cookiePath = join(cookiesDir, `${site}.json`);
|
|
55
|
-
writeFileSync(cookiePath, JSON.stringify(cookieArray, null, 2), "utf-8");
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
try {
|
|
60
|
-
await page.close();
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
/* best effort */
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return false;
|
|
69
91
|
}
|
|
70
92
|
}
|
|
71
93
|
//# sourceMappingURL=cookie-refresh.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie-refresh.js","sourceRoot":"","sources":["../../src/engine/cookie-refresh.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cookie-refresh.js","sourceRoot":"","sources":["../../src/engine/cookie-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AA2BpD,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAC3D,OAAO,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAuB;IAC3D,OAAO,EAAE,cAAc;IACvB,OAAO,EAAE,WAAW;CACrB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,MAAe,EACf,OAA2B,yBAAyB;IAEpD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;QACpE,IAAI,GAAG,cAAc,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,IAAI,IAAiB,CAAC;IACtB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,YAAY;YACpB,MAAM,EAAE,2CAA2C,IAAI,KACrD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE;SACH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,MAAM,CAAC;QAC3E,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO;gBACL,MAAM,EAAE,YAAY;gBACpB,MAAM,EAAE,0BAA0B,IAAI,wBAAwB,SAAS,EAAE;aAC1E,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,IAAI;YACJ,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM;SACzC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,sEAAsE;YACtE,yDAAyD;QAC3D,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::engine::cookie-source
|
|
3
|
+
* @does Multi-source cookie acquisition that surfaces the REAL cause of a
|
|
4
|
+
* miss (keychain denied / corrupt file / v20 encryption / CDP
|
|
5
|
+
* unavailable) as a typed outcome, instead of collapsing every
|
|
6
|
+
* failure to null.
|
|
7
|
+
* @needs node:fs, node:path, ./chromium-cookies (lazy), ./cookie-extractor (lazy)
|
|
8
|
+
* @feeds src::engine::cookies (loadCookies/loadCookiesWithCDP/acquireCookies
|
|
9
|
+
* projections), src::engine::executor (auth error detail)
|
|
10
|
+
* @breaks never throws from loadCookiesWithDiagnostics — failures become
|
|
11
|
+
* CookieLoadOutcome {status:"error", reasons}; readDiskCookies is total
|
|
12
|
+
* @invariants exactly one of loaded|absent|error; "absent" ⇒ no source errored
|
|
13
|
+
* (genuinely not logged in); "error" ⇒ ≥1 source had a real failure
|
|
14
|
+
* @side-effects readDiskCookies reads fs; default sources read browser DB / CDP
|
|
15
|
+
* @perf disk O(file); browser tries installed browsers in order, stops on hit
|
|
16
|
+
* @concurrency stateless; sources own their own IO
|
|
17
|
+
* @test tests/unit/engine/cookie-source.test.ts
|
|
18
|
+
* @stability experimental
|
|
19
|
+
* @since 2026-05-30
|
|
20
|
+
*/
|
|
21
|
+
export type CookieSourceName = "disk" | "browser" | "cdp";
|
|
22
|
+
/** A typed failure cause from one acquisition source. */
|
|
23
|
+
export interface CookieReason {
|
|
24
|
+
source: CookieSourceName;
|
|
25
|
+
/** Stable code, e.g. corrupt_file | keychain_denied | encryption_unsupported | cdp_unavailable. */
|
|
26
|
+
code: string;
|
|
27
|
+
detail: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of trying every cookie source for a site. Discriminated so callers
|
|
31
|
+
* cannot confuse "genuinely not logged in" (absent) with "Keychain denied /
|
|
32
|
+
* file corrupt / Chrome v20" (error) — the distinction the old null collapse
|
|
33
|
+
* destroyed.
|
|
34
|
+
*/
|
|
35
|
+
export type CookieLoadOutcome = {
|
|
36
|
+
status: "loaded";
|
|
37
|
+
source: CookieSourceName;
|
|
38
|
+
cookies: Record<string, string>;
|
|
39
|
+
} | {
|
|
40
|
+
status: "absent";
|
|
41
|
+
} | {
|
|
42
|
+
status: "error";
|
|
43
|
+
reasons: CookieReason[];
|
|
44
|
+
};
|
|
45
|
+
/** Disk read distinguishes absent (fine) from corrupt (a real, surfaceable fault). */
|
|
46
|
+
export type DiskRead = {
|
|
47
|
+
kind: "ok";
|
|
48
|
+
cookies: Record<string, string>;
|
|
49
|
+
} | {
|
|
50
|
+
kind: "absent";
|
|
51
|
+
} | {
|
|
52
|
+
kind: "corrupt";
|
|
53
|
+
detail: string;
|
|
54
|
+
};
|
|
55
|
+
/** Browser read across installed browsers: a hit, a clean miss, or real errors. */
|
|
56
|
+
export type BrowserAttempt = {
|
|
57
|
+
kind: "ok";
|
|
58
|
+
cookies: Record<string, string>;
|
|
59
|
+
} | {
|
|
60
|
+
kind: "none";
|
|
61
|
+
} | {
|
|
62
|
+
kind: "error";
|
|
63
|
+
reasons: CookieReason[];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Injectable acquisition sources. The default wires the real fs / browser DB /
|
|
67
|
+
* CDP; tests pass fakes so the whole policy is verifiable without a network,
|
|
68
|
+
* Keychain, or browser.
|
|
69
|
+
*/
|
|
70
|
+
export interface CookieSources {
|
|
71
|
+
readDisk(site: string): DiskRead;
|
|
72
|
+
readBrowser(domain: string): Promise<BrowserAttempt>;
|
|
73
|
+
readCdp(domain: string): Promise<Record<string, string>>;
|
|
74
|
+
}
|
|
75
|
+
export declare function cookieDir(): string;
|
|
76
|
+
/**
|
|
77
|
+
* Read the on-disk cookie file, distinguishing absent from corrupt. The old
|
|
78
|
+
* loadCookies collapsed a truncated / wrong-shaped file to null ("no auth"),
|
|
79
|
+
* hiding the real cause; this returns a typed corrupt with a detail.
|
|
80
|
+
*/
|
|
81
|
+
export declare function readDiskCookies(site: string): DiskRead;
|
|
82
|
+
/** Resolve the cookie domain the same way the legacy loader did. */
|
|
83
|
+
export declare function resolveCookieDomain(site: string, domain?: string): string;
|
|
84
|
+
export declare const defaultCookieSources: CookieSources;
|
|
85
|
+
/**
|
|
86
|
+
* Acquire cookies across disk → browser → CDP, collecting the real cause of
|
|
87
|
+
* each source's failure. Never throws: a miss with no underlying fault is
|
|
88
|
+
* "absent"; a miss where a source actually errored (Keychain denial, corrupt
|
|
89
|
+
* file, v20 encryption, CDP down) is "error" with the reasons.
|
|
90
|
+
*
|
|
91
|
+
* Domain precedence and the disk-first / browser / CDP order match the legacy
|
|
92
|
+
* loadCookiesWithCDP exactly, so the projection wrappers preserve behavior.
|
|
93
|
+
*/
|
|
94
|
+
export declare function loadCookiesWithDiagnostics(site: string, domain?: string, sources?: CookieSources, opts?: {
|
|
95
|
+
skipDisk?: boolean;
|
|
96
|
+
}): Promise<CookieLoadOutcome>;
|
|
97
|
+
/**
|
|
98
|
+
* Turn a non-loaded outcome into an agent-actionable message + suggestion.
|
|
99
|
+
* This is where the no-silent-failure win is spent: Keychain denial, v20
|
|
100
|
+
* encryption, and a corrupt file each get a DISTINCT next step instead of the
|
|
101
|
+
* legacy one-size-fits-all "run unicli auth setup".
|
|
102
|
+
*/
|
|
103
|
+
export declare function describeCookieFailure(outcome: Exclude<CookieLoadOutcome, {
|
|
104
|
+
status: "loaded";
|
|
105
|
+
}>, site: string, domain?: string): {
|
|
106
|
+
message: string;
|
|
107
|
+
suggestion: string;
|
|
108
|
+
retryable: boolean;
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=cookie-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-source.d.ts","sourceRoot":"","sources":["../../src/engine/cookie-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GACzB;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,GACD;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAEjD,sFAAsF;AACtF,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,mFAAmF;AACnF,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACjC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1D;AAID,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAyBtD;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAIzE;AA8CD,eAAO,MAAM,oBAAoB,EAAE,aAIlC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,GAAE,aAAoC,EAC7C,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC,iBAAiB,CAAC,CA8C5B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,CAAC,iBAAiB,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC,EACzD,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CA0B7D"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @owner src::engine::cookie-source
|
|
3
|
+
* @does Multi-source cookie acquisition that surfaces the REAL cause of a
|
|
4
|
+
* miss (keychain denied / corrupt file / v20 encryption / CDP
|
|
5
|
+
* unavailable) as a typed outcome, instead of collapsing every
|
|
6
|
+
* failure to null.
|
|
7
|
+
* @needs node:fs, node:path, ./chromium-cookies (lazy), ./cookie-extractor (lazy)
|
|
8
|
+
* @feeds src::engine::cookies (loadCookies/loadCookiesWithCDP/acquireCookies
|
|
9
|
+
* projections), src::engine::executor (auth error detail)
|
|
10
|
+
* @breaks never throws from loadCookiesWithDiagnostics — failures become
|
|
11
|
+
* CookieLoadOutcome {status:"error", reasons}; readDiskCookies is total
|
|
12
|
+
* @invariants exactly one of loaded|absent|error; "absent" ⇒ no source errored
|
|
13
|
+
* (genuinely not logged in); "error" ⇒ ≥1 source had a real failure
|
|
14
|
+
* @side-effects readDiskCookies reads fs; default sources read browser DB / CDP
|
|
15
|
+
* @perf disk O(file); browser tries installed browsers in order, stops on hit
|
|
16
|
+
* @concurrency stateless; sources own their own IO
|
|
17
|
+
* @test tests/unit/engine/cookie-source.test.ts
|
|
18
|
+
* @stability experimental
|
|
19
|
+
* @since 2026-05-30
|
|
20
|
+
*/
|
|
21
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
const SITE_RE = /^[a-zA-Z0-9._-]+$/;
|
|
24
|
+
export function cookieDir() {
|
|
25
|
+
return (process.env.UNICLI_COOKIE_DIR ??
|
|
26
|
+
join(process.env.HOME ?? "~", ".unicli", "cookies"));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read the on-disk cookie file, distinguishing absent from corrupt. The old
|
|
30
|
+
* loadCookies collapsed a truncated / wrong-shaped file to null ("no auth"),
|
|
31
|
+
* hiding the real cause; this returns a typed corrupt with a detail.
|
|
32
|
+
*/
|
|
33
|
+
export function readDiskCookies(site) {
|
|
34
|
+
if (!SITE_RE.test(site)) {
|
|
35
|
+
return { kind: "corrupt", detail: `invalid site name "${site}"` };
|
|
36
|
+
}
|
|
37
|
+
const path = join(cookieDir(), `${site}.json`);
|
|
38
|
+
if (!existsSync(path))
|
|
39
|
+
return { kind: "absent" };
|
|
40
|
+
let raw;
|
|
41
|
+
try {
|
|
42
|
+
raw = readFileSync(path, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return {
|
|
46
|
+
kind: "corrupt",
|
|
47
|
+
detail: `cannot read ${path}: ${err instanceof Error ? err.message : String(err)}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
let parsed;
|
|
51
|
+
try {
|
|
52
|
+
parsed = JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { kind: "corrupt", detail: `${path} is not valid JSON` };
|
|
56
|
+
}
|
|
57
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
58
|
+
return { kind: "corrupt", detail: `${path} is not a {name: value} object` };
|
|
59
|
+
}
|
|
60
|
+
return { kind: "ok", cookies: parsed };
|
|
61
|
+
}
|
|
62
|
+
/** Resolve the cookie domain the same way the legacy loader did. */
|
|
63
|
+
export function resolveCookieDomain(site, domain) {
|
|
64
|
+
let resolved = domain ?? site.replace(/_/g, ".");
|
|
65
|
+
if (!resolved.includes("."))
|
|
66
|
+
resolved = `${resolved}.com`;
|
|
67
|
+
return resolved;
|
|
68
|
+
}
|
|
69
|
+
async function defaultReadBrowser(domain) {
|
|
70
|
+
let mod;
|
|
71
|
+
try {
|
|
72
|
+
mod = await import("./chromium-cookies.js");
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return {
|
|
76
|
+
kind: "error",
|
|
77
|
+
reasons: [
|
|
78
|
+
{
|
|
79
|
+
source: "browser",
|
|
80
|
+
code: "module_load_failed",
|
|
81
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const installed = mod.detectInstalledBrowsers();
|
|
87
|
+
if (installed.length === 0)
|
|
88
|
+
return { kind: "none" };
|
|
89
|
+
const reasons = [];
|
|
90
|
+
for (const browser of installed) {
|
|
91
|
+
try {
|
|
92
|
+
const record = mod.readCookiesAsRecord({ browser, domain });
|
|
93
|
+
if (Object.keys(record).length > 0)
|
|
94
|
+
return { kind: "ok", cookies: record };
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
const code = err instanceof mod.ChromiumCookieError
|
|
98
|
+
? err.code
|
|
99
|
+
: "browser_read_failed";
|
|
100
|
+
reasons.push({
|
|
101
|
+
source: "browser",
|
|
102
|
+
code,
|
|
103
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return reasons.length > 0 ? { kind: "error", reasons } : { kind: "none" };
|
|
108
|
+
}
|
|
109
|
+
async function defaultReadCdp(domain) {
|
|
110
|
+
const { extractCookiesViaCDP } = await import("./cookie-extractor.js");
|
|
111
|
+
return extractCookiesViaCDP(domain);
|
|
112
|
+
}
|
|
113
|
+
export const defaultCookieSources = {
|
|
114
|
+
readDisk: readDiskCookies,
|
|
115
|
+
readBrowser: defaultReadBrowser,
|
|
116
|
+
readCdp: defaultReadCdp,
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Acquire cookies across disk → browser → CDP, collecting the real cause of
|
|
120
|
+
* each source's failure. Never throws: a miss with no underlying fault is
|
|
121
|
+
* "absent"; a miss where a source actually errored (Keychain denial, corrupt
|
|
122
|
+
* file, v20 encryption, CDP down) is "error" with the reasons.
|
|
123
|
+
*
|
|
124
|
+
* Domain precedence and the disk-first / browser / CDP order match the legacy
|
|
125
|
+
* loadCookiesWithCDP exactly, so the projection wrappers preserve behavior.
|
|
126
|
+
*/
|
|
127
|
+
export async function loadCookiesWithDiagnostics(site, domain, sources = defaultCookieSources, opts = {}) {
|
|
128
|
+
const reasons = [];
|
|
129
|
+
// Refresh-after-401 skips disk: the on-disk cookies are exactly the stale
|
|
130
|
+
// ones that just failed, so re-acquisition must go straight to the live
|
|
131
|
+
// browser / CDP sources.
|
|
132
|
+
if (!opts.skipDisk) {
|
|
133
|
+
const disk = sources.readDisk(site);
|
|
134
|
+
if (disk.kind === "ok") {
|
|
135
|
+
return { status: "loaded", source: "disk", cookies: disk.cookies };
|
|
136
|
+
}
|
|
137
|
+
if (disk.kind === "corrupt") {
|
|
138
|
+
reasons.push({
|
|
139
|
+
source: "disk",
|
|
140
|
+
code: "corrupt_file",
|
|
141
|
+
detail: disk.detail,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const cookieDomain = resolveCookieDomain(site, domain);
|
|
146
|
+
if (process.env.UNICLI_COOKIE_NO_BROWSER !== "1") {
|
|
147
|
+
const browser = await sources.readBrowser(cookieDomain);
|
|
148
|
+
if (browser.kind === "ok" && Object.keys(browser.cookies).length > 0) {
|
|
149
|
+
return { status: "loaded", source: "browser", cookies: browser.cookies };
|
|
150
|
+
}
|
|
151
|
+
if (browser.kind === "error")
|
|
152
|
+
reasons.push(...browser.reasons);
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const cdp = await sources.readCdp(cookieDomain);
|
|
156
|
+
if (Object.keys(cdp).length > 0) {
|
|
157
|
+
return { status: "loaded", source: "cdp", cookies: cdp };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
reasons.push({
|
|
162
|
+
source: "cdp",
|
|
163
|
+
code: "cdp_unavailable",
|
|
164
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return reasons.length > 0
|
|
168
|
+
? { status: "error", reasons }
|
|
169
|
+
: { status: "absent" };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Turn a non-loaded outcome into an agent-actionable message + suggestion.
|
|
173
|
+
* This is where the no-silent-failure win is spent: Keychain denial, v20
|
|
174
|
+
* encryption, and a corrupt file each get a DISTINCT next step instead of the
|
|
175
|
+
* legacy one-size-fits-all "run unicli auth setup".
|
|
176
|
+
*/
|
|
177
|
+
export function describeCookieFailure(outcome, site, domain) {
|
|
178
|
+
const d = resolveCookieDomain(site, domain);
|
|
179
|
+
if (outcome.status === "absent") {
|
|
180
|
+
return {
|
|
181
|
+
message: `No cookies found for "${site}".`,
|
|
182
|
+
suggestion: `Sign in to https://${d} in Chrome, then run: unicli auth import ${site} --domain ${d}`,
|
|
183
|
+
retryable: false,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const codes = outcome.reasons.map((r) => `${r.source}:${r.code}`).join(", ");
|
|
187
|
+
const has = (code) => outcome.reasons.some((r) => r.code === code);
|
|
188
|
+
let suggestion;
|
|
189
|
+
if (has("keychain_denied")) {
|
|
190
|
+
suggestion = `Chrome cookie decryption was denied by the macOS Keychain. Grant access when prompted, or run: unicli auth import ${site} --domain ${d}`;
|
|
191
|
+
}
|
|
192
|
+
else if (has("encryption_unsupported")) {
|
|
193
|
+
suggestion = `Chrome's app-bound encryption (v20) blocks direct cookie reads. Start Chrome via "unicli browser start" (CDP path), or run: unicli auth import ${site} --domain ${d}`;
|
|
194
|
+
}
|
|
195
|
+
else if (has("corrupt_file")) {
|
|
196
|
+
suggestion = `The saved cookie file is unreadable. Re-import: unicli auth import ${site} --domain ${d}`;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
suggestion = `Could not acquire cookies (${codes}). Sign in to https://${d}, then run: unicli auth import ${site} --domain ${d}`;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
message: `Failed to load cookies for "${site}" — ${codes}.`,
|
|
203
|
+
suggestion,
|
|
204
|
+
retryable: false,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=cookie-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-source.js","sourceRoot":"","sources":["../../src/engine/cookie-source.ts"],"names":[],"mappings":"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"}
|
package/dist/engine/cookies.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* @owner src::engine::cookies
|
|
3
|
+
* @does Cookie front door for adapters — thin projections over the
|
|
4
|
+
* structured acquisition core in cookie-source.ts (disk read,
|
|
5
|
+
* header formatting, multi-source load, refresh).
|
|
6
|
+
* @needs ./cookie-source, ./cookie-extractor (saveCookies)
|
|
7
|
+
* @feeds adapters (loadCookiesWithCDP/formatCookieHeader), executor,
|
|
8
|
+
* dispatch/social (refreshCookiesFromBrowser), commands/auth
|
|
9
|
+
* @breaks loadCookies/loadCookiesWithCDP return null on miss (back-compat);
|
|
10
|
+
* acquireCookies returns a typed CookieLoadOutcome that names the
|
|
11
|
+
* real cause — callers that need the cause use acquireCookies
|
|
12
|
+
* @invariants loadCookiesWithCDP cookies === acquireCookies(...).cookies on load
|
|
13
|
+
* @side-effects acquireCookies persists browser/CDP cookies to disk (best-effort)
|
|
14
|
+
* @test tests/unit/engine/cookie-source.test.ts, cookie-refresh-format.test.ts
|
|
15
|
+
* @stability stable
|
|
16
|
+
* @since 2026-05-30
|
|
6
17
|
*/
|
|
18
|
+
import { type CookieLoadOutcome } from "./cookie-source.js";
|
|
7
19
|
/**
|
|
8
|
-
* Load cookies for a site from disk.
|
|
9
|
-
*
|
|
20
|
+
* Load cookies for a site from disk. Returns null when the file is absent OR
|
|
21
|
+
* unreadable; callers needing to tell those apart use `acquireCookies`.
|
|
10
22
|
*/
|
|
11
23
|
export declare function loadCookies(site: string): Record<string, string> | null;
|
|
12
24
|
/**
|
|
@@ -14,32 +26,27 @@ export declare function loadCookies(site: string): Record<string, string> | null
|
|
|
14
26
|
* Example: "SESSDATA=abc; bili_jct=def"
|
|
15
27
|
*/
|
|
16
28
|
export declare function formatCookieHeader(cookies: Record<string, string>): string;
|
|
17
|
-
/**
|
|
18
|
-
* Validate that a cookie file has all required keys.
|
|
19
|
-
*/
|
|
29
|
+
/** Validate that a cookie file has all required keys. */
|
|
20
30
|
export declare function validateCookies(site: string, requiredKeys: string[]): {
|
|
21
31
|
valid: boolean;
|
|
22
32
|
missing: string[];
|
|
23
33
|
};
|
|
34
|
+
/** Get the cookie directory path (for display in auth commands). */
|
|
35
|
+
export declare function getCookieDir(): string;
|
|
24
36
|
/**
|
|
25
|
-
*
|
|
37
|
+
* Acquire cookies across disk → browser → CDP, returning the structured outcome
|
|
38
|
+
* (loaded / absent / error+reasons). On a non-disk load, persists to
|
|
39
|
+
* ~/.unicli/cookies for offline reuse. This is the front door for callers that
|
|
40
|
+
* need to surface WHY acquisition failed (Keychain denial, v20 encryption,
|
|
41
|
+
* corrupt file) instead of a bare null.
|
|
26
42
|
*/
|
|
27
|
-
export declare function
|
|
43
|
+
export declare function acquireCookies(site: string, domain?: string, opts?: {
|
|
44
|
+
skipDisk?: boolean;
|
|
45
|
+
}): Promise<CookieLoadOutcome>;
|
|
28
46
|
/**
|
|
29
|
-
* Load cookies with multi-source fallback.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* 1. ~/.unicli/cookies/<site>.json — explicit user import
|
|
33
|
-
* 2. browser local DB (Chrome/Arc/Dia/…) — direct SQLite read, no browser launch
|
|
34
|
-
* 3. CDP — connects to Chrome debug port (legacy)
|
|
35
|
-
*
|
|
36
|
-
* The browser disk source is the new default for `strategy: cookie` adapters:
|
|
37
|
-
* it works whether the browser is open or closed, never opens a new tab, and
|
|
38
|
-
* needs neither extension nor daemon. Successful reads are persisted to
|
|
39
|
-
* ~/.unicli/cookies for offline reuse.
|
|
40
|
-
*
|
|
41
|
-
* Set `UNICLI_COOKIE_NO_BROWSER=1` to skip the browser-disk step (e.g., in CI
|
|
42
|
-
* where the macOS Keychain prompt would block).
|
|
47
|
+
* Load cookies with multi-source fallback (disk → browser DB → CDP). Returns
|
|
48
|
+
* null on any miss. Behavior-compatible with every existing adapter consumer;
|
|
49
|
+
* the structured cause is available via `acquireCookies`.
|
|
43
50
|
*/
|
|
44
51
|
export declare function loadCookiesWithCDP(site: string, domain?: string): Promise<Record<string, string> | null>;
|
|
45
52
|
export interface CookieRefreshResult {
|
|
@@ -51,5 +58,10 @@ export interface CookieRefreshResult {
|
|
|
51
58
|
cookies?: string[];
|
|
52
59
|
suggestion?: string;
|
|
53
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Re-acquire cookies from the live browser / CDP after an auth failure. Skips
|
|
63
|
+
* the on-disk file (those are the stale cookies that just failed) and reports
|
|
64
|
+
* the real cause on failure rather than a generic message.
|
|
65
|
+
*/
|
|
54
66
|
export declare function refreshCookiesFromBrowser(site: string, domain?: string): Promise<CookieRefreshResult>;
|
|
55
67
|
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/engine/cookies.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/engine/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAG5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGvE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAI1E;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,GACrB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAKvC;AAED,oEAAoE;AACpE,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAgB5B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAGxC;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CA+B9B"}
|