zelari-code 2.46.2 → 2.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cli/automations/browser/cookieDisk.js +56 -0
- package/dist/cli/automations/browser/cookieDisk.js.map +1 -0
- package/dist/cli/automations/browser/pageAdapter.js +130 -0
- package/dist/cli/automations/browser/pageAdapter.js.map +1 -0
- package/dist/cli/automations/browser/selectors.js +178 -0
- package/dist/cli/automations/browser/selectors.js.map +1 -0
- package/dist/cli/automations/browser/session.js +243 -0
- package/dist/cli/automations/browser/session.js.map +1 -0
- package/dist/cli/budget/budgetRuntime.js +9 -13
- package/dist/cli/budget/budgetRuntime.js.map +1 -1
- package/dist/cli/budget/essentialBashConfig.js +118 -0
- package/dist/cli/budget/essentialBashConfig.js.map +1 -0
- package/dist/cli/headless/runOneTurn.js +36 -6
- package/dist/cli/headless/runOneTurn.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +30 -7
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/keyStore.js +2 -0
- package/dist/cli/keyStore.js.map +1 -1
- package/dist/cli/kraken/candidateRegistry.js +13 -0
- package/dist/cli/kraken/candidateRegistry.js.map +1 -1
- package/dist/cli/kraken/executor.js +200 -22
- package/dist/cli/kraken/executor.js.map +1 -1
- package/dist/cli/kraken/narrativeFloor.js +23 -0
- package/dist/cli/kraken/narrativeFloor.js.map +1 -0
- package/dist/cli/kraken/planner.js +27 -0
- package/dist/cli/kraken/planner.js.map +1 -1
- package/dist/cli/kraken/strictWaiver.js +22 -0
- package/dist/cli/kraken/strictWaiver.js.map +1 -0
- package/dist/cli/kraken/unresolvedGate.js +32 -0
- package/dist/cli/kraken/unresolvedGate.js.map +1 -0
- package/dist/cli/kraken/verificationBridge.js +67 -8
- package/dist/cli/kraken/verificationBridge.js.map +1 -1
- package/dist/cli/kraken/verifyHonestVerdict.js +30 -0
- package/dist/cli/kraken/verifyHonestVerdict.js.map +1 -0
- package/dist/cli/kraken/worktreeMergeRollback.js +277 -0
- package/dist/cli/kraken/worktreeMergeRollback.js.map +1 -0
- package/dist/cli/kraken/worktreeScheduling.js +11 -0
- package/dist/cli/kraken/worktreeScheduling.js.map +1 -1
- package/dist/cli/main.bundled.js +3278 -1483
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +1 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/modelDiscovery.js +5 -1
- package/dist/cli/modelDiscovery.js.map +1 -1
- package/dist/cli/museOAuth.js +384 -0
- package/dist/cli/museOAuth.js.map +1 -0
- package/dist/cli/oauthDesktop.js +18 -1
- package/dist/cli/oauthDesktop.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +1 -0
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/provider/resolveStream.js +2 -0
- package/dist/cli/provider/resolveStream.js.map +1 -1
- package/dist/cli/providerConfig.js +2 -0
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/refreshRegistry.js +6 -0
- package/dist/cli/refreshRegistry.js.map +1 -1
- package/dist/cli/runHeadless.js +184 -100
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/slashCommands.js +6 -6
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/krakenGraph.js +25 -7
- package/dist/cli/slashHandlers/krakenGraph.js.map +1 -1
- package/dist/cli/slashHandlers/provider.js +14 -4
- package/dist/cli/slashHandlers/provider.js.map +1 -1
- package/dist/cli/thinking.js +1 -0
- package/dist/cli/thinking.js.map +1 -1
- package/dist/cli/thinkingCapability.js +2 -0
- package/dist/cli/thinkingCapability.js.map +1 -1
- package/dist/cli/toolRegistry.js +22 -3
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/bashWriteWatch.js +195 -0
- package/dist/cli/tools/bashWriteWatch.js.map +1 -0
- package/dist/cli/tools/krakenModel.js +50 -0
- package/dist/cli/tools/krakenModel.js.map +1 -1
- package/dist/cli/tools/krakenRadio.js +50 -5
- package/dist/cli/tools/krakenRadio.js.map +1 -1
- package/dist/cli/tools/krakenWorktree.js +35 -6
- package/dist/cli/tools/krakenWorktree.js.map +1 -1
- package/dist/cli/tools/taskTool.js +328 -34
- package/dist/cli/tools/taskTool.js.map +1 -1
- package/dist/cli/tools/verifyDebtSpine.js +117 -0
- package/dist/cli/tools/verifyDebtSpine.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ By **[Anathema Studio](https://anathema-studio.com/)** ·
|
|
|
42
42
|
|
|
43
43
|
📖 **[Full user guide](./docs/GUIDA.md)** · **[Reference guide](./docs/GUIDE.md)** — install, TUI, slash commands, council, skills, workspace, headless, MCP, Desktop.
|
|
44
44
|
|
|
45
|
-
**Zelari Code** is an open-source **coding orchestrator** for the terminal — the agent that doesn't trust itself. The default dispatch is the **kraken** super-agent (aliases `agent`/`single`) spawning explore/general/verify tentacles; a six-role **council** pipeline (Caronte, Nettuno, Gerione, Plutone, Minosse, Lucifero) is there when the work earns a second opinion; optional **zelari** missions loop until a deliverable is *proven* done — done means verified, not claimed. It ships a rich TUI (Ink + React), slash commands, plan/build phases, and provider-agnostic LLM streaming (OpenAI-compatible, xAI Grok, ChatGPT, Anthropic, GLM/Z.AI, MiniMax, DeepSeek). OAuth via `/login grok`, `/login chatgpt`, `/login anthropic`. The reusable runtime is published as **[`@zelari/core`](https://www.npmjs.com/package/@zelari/core)** (Apache-2.0).
|
|
45
|
+
**Zelari Code** is an open-source **coding orchestrator** for the terminal — the agent that doesn't trust itself. The default dispatch is the **kraken** super-agent (aliases `agent`/`single`) spawning explore/general/verify tentacles; a six-role **council** pipeline (Caronte, Nettuno, Gerione, Plutone, Minosse, Lucifero) is there when the work earns a second opinion; optional **zelari** missions loop until a deliverable is *proven* done — done means verified, not claimed. It ships a rich TUI (Ink + React), slash commands, plan/build phases, and provider-agnostic LLM streaming (OpenAI-compatible, xAI Grok, ChatGPT, Anthropic, Muse, GLM/Z.AI, MiniMax, DeepSeek). OAuth via `/login grok`, `/login chatgpt`, `/login anthropic`, `/login muse`. The reusable runtime is published as **[`@zelari/core`](https://www.npmjs.com/package/@zelari/core)** (Apache-2.0).
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
# Zero install — runs from npm's cache (cold-cache start):
|
|
@@ -61,7 +61,7 @@ zelari-code
|
|
|
61
61
|
| **npm** | **≥ 10** | Required to reproduce the workspace lockfile; use the version pinned by `packageManager`. |
|
|
62
62
|
| **OS** | Linux, macOS, Windows 10/11 | Tested on Pop!_OS 24.04, macOS 15, Windows 11. Windows requires Git Bash (auto-detected). |
|
|
63
63
|
| **Disk** | ~50 MB for the CLI + `@zelari/core` | Models are not bundled — provider APIs are remote. |
|
|
64
|
-
| **Account + API key** | 1 of: xAI Grok, ChatGPT, Anthropic, OpenAI-compatible, GLM/Z.AI, MiniMax, DeepSeek | OAuth via `/login grok`, `/login chatgpt`, `/login anthropic`. |
|
|
64
|
+
| **Account + API key** | 1 of: xAI Grok, ChatGPT, Anthropic, Muse, OpenAI-compatible, GLM/Z.AI, MiniMax, DeepSeek | OAuth via `/login grok`, `/login chatgpt`, `/login anthropic`, `/login muse`. |
|
|
65
65
|
|
|
66
66
|
### Optional (advanced tools)
|
|
67
67
|
|
|
@@ -218,7 +218,7 @@ Full reference: **[docs/GUIDA.md](./docs/GUIDA.md#comandi-slash)** (all flags, e
|
|
|
218
218
|
|---|---|
|
|
219
219
|
| `/help` | List all commands + loaded skills |
|
|
220
220
|
| `/exit` | Exit the CLI |
|
|
221
|
-
| `/login <provider> [key]` | Set API key; `/login grok\|chatgpt\|anthropic` starts OAuth |
|
|
221
|
+
| `/login <provider> [key]` | Set API key; `/login grok\|chatgpt\|anthropic\|muse` starts OAuth |
|
|
222
222
|
| `/provider`, `/provider <id>` | Show / switch LLM provider |
|
|
223
223
|
| `/provider custom <url>` | Self-hosted endpoint (Ollama, LM Studio, …) |
|
|
224
224
|
| `/model <name>`, `/models` | Set model / list discovered models |
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cookieDisk.ts — login-state from the Chromium cookie DB WITHOUT launching a
|
|
3
|
+
* browser. Desktop used to poll `automation health` every 30s, which spawned
|
|
4
|
+
* Chromium on the same persistent profile and raced the cookie store
|
|
5
|
+
* (false `relogin_required`, locked profiles, failed publishes).
|
|
6
|
+
*
|
|
7
|
+
* Cookie *names* are plaintext in Chromium's SQLite even when values are
|
|
8
|
+
* encrypted. Presence of the channel's auth cookies is enough for health.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
/** Chromium cookie DB locations under a persistent profile directory. */
|
|
13
|
+
export function cookieDbCandidates(profile) {
|
|
14
|
+
return [
|
|
15
|
+
path.join(profile, 'Default', 'Network', 'Cookies'),
|
|
16
|
+
path.join(profile, 'Default', 'Cookies'),
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
function isLockError(err) {
|
|
20
|
+
const code = err?.code;
|
|
21
|
+
return code === 'EBUSY' || code === 'EPERM' || code === 'EACCES';
|
|
22
|
+
}
|
|
23
|
+
function containsName(haystack, name) {
|
|
24
|
+
return haystack.includes(Buffer.from(name, 'utf8'));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Probe auth-cookie names in the profile's cookie DB.
|
|
28
|
+
* - present: every `wanted` name is in the file
|
|
29
|
+
* - missing: a cookie DB exists but at least one name is absent
|
|
30
|
+
* - locked: Chromium has the file open (do NOT report logged-out)
|
|
31
|
+
* - absent: no cookie DB yet (fresh profile)
|
|
32
|
+
*/
|
|
33
|
+
export async function probeSessionCookiesOnDisk(profile, wanted, read = (p) => readFile(p)) {
|
|
34
|
+
if (wanted.length === 0)
|
|
35
|
+
return 'absent';
|
|
36
|
+
let sawFile = false;
|
|
37
|
+
for (const file of cookieDbCandidates(profile)) {
|
|
38
|
+
try {
|
|
39
|
+
const buf = await read(file);
|
|
40
|
+
sawFile = true;
|
|
41
|
+
if (wanted.every((n) => containsName(buf, n)))
|
|
42
|
+
return 'present';
|
|
43
|
+
return 'missing';
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (isLockError(err))
|
|
47
|
+
return 'locked';
|
|
48
|
+
const code = err?.code;
|
|
49
|
+
if (code === 'ENOENT')
|
|
50
|
+
continue;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return sawFile ? 'missing' : 'absent';
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=cookieDisk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookieDisk.js","sourceRoot":"","sources":["../../../../src/cli/automations/browser/cookieDisk.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAM7B,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,IAAI,GAAI,GAAyC,EAAE,IAAI,CAAC;IAC9D,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,CAAC;AACnE,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,IAAY;IAClD,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAAe,EACf,MAAyB,EACzB,OAAyB,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE3C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAAE,OAAO,SAAS,CAAC;YAChE,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,WAAW,CAAC,GAAG,CAAC;gBAAE,OAAO,QAAQ,CAAC;YACtC,MAAM,IAAI,GAAI,GAAyC,EAAE,IAAI,CAAC;YAC9D,IAAI,IAAI,KAAK,QAAQ;gBAAE,SAAS;YAChC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* automations/browser/pageAdapter.ts — the Playwright page/context adapter (F3.1/F3.2).
|
|
3
|
+
*
|
|
4
|
+
* The minimal structural seams the automation orchestration talks to, plus the
|
|
5
|
+
* thin adapters that narrow a real Playwright page/context onto them. Keeping
|
|
6
|
+
* this separate from session.ts holds each module under the size budget and
|
|
7
|
+
* keeps the "what the orchestration sees" contract in one place.
|
|
8
|
+
*
|
|
9
|
+
* Playwright itself is never imported here — only structural (duck-typed)
|
|
10
|
+
* interfaces — so the orchestration stays unit-testable with fake pages.
|
|
11
|
+
*/
|
|
12
|
+
/** How long a fallback selector may take to appear before we call it absent. */
|
|
13
|
+
export const SELECTOR_TIMEOUT_MS = 5_000;
|
|
14
|
+
/** Adapt a real Playwright page onto the orchestration's BrowserPageLike seam. */
|
|
15
|
+
export function adaptPage(p) {
|
|
16
|
+
return {
|
|
17
|
+
goto: (url, opts) => p.goto(url, opts),
|
|
18
|
+
url: () => p.url(),
|
|
19
|
+
close: () => p.close(),
|
|
20
|
+
bringToFront: async () => {
|
|
21
|
+
await p.bringToFront?.();
|
|
22
|
+
},
|
|
23
|
+
hasSelector: async (selector, timeoutMs = SELECTOR_TIMEOUT_MS) => {
|
|
24
|
+
try {
|
|
25
|
+
if ((await p.$(selector)) !== null)
|
|
26
|
+
return true;
|
|
27
|
+
await p.waitForSelector(selector, { timeout: timeoutMs });
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
waitForSelector: (selector, opts) => p.waitForSelector(selector, opts),
|
|
35
|
+
click: (selector, opts) => p.click(selector, opts),
|
|
36
|
+
typeText: (selector, text, opts) => p.type(selector, text, { delay: opts?.delayMs }),
|
|
37
|
+
press: (key) => p.keyboard.press(key),
|
|
38
|
+
setInputFiles: (selector, files) => p.setInputFiles(selector, files),
|
|
39
|
+
screenshot: (path) => p.screenshot({ path }),
|
|
40
|
+
hrefs: async (selector) => p.$$eval(selector, (els) => els.map((e) => String(e.href ?? ''))),
|
|
41
|
+
linkTexts: async (selector) => p.$$eval(selector, (els) => els.map((e) => {
|
|
42
|
+
const a = e;
|
|
43
|
+
const article = typeof a.closest === 'function' ? a.closest('article') : null;
|
|
44
|
+
const text = (article?.textContent ?? a.textContent ?? '').trim();
|
|
45
|
+
return { href: String(a.href ?? ''), text };
|
|
46
|
+
})),
|
|
47
|
+
articlePermalinks: async () => p.$$eval("div[role='article']", (els) => {
|
|
48
|
+
const shapes = /(\/posts\/|permalink\.php|story\.php|\/share\/p\/)/;
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const e of els) {
|
|
51
|
+
const el = e;
|
|
52
|
+
const anchor = Array.from(el.querySelectorAll('a[href]'))
|
|
53
|
+
.map((a) => String(a.href ?? ''))
|
|
54
|
+
.find((h) => shapes.test(h));
|
|
55
|
+
const text = (el.textContent ?? '').replace(/\s+/g, ' ').slice(0, 600);
|
|
56
|
+
if (anchor !== undefined && anchor.length > 0)
|
|
57
|
+
out.push({ href: anchor, text });
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}),
|
|
61
|
+
controls: (selector) => p.$$eval(selector, (els) => els
|
|
62
|
+
.map((e) => {
|
|
63
|
+
const el = e;
|
|
64
|
+
const attr = (n) => typeof el.getAttribute === 'function' ? (el.getAttribute(n) ?? undefined) : undefined;
|
|
65
|
+
return {
|
|
66
|
+
tag: String(el.tagName ?? '').toLowerCase(),
|
|
67
|
+
role: attr('role'),
|
|
68
|
+
aria: attr('aria-label'),
|
|
69
|
+
testid: attr('data-testid'),
|
|
70
|
+
text: String(el.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, 100),
|
|
71
|
+
disabled: typeof el.hasAttribute === 'function' && el.hasAttribute('disabled') ? true : undefined,
|
|
72
|
+
};
|
|
73
|
+
})
|
|
74
|
+
.slice(0, 100)),
|
|
75
|
+
clickText: (scopeSelector, texts) => p.$$eval(scopeSelector, (els, labels) => {
|
|
76
|
+
const norm = (s) => String(s ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
77
|
+
const wanted = labels.map((l) => norm(l));
|
|
78
|
+
const vis = (node) => {
|
|
79
|
+
const r = node.getBoundingClientRect();
|
|
80
|
+
const st = getComputedStyle(node);
|
|
81
|
+
return (r.width > 2 &&
|
|
82
|
+
r.height > 2 &&
|
|
83
|
+
st.visibility !== 'hidden' &&
|
|
84
|
+
st.display !== 'none' &&
|
|
85
|
+
Number(st.opacity) > 0.1);
|
|
86
|
+
};
|
|
87
|
+
let hit = null;
|
|
88
|
+
for (const e of els) {
|
|
89
|
+
const el = e;
|
|
90
|
+
const aria = typeof el.getAttribute === 'function' ? el.getAttribute('aria-label') : null;
|
|
91
|
+
const inner = String(el.innerText ?? el.textContent ?? '');
|
|
92
|
+
const ariaN = norm(aria);
|
|
93
|
+
const textN = norm(inner);
|
|
94
|
+
if (!wanted.some((w) => w === ariaN || w === textN))
|
|
95
|
+
continue;
|
|
96
|
+
if (typeof el.hasAttribute === 'function' && (el.hasAttribute('disabled') || el.getAttribute?.('aria-disabled') === 'true'))
|
|
97
|
+
continue;
|
|
98
|
+
if (!vis(el))
|
|
99
|
+
continue;
|
|
100
|
+
if (typeof el.click !== 'function')
|
|
101
|
+
continue;
|
|
102
|
+
hit = { click: el.click.bind(el), aria, text: inner };
|
|
103
|
+
}
|
|
104
|
+
if (!hit)
|
|
105
|
+
return null;
|
|
106
|
+
hit.click();
|
|
107
|
+
return norm(hit.aria ?? hit.text);
|
|
108
|
+
}, texts),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Adapt a real Playwright persistent context onto PersistentContextLike. */
|
|
112
|
+
export function adaptContext(ctx) {
|
|
113
|
+
return {
|
|
114
|
+
newPage: async () => adaptPage(await ctx.newPage()),
|
|
115
|
+
pages: () => ctx.pages().map(adaptPage),
|
|
116
|
+
close: () => ctx.close(),
|
|
117
|
+
cookies: typeof ctx.cookies === 'function'
|
|
118
|
+
? async () => (await ctx.cookies?.()) ?? []
|
|
119
|
+
: undefined,
|
|
120
|
+
onClose: (cb) => {
|
|
121
|
+
try {
|
|
122
|
+
ctx.on('close', cb);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// 'close' unsupported on a partial fake — the timeout still bounds us.
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=pageAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pageAdapter.js","sourceRoot":"","sources":["../../../../src/cli/automations/browser/pageAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* automations/browser/selectors.ts — per-channel login selectors (F3.1).
|
|
3
|
+
*
|
|
4
|
+
* The selectors are DATA, not code: one JSON file per channel under
|
|
5
|
+
* `./selectors/<channel>.json`, editable at runtime (no recompile). A broken
|
|
6
|
+
* CSS/selector set is diagnosed by `zelari-code automation probe <channel>`,
|
|
7
|
+
* never by shipping new code. Each list (`loggedIn` / `loggedOut`) is an
|
|
8
|
+
* ordered fallback chain — the FIRST matching label wins.
|
|
9
|
+
*
|
|
10
|
+
* Resolution order for the data directory: explicit `dir` override →
|
|
11
|
+
* `ZELARI_BROWSER_SELECTORS_DIR` → next to this module → the source tree
|
|
12
|
+
* (dev: bundled CLI running from the repo root). Unknown channel ⇒ hard error.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { readFile } from 'node:fs/promises';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
/** Channels that have a browser session manager. Keep in sync with ./selectors. */
|
|
20
|
+
export const SUPPORTED_CHANNELS = ['x', 'facebook'];
|
|
21
|
+
/** Zod enum reused by the CLI for `--channel` / positional parsing. */
|
|
22
|
+
export const ChannelSchema = z.enum(SUPPORTED_CHANNELS);
|
|
23
|
+
/** One labelled selector in a fallback chain. */
|
|
24
|
+
export const SelectorEntrySchema = z.object({
|
|
25
|
+
label: z.string().min(1),
|
|
26
|
+
selector: z.string().min(1),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Composer/publish fallback chains (F3.2). Every field is an ordered chain; the
|
|
30
|
+
* ones the flow cannot run without (`textbox`, `postButton`) are required, the
|
|
31
|
+
* rest are optional so a channel that only supports login (or a channel whose
|
|
32
|
+
* publish support lands later) still validates.
|
|
33
|
+
*/
|
|
34
|
+
export const PublishSelectorsSchema = z.object({
|
|
35
|
+
/** Click target that opens the composer from the home timeline. */
|
|
36
|
+
composeEntry: z.array(SelectorEntrySchema).min(1).optional(),
|
|
37
|
+
/** The contenteditable tweet textbox (required to type). */
|
|
38
|
+
textbox: z.array(SelectorEntrySchema).min(1),
|
|
39
|
+
/** The "Post" button (required to publish). */
|
|
40
|
+
postButton: z.array(SelectorEntrySchema).min(1),
|
|
41
|
+
/**
|
|
42
|
+
* Multi-step composer "Next" buttons (it "Avanti", en "Next", …): clicked
|
|
43
|
+
* BETWEEN typing and the post button when Facebook renders the stepped
|
|
44
|
+
* composer — the final "Pubblica/Post" only appears after advancing.
|
|
45
|
+
*/
|
|
46
|
+
advanceButtons: z.array(SelectorEntrySchema).min(1).optional(),
|
|
47
|
+
/** Post-publish toast "View" link — the PRIMARY permalink source. */
|
|
48
|
+
toastView: z.array(SelectorEntrySchema).min(1).optional(),
|
|
49
|
+
/** Media file input (optional; missing ⇒ text-only with a warning). */
|
|
50
|
+
fileInput: z.array(SelectorEntrySchema).min(1).optional(),
|
|
51
|
+
/** Nav link to the author's own profile — the FALLBACK permalink source. */
|
|
52
|
+
profileLink: z.array(SelectorEntrySchema).min(1).optional(),
|
|
53
|
+
/** Post anchors on the profile — the FALLBACK permalink source. */
|
|
54
|
+
profilePost: z.array(SelectorEntrySchema).min(1).optional(),
|
|
55
|
+
/**
|
|
56
|
+
* Post-navigation logged-OUT markers (F3.3 hardening): when any of these is
|
|
57
|
+
* present after loading the composer landing page, the cookies passed the
|
|
58
|
+
* local gate but the SERVER rejected the session — the run must stop with
|
|
59
|
+
* ReloginRequired BEFORE any click/typing (localized: 'Accedi', 'Log in', …).
|
|
60
|
+
*/
|
|
61
|
+
loggedOutMarkers: z.array(SelectorEntrySchema).min(1).optional(),
|
|
62
|
+
});
|
|
63
|
+
/** The parsed shape of one `selectors/<channel>.json` file. */
|
|
64
|
+
export const ChannelSelectorsSchema = z.object({
|
|
65
|
+
channel: z.string().min(1),
|
|
66
|
+
loginUrl: z.string().min(1),
|
|
67
|
+
/** Ordered fallback chain: first present selector means "logged in". */
|
|
68
|
+
loggedIn: z.array(SelectorEntrySchema).min(1),
|
|
69
|
+
/** Ordered fallback chain: first present selector means "logged out". */
|
|
70
|
+
loggedOut: z.array(SelectorEntrySchema).min(1),
|
|
71
|
+
/**
|
|
72
|
+
* Locale-proof PRIMARY login signal: session/auth cookie names — logged in
|
|
73
|
+
* iff ALL of them are present in the persistent profile (X `auth_token`,
|
|
74
|
+
* Facebook `c_user`+`xs`). DOM selectors stay as the fallback because
|
|
75
|
+
* aria-labels get translated and the DOMs are A/B-shuffled.
|
|
76
|
+
*/
|
|
77
|
+
sessionCookies: z.array(z.string().min(1)).min(1).optional(),
|
|
78
|
+
/**
|
|
79
|
+
* GDPR cookie-wall "allow all" buttons (localized). EU accounts see this
|
|
80
|
+
* interstitial BEFORE the feed renders — login/health/publish flows dismiss
|
|
81
|
+
* it (the same consent the user gave in the headed session) or every
|
|
82
|
+
* downstream selector would miss. Optional ⇒ older files still validate.
|
|
83
|
+
*/
|
|
84
|
+
consentButtons: z.array(SelectorEntrySchema).min(1).optional(),
|
|
85
|
+
/** Composer/publish chains (F3.2). Optional ⇒ facebook.json still validates. */
|
|
86
|
+
publish: PublishSelectorsSchema.optional(),
|
|
87
|
+
notes: z.string().optional(),
|
|
88
|
+
/** Free-form note (e.g. "UNVERIFIED-GUESSES" for the publish chains). */
|
|
89
|
+
_comment: z.string().optional(),
|
|
90
|
+
});
|
|
91
|
+
/** Type guard for a supported channel id. */
|
|
92
|
+
export function isSupportedChannel(channel) {
|
|
93
|
+
return SUPPORTED_CHANNELS.includes(channel);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Selectors-dir candidates derived from the MODULE location by walking up to the
|
|
97
|
+
* package root (bounded). This is what makes the bundled CLI cwd-INDEPENDENT: the
|
|
98
|
+
* dev bundle sits at `<pkgRoot>/dist/cli/main.bundled.js`, so the source data at
|
|
99
|
+
* `<pkgRoot>/src/cli/automations/browser/selectors` is a couple of levels up —
|
|
100
|
+
* regardless of the `process.cwd()` a spawner (e.g. the Desktop, whose cwd is the
|
|
101
|
+
* user's workdir, not the repo root) chose. Exported for the unit test.
|
|
102
|
+
*/
|
|
103
|
+
export function moduleRelativeSelectorsDirs(here) {
|
|
104
|
+
const out = [];
|
|
105
|
+
let cur = here;
|
|
106
|
+
for (let i = 0; i < 8; i += 1) {
|
|
107
|
+
const parent = path.dirname(cur);
|
|
108
|
+
if (parent === cur)
|
|
109
|
+
break; // filesystem root
|
|
110
|
+
out.push(path.join(parent, 'src', 'cli', 'automations', 'browser', 'selectors'));
|
|
111
|
+
out.push(path.join(parent, 'dist', 'cli', 'automations', 'browser', 'selectors'));
|
|
112
|
+
cur = parent;
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Locate the directory holding `selectors/*.json`.
|
|
118
|
+
* Explicit `dir` wins; then the env override; then the candidates that work in
|
|
119
|
+
* src (vitest), in a bundled CLI run from ANY cwd (module-relative walk up), and
|
|
120
|
+
* the `process.cwd()` source tree as a last resort.
|
|
121
|
+
*/
|
|
122
|
+
export function selectorsDir(dir) {
|
|
123
|
+
if (dir && dir.length > 0)
|
|
124
|
+
return dir;
|
|
125
|
+
const env = process.env.ZELARI_BROWSER_SELECTORS_DIR?.trim();
|
|
126
|
+
if (env)
|
|
127
|
+
return env;
|
|
128
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
129
|
+
const candidates = [
|
|
130
|
+
path.join(here, 'selectors'), // src/cli/automations/browser/selectors (tsx/vitest)
|
|
131
|
+
path.join(here, 'automations', 'browser', 'selectors'), // dist/cli (bundled asset)
|
|
132
|
+
// Bundled CLI spawned from another cwd: reach the source tree relative to
|
|
133
|
+
// THIS module (the Desktop sets cwd = the user's workdir, not the repo root).
|
|
134
|
+
...moduleRelativeSelectorsDirs(here),
|
|
135
|
+
path.join(process.cwd(), 'src', 'cli', 'automations', 'browser', 'selectors'),
|
|
136
|
+
];
|
|
137
|
+
for (const c of candidates) {
|
|
138
|
+
if (existsSync(c))
|
|
139
|
+
return c;
|
|
140
|
+
}
|
|
141
|
+
return candidates[0];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Load + validate the selectors for `channel`. Throws a descriptive Error on an
|
|
145
|
+
* unknown channel, a missing file, malformed JSON, a schema mismatch, or a file
|
|
146
|
+
* whose `channel` field does not match the requested one — the probe surfaces
|
|
147
|
+
* these verbatim.
|
|
148
|
+
*/
|
|
149
|
+
export async function loadSelectors(channel, dir) {
|
|
150
|
+
if (!isSupportedChannel(channel)) {
|
|
151
|
+
throw new Error(`unknown social channel: ${channel} (supported: ${SUPPORTED_CHANNELS.join(', ')})`);
|
|
152
|
+
}
|
|
153
|
+
const file = path.join(selectorsDir(dir), `${channel}.json`);
|
|
154
|
+
let raw;
|
|
155
|
+
try {
|
|
156
|
+
raw = await readFile(file, 'utf-8');
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
throw new Error(`selectors file not found: ${file}`);
|
|
160
|
+
}
|
|
161
|
+
let parsedJson;
|
|
162
|
+
try {
|
|
163
|
+
parsedJson = JSON.parse(raw);
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
throw new Error(`selectors file is malformed JSON: ${file} (${e instanceof Error ? e.message : String(e)})`);
|
|
167
|
+
}
|
|
168
|
+
const parsed = ChannelSelectorsSchema.safeParse(parsedJson);
|
|
169
|
+
if (!parsed.success) {
|
|
170
|
+
const issues = parsed.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`).join('; ');
|
|
171
|
+
throw new Error(`selectors file is invalid: ${file} (${issues})`);
|
|
172
|
+
}
|
|
173
|
+
if (parsed.data.channel !== channel) {
|
|
174
|
+
throw new Error(`selectors file ${file} declares channel "${parsed.data.channel}", expected "${channel}"`);
|
|
175
|
+
}
|
|
176
|
+
return parsed.data;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=selectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../../../src/cli/automations/browser/selectors.ts"],"names":[],"mappings":"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"}
|