opencandle 0.6.0 → 0.7.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 +10 -3
- package/dist/cli.js +36 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/infra/index.d.ts +0 -1
- package/dist/infra/index.js +0 -1
- package/dist/infra/index.js.map +1 -1
- package/dist/onboarding/connect.d.ts +2 -2
- package/dist/onboarding/connect.js +10 -3
- package/dist/onboarding/connect.js.map +1 -1
- package/dist/onboarding/provider-status.d.ts +48 -0
- package/dist/onboarding/provider-status.js +285 -0
- package/dist/onboarding/provider-status.js.map +1 -0
- package/dist/onboarding/providers.d.ts +85 -8
- package/dist/onboarding/providers.js +87 -9
- package/dist/onboarding/providers.js.map +1 -1
- package/dist/onboarding/state.d.ts +1 -0
- package/dist/onboarding/state.js +5 -0
- package/dist/onboarding/state.js.map +1 -1
- package/dist/onboarding/tool-tags.d.ts +12 -1
- package/dist/onboarding/tool-tags.js +31 -1
- package/dist/onboarding/tool-tags.js.map +1 -1
- package/dist/onboarding/validation.d.ts +2 -2
- package/dist/onboarding/validation.js.map +1 -1
- package/dist/pi/opencandle-extension.js +91 -15
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/tool-adapter.d.ts +4 -1
- package/dist/pi/tool-adapter.js +5 -4
- package/dist/pi/tool-adapter.js.map +1 -1
- package/dist/prompts/context-builder.js +1 -1
- package/dist/prompts/policy-cards.js +1 -1
- package/dist/prompts/policy-cards.js.map +1 -1
- package/dist/providers/external-tool-error.d.ts +10 -0
- package/dist/providers/external-tool-error.js +21 -0
- package/dist/providers/external-tool-error.js.map +1 -0
- package/dist/providers/reddit-cli.d.ts +36 -0
- package/dist/providers/reddit-cli.js +201 -0
- package/dist/providers/reddit-cli.js.map +1 -0
- package/dist/providers/reddit.d.ts +1 -1
- package/dist/providers/reddit.js +7 -35
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/twitter-cli.d.ts +40 -0
- package/dist/providers/twitter-cli.js +153 -0
- package/dist/providers/twitter-cli.js.map +1 -0
- package/dist/providers/twitter.d.ts +0 -8
- package/dist/providers/twitter.js +4 -54
- package/dist/providers/twitter.js.map +1 -1
- package/dist/providers/wrap-provider.js +30 -0
- package/dist/providers/wrap-provider.js.map +1 -1
- package/dist/providers/yahoo-finance.js +53 -32
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/planning.d.ts +1 -1
- package/dist/routing/planning.js.map +1 -1
- package/dist/runtime/answer-contracts.d.ts +1 -1
- package/dist/runtime/answer-contracts.js +12 -1
- package/dist/runtime/answer-contracts.js.map +1 -1
- package/dist/runtime/tool-defaults-wrapper.js +6 -2
- package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
- package/dist/sentiment/index.d.ts +1 -0
- package/dist/sentiment/index.js +1 -0
- package/dist/sentiment/index.js.map +1 -1
- package/dist/sentiment/insights.d.ts +17 -0
- package/dist/sentiment/insights.js +206 -0
- package/dist/sentiment/insights.js.map +1 -0
- package/dist/sentiment/pipeline.js +13 -1
- package/dist/sentiment/pipeline.js.map +1 -1
- package/dist/sentiment/scorer.d.ts +2 -0
- package/dist/sentiment/scorer.js +10 -1
- package/dist/sentiment/scorer.js.map +1 -1
- package/dist/sentiment/types.d.ts +2 -0
- package/dist/sentiment/types.js.map +1 -1
- package/dist/system-prompt.js +3 -7
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/index.d.ts +5 -2
- package/dist/tools/index.js +8 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/sentiment/insight-format.d.ts +2 -0
- package/dist/tools/sentiment/insight-format.js +36 -0
- package/dist/tools/sentiment/insight-format.js.map +1 -0
- package/dist/tools/sentiment/query-match.d.ts +3 -0
- package/dist/tools/sentiment/query-match.js +113 -0
- package/dist/tools/sentiment/query-match.js.map +1 -0
- package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
- package/dist/tools/sentiment/reddit-sentiment.js +263 -117
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
- package/dist/tools/sentiment/sentiment-summary.js +217 -201
- package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
- package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
- package/dist/tools/sentiment/twitter-sentiment.js +187 -64
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +4 -0
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/types/sentiment.d.ts +52 -0
- package/gui/server/invoke-tool.ts +17 -3
- package/gui/server/model-setup.ts +10 -3
- package/gui/server/projector.ts +6 -2
- package/gui/server/server.ts +18 -0
- package/gui/server/tool-metadata.ts +80 -16
- package/gui/server/ws-hub.ts +19 -0
- package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
- package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
- package/gui/web/dist/assets/{index-2KZtKBmu.css → index-hwbx24a5.css} +1 -1
- package/gui/web/dist/index.html +2 -2
- package/package.json +5 -6
- package/src/cli.ts +41 -0
- package/src/config.ts +27 -0
- package/src/infra/index.ts +0 -1
- package/src/onboarding/connect.ts +20 -4
- package/src/onboarding/provider-status.ts +410 -0
- package/src/onboarding/providers.ts +148 -18
- package/src/onboarding/state.ts +9 -0
- package/src/onboarding/tool-tags.ts +45 -2
- package/src/onboarding/validation.ts +2 -2
- package/src/pi/opencandle-extension.ts +115 -17
- package/src/pi/tool-adapter.ts +14 -4
- package/src/prompts/context-builder.ts +1 -1
- package/src/prompts/policy-cards.ts +1 -1
- package/src/providers/external-tool-error.ts +20 -0
- package/src/providers/reddit-cli.ts +317 -0
- package/src/providers/reddit.ts +7 -63
- package/src/providers/twitter-cli.ts +233 -0
- package/src/providers/twitter.ts +4 -73
- package/src/providers/wrap-provider.ts +34 -0
- package/src/providers/yahoo-finance.ts +65 -32
- package/src/routing/planning.ts +1 -0
- package/src/runtime/answer-contracts.ts +23 -2
- package/src/runtime/tool-defaults-wrapper.ts +12 -2
- package/src/sentiment/index.ts +1 -0
- package/src/sentiment/insights.ts +269 -0
- package/src/sentiment/pipeline.ts +13 -1
- package/src/sentiment/scorer.ts +12 -1
- package/src/sentiment/types.ts +3 -0
- package/src/system-prompt.ts +3 -7
- package/src/tools/index.ts +9 -8
- package/src/tools/sentiment/insight-format.ts +50 -0
- package/src/tools/sentiment/query-match.ts +117 -0
- package/src/tools/sentiment/reddit-sentiment.ts +354 -141
- package/src/tools/sentiment/sentiment-summary.ts +283 -237
- package/src/tools/sentiment/twitter-sentiment.ts +262 -78
- package/src/tools/sentiment/web-sentiment.ts +4 -0
- package/src/types/sentiment.ts +59 -0
- package/dist/infra/browser.d.ts +0 -35
- package/dist/infra/browser.js +0 -105
- package/dist/infra/browser.js.map +0 -1
- package/dist/tools/interaction/twitter-login.d.ts +0 -8
- package/dist/tools/interaction/twitter-login.js +0 -87
- package/dist/tools/interaction/twitter-login.js.map +0 -1
- package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +0 -1
- package/gui/web/dist/assets/index-CveNgtDg.js +0 -69
- package/src/infra/browser.ts +0 -113
- package/src/tools/interaction/twitter-login.ts +0 -105
package/src/infra/browser.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared stealth browser infrastructure using Camoufox (anti-detection Firefox).
|
|
3
|
-
* Provides a singleton browser instance that any tool can use for scraping.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* import { StealthBrowser } from "../infra/browser.js";
|
|
7
|
-
* const data = await StealthBrowser.fetchJson<MyType>(url);
|
|
8
|
-
* const result = await StealthBrowser.evaluate(url, () => document.title);
|
|
9
|
-
*/
|
|
10
|
-
import "./node-version.js";
|
|
11
|
-
import { Camoufox } from "camoufox-js";
|
|
12
|
-
import type { Browser, Page } from "playwright-core";
|
|
13
|
-
|
|
14
|
-
let browser: Browser | null = null;
|
|
15
|
-
let page: Page | null = null;
|
|
16
|
-
let launching: Promise<void> | null = null;
|
|
17
|
-
let pageQueue: Promise<void> = Promise.resolve();
|
|
18
|
-
|
|
19
|
-
async function ensureBrowser(): Promise<Page> {
|
|
20
|
-
if (page && browser?.isConnected()) return page;
|
|
21
|
-
|
|
22
|
-
// Prevent concurrent launches
|
|
23
|
-
if (launching) {
|
|
24
|
-
await launching;
|
|
25
|
-
if (page && browser?.isConnected()) return page;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
launching = (async () => {
|
|
29
|
-
const b = await Camoufox({ headless: true });
|
|
30
|
-
browser = b;
|
|
31
|
-
page = await b.newPage();
|
|
32
|
-
})();
|
|
33
|
-
|
|
34
|
-
await launching;
|
|
35
|
-
launching = null;
|
|
36
|
-
return page!;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function withPage<T>(fn: (p: Page) => Promise<T>): Promise<T> {
|
|
40
|
-
let resolve!: () => void;
|
|
41
|
-
const next = new Promise<void>((r) => {
|
|
42
|
-
resolve = r;
|
|
43
|
-
});
|
|
44
|
-
const prev = pageQueue;
|
|
45
|
-
pageQueue = next;
|
|
46
|
-
await prev;
|
|
47
|
-
try {
|
|
48
|
-
const p = await ensureBrowser();
|
|
49
|
-
return await fn(p);
|
|
50
|
-
} finally {
|
|
51
|
-
resolve();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const StealthBrowser = {
|
|
56
|
-
/**
|
|
57
|
-
* Navigate to a URL, run a JS function in the page context, and return the result.
|
|
58
|
-
*/
|
|
59
|
-
async evaluate<T>(url: string, fn: () => T | Promise<T>): Promise<T> {
|
|
60
|
-
return withPage(async (p) => {
|
|
61
|
-
await p.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
62
|
-
return p.evaluate(fn);
|
|
63
|
-
});
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Fetch JSON from a URL using the browser's session (cookies, TLS fingerprint).
|
|
68
|
-
* Useful for APIs that block Node.js fetch but allow real browsers.
|
|
69
|
-
*/
|
|
70
|
-
async fetchJson<T>(url: string): Promise<T> {
|
|
71
|
-
return withPage(async (p) => {
|
|
72
|
-
const result = await p.evaluate(async (fetchUrl: string) => {
|
|
73
|
-
const res = await fetch(fetchUrl, { credentials: "include" });
|
|
74
|
-
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
75
|
-
return res.json();
|
|
76
|
-
}, url);
|
|
77
|
-
return result as T;
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Run a custom async function in the browser page context.
|
|
83
|
-
* The page must already be on a relevant domain for cookies to work.
|
|
84
|
-
*/
|
|
85
|
-
async run<T>(fn: (page: Page) => Promise<T>): Promise<T> {
|
|
86
|
-
return withPage(async (p) => fn(p));
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Navigate to a URL and establish session cookies for that domain.
|
|
91
|
-
*/
|
|
92
|
-
async initSession(url: string): Promise<void> {
|
|
93
|
-
return withPage(async (p) => {
|
|
94
|
-
await p.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
95
|
-
});
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Close the browser. It will be re-launched on next use.
|
|
100
|
-
*/
|
|
101
|
-
async close(): Promise<void> {
|
|
102
|
-
if (browser) {
|
|
103
|
-
await browser.close().catch(() => {});
|
|
104
|
-
browser = null;
|
|
105
|
-
page = null;
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Clean up on process exit
|
|
111
|
-
process.on("exit", () => {
|
|
112
|
-
browser?.close().catch(() => {});
|
|
113
|
-
});
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
2
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { Type } from "@sinclair/typebox";
|
|
4
|
-
import type { BrowserContext } from "playwright-core";
|
|
5
|
-
|
|
6
|
-
interface TwitterLoginResult {
|
|
7
|
-
success: boolean;
|
|
8
|
-
message: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function runTwitterLogin(notify: (msg: string) => void): Promise<TwitterLoginResult> {
|
|
12
|
-
const { getBrowserProfileDir } = await import("../../infra/opencandle-paths.js");
|
|
13
|
-
const { Camoufox } = await import("camoufox-js");
|
|
14
|
-
|
|
15
|
-
const profileDir = getBrowserProfileDir();
|
|
16
|
-
mkdirSync(profileDir, { recursive: true });
|
|
17
|
-
|
|
18
|
-
notify("Launching browser for Twitter login...");
|
|
19
|
-
|
|
20
|
-
const context = (await Camoufox({
|
|
21
|
-
headless: false,
|
|
22
|
-
user_data_dir: profileDir,
|
|
23
|
-
geoip: true,
|
|
24
|
-
humanize: true,
|
|
25
|
-
os: "macos",
|
|
26
|
-
block_webrtc: true,
|
|
27
|
-
window: [1440, 900],
|
|
28
|
-
})) as unknown as BrowserContext;
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const pages = context.pages();
|
|
32
|
-
const page = pages.length > 0 ? pages[0] : await context.newPage();
|
|
33
|
-
|
|
34
|
-
await page.goto("https://x.com/login", {
|
|
35
|
-
waitUntil: "domcontentloaded",
|
|
36
|
-
timeout: 30_000,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
notify("Log in via the browser window. Waiting for auth cookies...");
|
|
40
|
-
|
|
41
|
-
const deadline = Date.now() + 300_000;
|
|
42
|
-
while (Date.now() < deadline) {
|
|
43
|
-
try {
|
|
44
|
-
const cookies = await context.cookies("https://x.com");
|
|
45
|
-
const authNames = ["auth_token", "ct0", "twid"];
|
|
46
|
-
const found = cookies.filter((c) => authNames.includes(c.name));
|
|
47
|
-
if (found.length >= 2) {
|
|
48
|
-
await page.waitForTimeout(3000);
|
|
49
|
-
await context.close();
|
|
50
|
-
return {
|
|
51
|
-
success: true,
|
|
52
|
-
message: `Twitter login successful! (${found.map((c) => c.name).join(", ")})`,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
return { success: false, message: "Twitter login cancelled (browser closed)." };
|
|
57
|
-
}
|
|
58
|
-
await page.waitForTimeout(2000);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
await context.close();
|
|
62
|
-
return { success: false, message: "Twitter login timed out after 5 minutes." };
|
|
63
|
-
} catch (error) {
|
|
64
|
-
try {
|
|
65
|
-
await context.close();
|
|
66
|
-
} catch {
|
|
67
|
-
/* already closed */
|
|
68
|
-
}
|
|
69
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
70
|
-
return { success: false, message: `Twitter login failed: ${msg}` };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function registerTwitterLoginTool(pi: ExtensionAPI): void {
|
|
75
|
-
pi.registerTool({
|
|
76
|
-
name: "trigger_twitter_login",
|
|
77
|
-
label: "Twitter Login",
|
|
78
|
-
description:
|
|
79
|
-
"Open a browser window for the user to log in to Twitter/X. Call this when get_twitter_sentiment reports that login is needed and the user has confirmed they want to proceed. Returns success/failure. After success, retry get_twitter_sentiment.",
|
|
80
|
-
promptSnippet:
|
|
81
|
-
"trigger_twitter_login: Opens a browser for Twitter/X login. Use when Twitter sentiment is unavailable due to missing session.",
|
|
82
|
-
parameters: Type.Object({}),
|
|
83
|
-
|
|
84
|
-
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
85
|
-
if (!ctx?.hasUI) {
|
|
86
|
-
return {
|
|
87
|
-
content: [
|
|
88
|
-
{
|
|
89
|
-
type: "text",
|
|
90
|
-
text: "Cannot open browser in non-interactive mode. Twitter login requires a terminal session with UI.",
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
details: { success: false },
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const result = await runTwitterLogin((msg) => ctx.ui.notify(msg, "info"));
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
content: [{ type: "text", text: result.message }],
|
|
101
|
-
details: { success: result.success },
|
|
102
|
-
};
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
}
|