opencandle 0.1.1 → 0.3.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 +111 -79
- package/dist/analysts/contracts.d.ts +31 -0
- package/dist/analysts/contracts.js +158 -0
- package/dist/analysts/contracts.js.map +1 -0
- package/dist/analysts/orchestrator.d.ts +11 -2
- package/dist/analysts/orchestrator.js +156 -9
- package/dist/analysts/orchestrator.js.map +1 -1
- package/dist/cli.js +26 -10
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +29 -5
- package/dist/config.js +18 -8
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/cache.d.ts +34 -0
- package/dist/infra/cache.js +44 -3
- package/dist/infra/cache.js.map +1 -1
- package/dist/infra/index.d.ts +1 -1
- package/dist/infra/index.js +1 -1
- package/dist/infra/index.js.map +1 -1
- package/dist/infra/opencandle-paths.d.ts +1 -0
- package/dist/infra/opencandle-paths.js +3 -0
- package/dist/infra/opencandle-paths.js.map +1 -1
- package/dist/infra/rate-limiter.js +7 -0
- package/dist/infra/rate-limiter.js.map +1 -1
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manager.d.ts +19 -0
- package/dist/memory/manager.js +132 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/sqlite.js +12 -1
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/types.d.ts +21 -0
- package/dist/memory/types.js +47 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/onboarding/connect.d.ts +23 -0
- package/dist/onboarding/connect.js +107 -0
- package/dist/onboarding/connect.js.map +1 -0
- package/dist/onboarding/credential-interceptor.d.ts +44 -0
- package/dist/onboarding/credential-interceptor.js +72 -0
- package/dist/onboarding/credential-interceptor.js.map +1 -0
- package/dist/onboarding/degradation-accumulator.d.ts +21 -0
- package/dist/onboarding/degradation-accumulator.js +55 -0
- package/dist/onboarding/degradation-accumulator.js.map +1 -0
- package/dist/onboarding/prompt-user.d.ts +23 -0
- package/dist/onboarding/prompt-user.js +61 -0
- package/dist/onboarding/prompt-user.js.map +1 -0
- package/dist/onboarding/providers.d.ts +109 -0
- package/dist/onboarding/providers.js +236 -0
- package/dist/onboarding/providers.js.map +1 -0
- package/dist/onboarding/state.d.ts +31 -2
- package/dist/onboarding/state.js +141 -13
- package/dist/onboarding/state.js.map +1 -1
- package/dist/onboarding/tool-helpers.d.ts +34 -0
- package/dist/onboarding/tool-helpers.js +80 -0
- package/dist/onboarding/tool-helpers.js.map +1 -0
- package/dist/onboarding/tool-tags.d.ts +37 -0
- package/dist/onboarding/tool-tags.js +149 -0
- package/dist/onboarding/tool-tags.js.map +1 -0
- package/dist/onboarding/validation.d.ts +19 -0
- package/dist/onboarding/validation.js +117 -0
- package/dist/onboarding/validation.js.map +1 -0
- package/dist/pi/opencandle-extension.d.ts +5 -1
- package/dist/pi/opencandle-extension.js +345 -143
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/session.d.ts +2 -0
- package/dist/pi/session.js +1 -1
- package/dist/pi/session.js.map +1 -1
- package/dist/pi/setup.d.ts +0 -1
- package/dist/pi/setup.js +66 -119
- package/dist/pi/setup.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +26 -0
- package/dist/prompts/context-builder.js +127 -0
- package/dist/prompts/context-builder.js.map +1 -0
- package/dist/prompts/sections.d.ts +13 -0
- package/dist/prompts/sections.js +35 -0
- package/dist/prompts/sections.js.map +1 -0
- package/dist/providers/alpha-vantage.d.ts +3 -0
- package/dist/providers/alpha-vantage.js +204 -77
- package/dist/providers/alpha-vantage.js.map +1 -1
- package/dist/providers/coingecko.js +53 -37
- package/dist/providers/coingecko.js.map +1 -1
- package/dist/providers/exa-search.d.ts +39 -0
- package/dist/providers/exa-search.js +276 -0
- package/dist/providers/exa-search.js.map +1 -0
- package/dist/providers/fear-greed.js +23 -15
- package/dist/providers/fear-greed.js.map +1 -1
- package/dist/providers/finnhub.d.ts +17 -0
- package/dist/providers/finnhub.js +94 -0
- package/dist/providers/finnhub.js.map +1 -0
- package/dist/providers/fred.js +48 -28
- package/dist/providers/fred.js.map +1 -1
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/provider-credential-error.d.ts +8 -0
- package/dist/providers/provider-credential-error.js +22 -0
- package/dist/providers/provider-credential-error.js.map +1 -0
- package/dist/providers/reddit.d.ts +8 -0
- package/dist/providers/reddit.js +78 -43
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/twitter.d.ts +20 -0
- package/dist/providers/twitter.js +137 -0
- package/dist/providers/twitter.js.map +1 -0
- package/dist/providers/web-search.d.ts +17 -0
- package/dist/providers/web-search.js +224 -0
- package/dist/providers/web-search.js.map +1 -0
- package/dist/providers/with-fallback.d.ts +15 -0
- package/dist/providers/with-fallback.js +32 -0
- package/dist/providers/with-fallback.js.map +1 -0
- package/dist/providers/wrap-provider.d.ts +20 -0
- package/dist/providers/wrap-provider.js +58 -0
- package/dist/providers/wrap-provider.js.map +1 -0
- package/dist/providers/yahoo-finance.js +77 -57
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.js +22 -0
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/runtime/evidence.d.ts +35 -0
- package/dist/runtime/evidence.js +29 -0
- package/dist/runtime/evidence.js.map +1 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.js +10 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/prompt-step.d.ts +41 -0
- package/dist/runtime/prompt-step.js +42 -0
- package/dist/runtime/prompt-step.js.map +1 -0
- package/dist/runtime/provider-ids.d.ts +14 -0
- package/dist/runtime/provider-ids.js +14 -0
- package/dist/runtime/provider-ids.js.map +1 -0
- package/dist/runtime/provider-tracker.d.ts +20 -0
- package/dist/runtime/provider-tracker.js +36 -0
- package/dist/runtime/provider-tracker.js.map +1 -0
- package/dist/runtime/run-context.d.ts +11 -0
- package/dist/runtime/run-context.js +14 -0
- package/dist/runtime/run-context.js.map +1 -0
- package/dist/runtime/session-coordinator.d.ts +47 -0
- package/dist/runtime/session-coordinator.js +171 -0
- package/dist/runtime/session-coordinator.js.map +1 -0
- package/dist/runtime/validation.d.ts +44 -0
- package/dist/runtime/validation.js +157 -0
- package/dist/runtime/validation.js.map +1 -0
- package/dist/runtime/workflow-events.d.ts +21 -0
- package/dist/runtime/workflow-events.js +31 -0
- package/dist/runtime/workflow-events.js.map +1 -0
- package/dist/runtime/workflow-runner.d.ts +36 -0
- package/dist/runtime/workflow-runner.js +129 -0
- package/dist/runtime/workflow-runner.js.map +1 -0
- package/dist/runtime/workflow-types.d.ts +60 -0
- package/dist/runtime/workflow-types.js +32 -0
- package/dist/runtime/workflow-types.js.map +1 -0
- package/dist/sentiment/adapters/finnhub.d.ts +7 -0
- package/dist/sentiment/adapters/finnhub.js +39 -0
- package/dist/sentiment/adapters/finnhub.js.map +1 -0
- package/dist/sentiment/adapters/reddit.d.ts +11 -0
- package/dist/sentiment/adapters/reddit.js +54 -0
- package/dist/sentiment/adapters/reddit.js.map +1 -0
- package/dist/sentiment/adapters/twitter.d.ts +9 -0
- package/dist/sentiment/adapters/twitter.js +32 -0
- package/dist/sentiment/adapters/twitter.js.map +1 -0
- package/dist/sentiment/adapters/web.d.ts +9 -0
- package/dist/sentiment/adapters/web.js +40 -0
- package/dist/sentiment/adapters/web.js.map +1 -0
- package/dist/sentiment/index.d.ts +16 -0
- package/dist/sentiment/index.js +44 -0
- package/dist/sentiment/index.js.map +1 -0
- package/dist/sentiment/keywords.d.ts +2 -0
- package/dist/sentiment/keywords.js +9 -0
- package/dist/sentiment/keywords.js.map +1 -0
- package/dist/sentiment/pipeline.d.ts +9 -0
- package/dist/sentiment/pipeline.js +57 -0
- package/dist/sentiment/pipeline.js.map +1 -0
- package/dist/sentiment/scorer.d.ts +9 -0
- package/dist/sentiment/scorer.js +64 -0
- package/dist/sentiment/scorer.js.map +1 -0
- package/dist/sentiment/store.d.ts +24 -0
- package/dist/sentiment/store.js +177 -0
- package/dist/sentiment/store.js.map +1 -0
- package/dist/sentiment/trends.d.ts +13 -0
- package/dist/sentiment/trends.js +73 -0
- package/dist/sentiment/trends.js.map +1 -0
- package/dist/sentiment/types.d.ts +66 -0
- package/dist/sentiment/types.js +54 -0
- package/dist/sentiment/types.js.map +1 -0
- package/dist/system-prompt.js +60 -2
- package/dist/system-prompt.js.map +1 -1
- package/dist/tool-kit.d.ts +9 -5
- package/dist/tool-kit.js +29 -6
- package/dist/tool-kit.js.map +1 -1
- package/dist/tools/fundamentals/company-overview.d.ts +3 -1
- package/dist/tools/fundamentals/company-overview.js +28 -17
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +47 -39
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +83 -65
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.d.ts +3 -1
- package/dist/tools/fundamentals/earnings.js +26 -18
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.d.ts +3 -1
- package/dist/tools/fundamentals/financials.js +24 -16
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.js +9 -1
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.js +10 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interaction/ask-user.d.ts +3 -0
- package/dist/tools/interaction/ask-user.js +51 -0
- package/dist/tools/interaction/ask-user.js.map +1 -0
- package/dist/tools/interaction/twitter-login.d.ts +8 -0
- package/dist/tools/interaction/twitter-login.js +77 -0
- package/dist/tools/interaction/twitter-login.js.map +1 -0
- package/dist/tools/macro/fear-greed.js +9 -1
- package/dist/tools/macro/fear-greed.js.map +1 -1
- package/dist/tools/macro/fred-data.d.ts +3 -1
- package/dist/tools/macro/fred-data.js +27 -16
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +9 -1
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +9 -1
- package/dist/tools/market/crypto-price.js.map +1 -1
- package/dist/tools/market/stock-history.js +28 -1
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +29 -4
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/option-chain.js +9 -1
- package/dist/tools/options/option-chain.js.map +1 -1
- package/dist/tools/portfolio/correlation.js +15 -3
- package/dist/tools/portfolio/correlation.js.map +1 -1
- package/dist/tools/portfolio/predictions.js +6 -5
- package/dist/tools/portfolio/predictions.js.map +1 -1
- package/dist/tools/portfolio/risk-analysis.js +9 -1
- package/dist/tools/portfolio/risk-analysis.js.map +1 -1
- package/dist/tools/portfolio/tracker.js +6 -3
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.js +6 -1
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/reddit-sentiment.d.ts +3 -1
- package/dist/tools/sentiment/reddit-sentiment.js +112 -19
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.d.ts +7 -0
- package/dist/tools/sentiment/sentiment-summary.js +230 -0
- package/dist/tools/sentiment/sentiment-summary.js.map +1 -0
- package/dist/tools/sentiment/sentiment-trend.d.ts +22 -0
- package/dist/tools/sentiment/sentiment-trend.js +39 -0
- package/dist/tools/sentiment/sentiment-trend.js.map +1 -0
- package/dist/tools/sentiment/twitter-sentiment.d.ts +9 -0
- package/dist/tools/sentiment/twitter-sentiment.js +75 -0
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -0
- package/dist/tools/sentiment/web-search.d.ts +11 -0
- package/dist/tools/sentiment/web-search.js +115 -0
- package/dist/tools/sentiment/web-search.js.map +1 -0
- package/dist/tools/sentiment/web-sentiment.d.ts +8 -0
- package/dist/tools/sentiment/web-sentiment.js +66 -0
- package/dist/tools/sentiment/web-sentiment.js.map +1 -0
- package/dist/tools/technical/backtest.js +9 -1
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +9 -1
- package/dist/tools/technical/indicators.js.map +1 -1
- package/dist/types/index.d.ts +16 -1
- package/dist/types/sentiment.d.ts +41 -0
- package/dist/workflows/compare-assets.d.ts +3 -0
- package/dist/workflows/compare-assets.js +21 -5
- package/dist/workflows/compare-assets.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -3
- package/dist/workflows/index.js +3 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/options-screener.d.ts +3 -0
- package/dist/workflows/options-screener.js +24 -7
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +3 -0
- package/dist/workflows/portfolio-builder.js +30 -9
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/package.json +27 -7
- package/dist/tools/sentiment/news-sentiment.d.ts +0 -7
- package/dist/tools/sentiment/news-sentiment.js +0 -57
- package/dist/tools/sentiment/news-sentiment.js.map +0 -1
package/dist/onboarding/state.js
CHANGED
|
@@ -1,31 +1,159 @@
|
|
|
1
|
+
// Onboarding state schema and pure helpers.
|
|
2
|
+
//
|
|
3
|
+
// Shape: one flag for the welcome message (`welcomeShownAt`) plus a partial
|
|
4
|
+
// per-provider map with a proper discriminated union for status + snooze.
|
|
5
|
+
//
|
|
6
|
+
// All transition helpers are PURE — they return a new state object rather
|
|
7
|
+
// than mutating. The caller is responsible for persisting via saveOnboardingState.
|
|
1
8
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
9
|
import { ensureParentDir, getOnboardingPath } from "../infra/opencandle-paths.js";
|
|
3
|
-
export const ONBOARDING_VERSION =
|
|
10
|
+
export const ONBOARDING_VERSION = 2;
|
|
4
11
|
export function getDefaultOnboardingState() {
|
|
5
|
-
return { version: ONBOARDING_VERSION };
|
|
12
|
+
return { version: ONBOARDING_VERSION, providers: {} };
|
|
13
|
+
}
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
// Load / save
|
|
16
|
+
// -----------------------------------------------------------------------------
|
|
17
|
+
const STATUS_VALUES = new Set(["completed", "snoozed", "never_ask"]);
|
|
18
|
+
function parseEntry(raw) {
|
|
19
|
+
if (!raw || typeof raw !== "object")
|
|
20
|
+
return undefined;
|
|
21
|
+
const obj = raw;
|
|
22
|
+
const status = obj.status;
|
|
23
|
+
if (typeof status !== "string" || !STATUS_VALUES.has(status))
|
|
24
|
+
return undefined;
|
|
25
|
+
const lastPromptAt = obj.lastPromptAt;
|
|
26
|
+
if (typeof lastPromptAt !== "string")
|
|
27
|
+
return undefined;
|
|
28
|
+
if (status === "snoozed") {
|
|
29
|
+
const snoozeUntil = obj.snoozeUntil;
|
|
30
|
+
if (typeof snoozeUntil !== "string")
|
|
31
|
+
return undefined;
|
|
32
|
+
return { status: "snoozed", lastPromptAt, snoozeUntil };
|
|
33
|
+
}
|
|
34
|
+
if (status === "completed") {
|
|
35
|
+
return { status: "completed", lastPromptAt };
|
|
36
|
+
}
|
|
37
|
+
return { status: "never_ask", lastPromptAt };
|
|
38
|
+
}
|
|
39
|
+
function parseProvidersMap(raw) {
|
|
40
|
+
if (!raw || typeof raw !== "object")
|
|
41
|
+
return {};
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
44
|
+
const entry = parseEntry(value);
|
|
45
|
+
if (entry)
|
|
46
|
+
result[key] = entry;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
6
49
|
}
|
|
7
50
|
export function loadOnboardingState(path = getOnboardingPath()) {
|
|
8
51
|
if (!existsSync(path)) {
|
|
9
52
|
return getDefaultOnboardingState();
|
|
10
53
|
}
|
|
54
|
+
let raw;
|
|
11
55
|
try {
|
|
12
|
-
|
|
13
|
-
if (!parsed || typeof parsed !== "object") {
|
|
14
|
-
return getDefaultOnboardingState();
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
version: typeof parsed.version === "number" ? parsed.version : ONBOARDING_VERSION,
|
|
18
|
-
financeSetupStatus: parsed.financeSetupStatus === "completed" || parsed.financeSetupStatus === "dismissed"
|
|
19
|
-
? parsed.financeSetupStatus
|
|
20
|
-
: undefined,
|
|
21
|
-
};
|
|
56
|
+
raw = readFileSync(path, "utf-8");
|
|
22
57
|
}
|
|
23
58
|
catch {
|
|
24
59
|
return getDefaultOnboardingState();
|
|
25
60
|
}
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return getDefaultOnboardingState();
|
|
67
|
+
}
|
|
68
|
+
if (!parsed || typeof parsed !== "object") {
|
|
69
|
+
return getDefaultOnboardingState();
|
|
70
|
+
}
|
|
71
|
+
const obj = parsed;
|
|
72
|
+
const version = typeof obj.version === "number" ? obj.version : ONBOARDING_VERSION;
|
|
73
|
+
const welcomeShownAt = typeof obj.welcomeShownAt === "string" ? obj.welcomeShownAt : undefined;
|
|
74
|
+
const providers = parseProvidersMap(obj.providers);
|
|
75
|
+
return { version, welcomeShownAt, providers };
|
|
26
76
|
}
|
|
27
77
|
export function saveOnboardingState(state, path = getOnboardingPath()) {
|
|
28
78
|
ensureParentDir(path);
|
|
29
|
-
|
|
79
|
+
// Strip undefined fields for cleaner on-disk output.
|
|
80
|
+
const serializable = {
|
|
81
|
+
version: state.version,
|
|
82
|
+
providers: state.providers,
|
|
83
|
+
};
|
|
84
|
+
if (state.welcomeShownAt !== undefined) {
|
|
85
|
+
serializable.welcomeShownAt = state.welcomeShownAt;
|
|
86
|
+
}
|
|
87
|
+
writeFileSync(path, `${JSON.stringify(serializable, null, 2)}\n`, "utf-8");
|
|
88
|
+
}
|
|
89
|
+
// -----------------------------------------------------------------------------
|
|
90
|
+
// Pure transitions
|
|
91
|
+
// -----------------------------------------------------------------------------
|
|
92
|
+
function nowIso() {
|
|
93
|
+
return new Date().toISOString();
|
|
94
|
+
}
|
|
95
|
+
export function markProviderCompleted(state, id) {
|
|
96
|
+
return {
|
|
97
|
+
...state,
|
|
98
|
+
providers: {
|
|
99
|
+
...state.providers,
|
|
100
|
+
[id]: { status: "completed", lastPromptAt: nowIso() },
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function markProviderSnoozed(state, id, days) {
|
|
105
|
+
const now = new Date();
|
|
106
|
+
const snoozeUntil = new Date(now.getTime() + days * 24 * 3600 * 1000);
|
|
107
|
+
return {
|
|
108
|
+
...state,
|
|
109
|
+
providers: {
|
|
110
|
+
...state.providers,
|
|
111
|
+
[id]: {
|
|
112
|
+
status: "snoozed",
|
|
113
|
+
lastPromptAt: now.toISOString(),
|
|
114
|
+
snoozeUntil: snoozeUntil.toISOString(),
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function markProviderNeverAsk(state, id) {
|
|
120
|
+
return {
|
|
121
|
+
...state,
|
|
122
|
+
providers: {
|
|
123
|
+
...state.providers,
|
|
124
|
+
[id]: { status: "never_ask", lastPromptAt: nowIso() },
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export function markWelcomeShown(state) {
|
|
129
|
+
return { ...state, welcomeShownAt: nowIso() };
|
|
130
|
+
}
|
|
131
|
+
// -----------------------------------------------------------------------------
|
|
132
|
+
// Pure queries
|
|
133
|
+
// -----------------------------------------------------------------------------
|
|
134
|
+
export function getProviderEntry(state, id) {
|
|
135
|
+
return state.providers[id];
|
|
136
|
+
}
|
|
137
|
+
export function shouldPrompt(state, id, now, options = {}) {
|
|
138
|
+
const entry = state.providers[id];
|
|
139
|
+
if (!entry)
|
|
140
|
+
return true;
|
|
141
|
+
switch (entry.status) {
|
|
142
|
+
case "never_ask":
|
|
143
|
+
return false;
|
|
144
|
+
case "completed":
|
|
145
|
+
return options.stale === true;
|
|
146
|
+
case "snoozed": {
|
|
147
|
+
const until = Date.parse(entry.snoozeUntil);
|
|
148
|
+
if (Number.isNaN(until))
|
|
149
|
+
return true;
|
|
150
|
+
return now.getTime() >= until;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export function shouldShowWelcome(state, hasUI) {
|
|
155
|
+
if (!hasUI)
|
|
156
|
+
return false;
|
|
157
|
+
return state.welcomeShownAt === undefined;
|
|
30
158
|
}
|
|
31
159
|
//# sourceMappingURL=state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/onboarding/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/onboarding/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { type ProviderId } from "./providers.js";
|
|
3
|
+
import { type CredentialRequiredReason } from "./tool-tags.js";
|
|
4
|
+
export interface CredentialRequiredDetails {
|
|
5
|
+
credentialRequired: {
|
|
6
|
+
provider: ProviderId;
|
|
7
|
+
reason: CredentialRequiredReason;
|
|
8
|
+
httpStatus?: number;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Wrap a tool's provider-calling logic with a credential-check layer.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* ```
|
|
16
|
+
* async execute(_, args) {
|
|
17
|
+
* return withCredentialCheck("alpha_vantage", async () => {
|
|
18
|
+
* const apiKey = getConfig().alphaVantageApiKey!;
|
|
19
|
+
* const result = await wrapProvider("alphavantage", () => getFinancials(args.symbol, apiKey));
|
|
20
|
+
* // ... format success result ...
|
|
21
|
+
* return { content: [...], details: [...] };
|
|
22
|
+
* });
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* On missing credential (upfront): `fn` is NOT called. A tagged tool result
|
|
27
|
+
* is returned directly.
|
|
28
|
+
*
|
|
29
|
+
* On `ProviderCredentialError` thrown during `fn`: the error is caught and a
|
|
30
|
+
* tagged tool result is returned.
|
|
31
|
+
*
|
|
32
|
+
* Any other thrown value (plain Error, string, etc.) is re-thrown unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export declare function withCredentialCheck<T>(providerId: ProviderId, fn: () => Promise<AgentToolResult<T>>): Promise<AgentToolResult<T> | AgentToolResult<CredentialRequiredDetails>>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Tool-boundary helpers for credentialed providers.
|
|
2
|
+
//
|
|
3
|
+
// `withCredentialCheck` is the single-entry helper that OpenCandle tools use
|
|
4
|
+
// to wrap their provider calls. It centralizes:
|
|
5
|
+
// 1. The upfront "credential missing" check via the registry.
|
|
6
|
+
// 2. The catch-and-convert logic for `ProviderCredentialError` thrown from
|
|
7
|
+
// providers on 401/403 (stale credential after HTTP call).
|
|
8
|
+
// 3. The tagged `[OPENCANDLE_CREDENTIAL_REQUIRED ...]` content emission that
|
|
9
|
+
// the Pi `tool_result` extension hook intercepts.
|
|
10
|
+
//
|
|
11
|
+
// Non-credential errors (network timeouts, parse errors, etc.) are re-thrown
|
|
12
|
+
// unchanged so existing error handling in `wrapProvider` continues to work.
|
|
13
|
+
import { ProviderCredentialError } from "../providers/provider-credential-error.js";
|
|
14
|
+
import { getProvider, hasCredential } from "./providers.js";
|
|
15
|
+
import { buildCredentialRequiredTag, } from "./tool-tags.js";
|
|
16
|
+
function buildCredentialRequiredResult(provider, reason, httpStatus) {
|
|
17
|
+
const descriptor = getProvider(provider);
|
|
18
|
+
const tag = buildCredentialRequiredTag({
|
|
19
|
+
provider,
|
|
20
|
+
reason,
|
|
21
|
+
unlocks: descriptor.unlocks,
|
|
22
|
+
fallback: descriptor.fallbackDescription,
|
|
23
|
+
httpStatus,
|
|
24
|
+
});
|
|
25
|
+
// The second line is natural language describing what's missing. Users won't
|
|
26
|
+
// see it directly — the model sees it and uses the information to synthesize
|
|
27
|
+
// a gap note in its final answer. The tagged first line is the machine-
|
|
28
|
+
// readable signal the `tool_result` interception handler keys off of.
|
|
29
|
+
const narrative = reason === "missing"
|
|
30
|
+
? `${descriptor.displayName} was not fetched for this request because no API key is configured. It unlocks ${descriptor.unlocks.join(", ")}.`
|
|
31
|
+
: `${descriptor.displayName} rejected the configured API key (HTTP ${httpStatus ?? "auth error"}). It may be expired or revoked.`;
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text", text: `${tag}\n\n${narrative}` }],
|
|
34
|
+
details: {
|
|
35
|
+
credentialRequired: {
|
|
36
|
+
provider,
|
|
37
|
+
reason,
|
|
38
|
+
...(httpStatus !== undefined ? { httpStatus } : {}),
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Wrap a tool's provider-calling logic with a credential-check layer.
|
|
45
|
+
*
|
|
46
|
+
* Usage:
|
|
47
|
+
* ```
|
|
48
|
+
* async execute(_, args) {
|
|
49
|
+
* return withCredentialCheck("alpha_vantage", async () => {
|
|
50
|
+
* const apiKey = getConfig().alphaVantageApiKey!;
|
|
51
|
+
* const result = await wrapProvider("alphavantage", () => getFinancials(args.symbol, apiKey));
|
|
52
|
+
* // ... format success result ...
|
|
53
|
+
* return { content: [...], details: [...] };
|
|
54
|
+
* });
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* On missing credential (upfront): `fn` is NOT called. A tagged tool result
|
|
59
|
+
* is returned directly.
|
|
60
|
+
*
|
|
61
|
+
* On `ProviderCredentialError` thrown during `fn`: the error is caught and a
|
|
62
|
+
* tagged tool result is returned.
|
|
63
|
+
*
|
|
64
|
+
* Any other thrown value (plain Error, string, etc.) is re-thrown unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export async function withCredentialCheck(providerId, fn) {
|
|
67
|
+
if (!hasCredential(providerId)) {
|
|
68
|
+
return buildCredentialRequiredResult(providerId, "missing");
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return await fn();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof ProviderCredentialError) {
|
|
75
|
+
return buildCredentialRequiredResult(error.provider, error.reason, error.httpStatus);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=tool-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/onboarding/tool-helpers.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,6EAA6E;AAC7E,gDAAgD;AAChD,gEAAgE;AAChE,6EAA6E;AAC7E,gEAAgE;AAChE,+EAA+E;AAC/E,uDAAuD;AACvD,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAG5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAmB,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,0BAA0B,GAE3B,MAAM,gBAAgB,CAAC;AAcxB,SAAS,6BAA6B,CACpC,QAAoB,EACpB,MAAgC,EAChC,UAAmB;IAEnB,MAAM,UAAU,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,0BAA0B,CAAC;QACrC,QAAQ;QACR,MAAM;QACN,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,QAAQ,EAAE,UAAU,CAAC,mBAAmB;QACxC,UAAU;KACX,CAAC,CAAC;IACH,6EAA6E;IAC7E,6EAA6E;IAC7E,wEAAwE;IACxE,sEAAsE;IACtE,MAAM,SAAS,GACb,MAAM,KAAK,SAAS;QAClB,CAAC,CAAC,GAAG,UAAU,CAAC,WAAW,kFAAkF,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QAC7I,CAAC,CAAC,GAAG,UAAU,CAAC,WAAW,0CAA0C,UAAU,IAAI,YAAY,kCAAkC,CAAC;IAEtI,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,SAAS,EAAE,EAAE,CAAC;QAC3D,OAAO,EAAE;YACP,kBAAkB,EAAE;gBAClB,QAAQ;gBACR,MAAM;gBACN,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAsB,EACtB,EAAqC;IAErC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,6BAA6B,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;YAC7C,OAAO,6BAA6B,CAClC,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,UAAU,CACjB,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ProviderId } from "./providers.js";
|
|
2
|
+
export type CredentialRequiredReason = "missing" | "stale";
|
|
3
|
+
export interface CredentialRequiredTagFields {
|
|
4
|
+
provider: ProviderId;
|
|
5
|
+
reason: CredentialRequiredReason;
|
|
6
|
+
unlocks: readonly string[];
|
|
7
|
+
fallback: string | null;
|
|
8
|
+
httpStatus?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface SoftDegradedTagFields {
|
|
11
|
+
provider: ProviderId;
|
|
12
|
+
fallback: string;
|
|
13
|
+
remediation: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SkippedTagFields {
|
|
16
|
+
provider: ProviderId;
|
|
17
|
+
reason: "credential_not_provided";
|
|
18
|
+
remediation: string;
|
|
19
|
+
silenced?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface ConnectedTagFields {
|
|
22
|
+
provider: ProviderId;
|
|
23
|
+
}
|
|
24
|
+
export type ParsedTag = ({
|
|
25
|
+
kind: "credential_required";
|
|
26
|
+
} & CredentialRequiredTagFields) | ({
|
|
27
|
+
kind: "soft_degraded";
|
|
28
|
+
} & SoftDegradedTagFields) | ({
|
|
29
|
+
kind: "skipped";
|
|
30
|
+
} & SkippedTagFields) | ({
|
|
31
|
+
kind: "connected";
|
|
32
|
+
} & ConnectedTagFields);
|
|
33
|
+
export declare function buildCredentialRequiredTag(fields: CredentialRequiredTagFields): string;
|
|
34
|
+
export declare function buildSoftDegradedTag(fields: SoftDegradedTagFields): string;
|
|
35
|
+
export declare function buildSkippedTag(fields: SkippedTagFields): string;
|
|
36
|
+
export declare function buildConnectedTag(fields: ConnectedTagFields): string;
|
|
37
|
+
export declare function parseToolTag(text: string): ParsedTag | undefined;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Tool-result text tags used by OpenCandle's conversational-provider-setup
|
|
2
|
+
// flow. These tagged lines are the LLM-facing contract — Pi provider adapters
|
|
3
|
+
// serialize the `content` field of tool results into the model's conversation,
|
|
4
|
+
// so any signal we want the model to reason over must live in text, not
|
|
5
|
+
// `details`.
|
|
6
|
+
//
|
|
7
|
+
// Tag format (single line, parseable):
|
|
8
|
+
// [OPENCANDLE_<KIND> field=value field2=value ... ]
|
|
9
|
+
//
|
|
10
|
+
// Values may be:
|
|
11
|
+
// - bare: provider=alpha_vantage reason=missing silenced=true httpStatus=401
|
|
12
|
+
// - quoted (for spaces or commas): remediation="run /connect economy"
|
|
13
|
+
// - lists (comma-separated inside quotes): unlocks="fundamentals, DCF"
|
|
14
|
+
// - null/absent: fallback=none
|
|
15
|
+
//
|
|
16
|
+
// Parser is tolerant of unknown fields and extra whitespace.
|
|
17
|
+
// -----------------------------------------------------------------------------
|
|
18
|
+
// Builders
|
|
19
|
+
// -----------------------------------------------------------------------------
|
|
20
|
+
function quote(value) {
|
|
21
|
+
return `"${value.replace(/"/g, '\\"')}"`;
|
|
22
|
+
}
|
|
23
|
+
function formatField(key, value) {
|
|
24
|
+
return `${key}=${value}`;
|
|
25
|
+
}
|
|
26
|
+
export function buildCredentialRequiredTag(fields) {
|
|
27
|
+
const parts = [
|
|
28
|
+
"[OPENCANDLE_CREDENTIAL_REQUIRED",
|
|
29
|
+
formatField("provider", fields.provider),
|
|
30
|
+
formatField("reason", fields.reason),
|
|
31
|
+
`unlocks=${quote(fields.unlocks.join(", "))}`,
|
|
32
|
+
formatField("fallback", fields.fallback ?? "none"),
|
|
33
|
+
];
|
|
34
|
+
if (fields.httpStatus !== undefined) {
|
|
35
|
+
parts.push(formatField("httpStatus", fields.httpStatus));
|
|
36
|
+
}
|
|
37
|
+
return `${parts.join(" ")}]`;
|
|
38
|
+
}
|
|
39
|
+
export function buildSoftDegradedTag(fields) {
|
|
40
|
+
return [
|
|
41
|
+
"[OPENCANDLE_SOFT_DEGRADED",
|
|
42
|
+
formatField("provider", fields.provider),
|
|
43
|
+
formatField("fallback", fields.fallback),
|
|
44
|
+
`remediation=${quote(fields.remediation)}`,
|
|
45
|
+
].join(" ") + "]";
|
|
46
|
+
}
|
|
47
|
+
export function buildSkippedTag(fields) {
|
|
48
|
+
const parts = [
|
|
49
|
+
"[OPENCANDLE_SKIPPED",
|
|
50
|
+
formatField("provider", fields.provider),
|
|
51
|
+
formatField("reason", fields.reason),
|
|
52
|
+
`remediation=${quote(fields.remediation)}`,
|
|
53
|
+
];
|
|
54
|
+
if (fields.silenced) {
|
|
55
|
+
parts.push(formatField("silenced", "true"));
|
|
56
|
+
}
|
|
57
|
+
return `${parts.join(" ")}]`;
|
|
58
|
+
}
|
|
59
|
+
export function buildConnectedTag(fields) {
|
|
60
|
+
return `[OPENCANDLE_CONNECTED provider=${fields.provider}]`;
|
|
61
|
+
}
|
|
62
|
+
// -----------------------------------------------------------------------------
|
|
63
|
+
// Parser
|
|
64
|
+
// -----------------------------------------------------------------------------
|
|
65
|
+
const TAG_LINE_REGEX = /\[OPENCANDLE_(CREDENTIAL_REQUIRED|SOFT_DEGRADED|SKIPPED|CONNECTED)([^\]]*)\]/;
|
|
66
|
+
function parseFields(raw) {
|
|
67
|
+
// Split on `key=value` pairs, respecting quoted values.
|
|
68
|
+
const fields = {};
|
|
69
|
+
const pattern = /(\w+)=("((?:[^"\\]|\\.)*)"|([^\s\]]+))/g;
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = pattern.exec(raw)) !== null) {
|
|
72
|
+
const key = match[1];
|
|
73
|
+
const value = match[3] !== undefined ? match[3].replace(/\\"/g, '"') : match[4];
|
|
74
|
+
fields[key] = value;
|
|
75
|
+
}
|
|
76
|
+
return fields;
|
|
77
|
+
}
|
|
78
|
+
export function parseToolTag(text) {
|
|
79
|
+
const match = TAG_LINE_REGEX.exec(text);
|
|
80
|
+
if (!match)
|
|
81
|
+
return undefined;
|
|
82
|
+
const kind = match[1];
|
|
83
|
+
const rest = match[2];
|
|
84
|
+
const fields = parseFields(rest);
|
|
85
|
+
switch (kind) {
|
|
86
|
+
case "CREDENTIAL_REQUIRED": {
|
|
87
|
+
const provider = fields.provider;
|
|
88
|
+
const reason = fields.reason;
|
|
89
|
+
const unlocksRaw = fields.unlocks ?? "";
|
|
90
|
+
const fallbackRaw = fields.fallback;
|
|
91
|
+
if (!provider || (reason !== "missing" && reason !== "stale")) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const unlocks = unlocksRaw
|
|
95
|
+
.split(",")
|
|
96
|
+
.map((s) => s.trim())
|
|
97
|
+
.filter((s) => s.length > 0);
|
|
98
|
+
const fallback = fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
|
|
99
|
+
const parsed = {
|
|
100
|
+
kind: "credential_required",
|
|
101
|
+
provider: provider,
|
|
102
|
+
reason: reason,
|
|
103
|
+
unlocks,
|
|
104
|
+
fallback,
|
|
105
|
+
};
|
|
106
|
+
if (fields.httpStatus !== undefined) {
|
|
107
|
+
const n = Number(fields.httpStatus);
|
|
108
|
+
if (!Number.isNaN(n))
|
|
109
|
+
parsed.httpStatus = n;
|
|
110
|
+
}
|
|
111
|
+
return parsed;
|
|
112
|
+
}
|
|
113
|
+
case "SOFT_DEGRADED": {
|
|
114
|
+
const { provider, fallback, remediation } = fields;
|
|
115
|
+
if (!provider || !fallback || !remediation)
|
|
116
|
+
return undefined;
|
|
117
|
+
return {
|
|
118
|
+
kind: "soft_degraded",
|
|
119
|
+
provider: provider,
|
|
120
|
+
fallback,
|
|
121
|
+
remediation,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case "SKIPPED": {
|
|
125
|
+
const { provider, reason, remediation } = fields;
|
|
126
|
+
if (!provider || reason !== "credential_not_provided" || !remediation) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const parsed = {
|
|
130
|
+
kind: "skipped",
|
|
131
|
+
provider: provider,
|
|
132
|
+
reason: "credential_not_provided",
|
|
133
|
+
remediation,
|
|
134
|
+
};
|
|
135
|
+
if (fields.silenced === "true")
|
|
136
|
+
parsed.silenced = true;
|
|
137
|
+
return parsed;
|
|
138
|
+
}
|
|
139
|
+
case "CONNECTED": {
|
|
140
|
+
const { provider } = fields;
|
|
141
|
+
if (!provider)
|
|
142
|
+
return undefined;
|
|
143
|
+
return { kind: "connected", provider: provider };
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=tool-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-tags.js","sourceRoot":"","sources":["../../src/onboarding/tool-tags.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProviderId } from "./providers.js";
|
|
2
|
+
export type ValidationResult = {
|
|
3
|
+
status: "valid";
|
|
4
|
+
} | {
|
|
5
|
+
status: "invalid";
|
|
6
|
+
httpStatus?: number;
|
|
7
|
+
message?: string;
|
|
8
|
+
} | {
|
|
9
|
+
status: "transient";
|
|
10
|
+
reason: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Validate a pasted credential against the provider's API. Returns a
|
|
14
|
+
* ValidationResult describing whether the key should be persisted.
|
|
15
|
+
*
|
|
16
|
+
* This function is side-effect-free beyond the network call: it does not
|
|
17
|
+
* touch the config file, onboarding state, or any in-memory cache.
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateCredential(providerId: ProviderId, key: string): Promise<ValidationResult>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Pre-persist credential validation for OpenCandle data providers.
|
|
2
|
+
//
|
|
3
|
+
// Each `validateCredential` call makes a single cheap request to the
|
|
4
|
+
// provider's canonical API with the pasted key and classifies the response
|
|
5
|
+
// into one of three states:
|
|
6
|
+
//
|
|
7
|
+
// - `valid` — provider accepted the credential (persist it)
|
|
8
|
+
// - `invalid` — provider rejected the credential (do NOT persist;
|
|
9
|
+
// re-prompt or return a classified failure)
|
|
10
|
+
// - `transient` — network/timeout/server error (persist anyway; the next
|
|
11
|
+
// tool call will surface any real issue and re-open the
|
|
12
|
+
// prompt via the credential-required tag)
|
|
13
|
+
//
|
|
14
|
+
// The endpoint chosen for each provider is the cheapest one available that
|
|
15
|
+
// also exercises the auth header/query param — we don't pay for meaningful
|
|
16
|
+
// data, just "did the server like the key". Alpha Vantage is a special case:
|
|
17
|
+
// it returns HTTP 200 even for bad keys, with an `Error Message` or
|
|
18
|
+
// `Information` field in the JSON body, so the body must be inspected.
|
|
19
|
+
//
|
|
20
|
+
// All validators share a 5-second timeout via `AbortSignal.timeout` so the
|
|
21
|
+
// connect flow cannot hang if a provider is unreachable.
|
|
22
|
+
const VALIDATION_TIMEOUT_MS = 5_000;
|
|
23
|
+
async function validateWithFetch(url, init, classifyBody) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(url, {
|
|
26
|
+
...init,
|
|
27
|
+
signal: AbortSignal.timeout(VALIDATION_TIMEOUT_MS),
|
|
28
|
+
});
|
|
29
|
+
// Hard auth failures — the key is definitively bad.
|
|
30
|
+
if (response.status === 401 || response.status === 403) {
|
|
31
|
+
return { status: "invalid", httpStatus: response.status };
|
|
32
|
+
}
|
|
33
|
+
// FRED returns 400 with an `error_message` for bad keys.
|
|
34
|
+
if (response.status === 400) {
|
|
35
|
+
return { status: "invalid", httpStatus: 400 };
|
|
36
|
+
}
|
|
37
|
+
// 5xx and other non-2xx — treat as transient. Don't block the user on a
|
|
38
|
+
// provider outage; the next real tool call will surface a fresh error.
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
return { status: "transient", reason: `HTTP ${response.status}` };
|
|
41
|
+
}
|
|
42
|
+
// Body-level classification for providers that return 200 with an error
|
|
43
|
+
// shape (Alpha Vantage).
|
|
44
|
+
if (classifyBody) {
|
|
45
|
+
const body = await response.text();
|
|
46
|
+
const classified = classifyBody(body);
|
|
47
|
+
if (classified)
|
|
48
|
+
return classified;
|
|
49
|
+
}
|
|
50
|
+
return { status: "valid" };
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
54
|
+
return { status: "transient", reason };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Alpha Vantage returns HTTP 200 with an error shape in the JSON body when
|
|
59
|
+
* the key is bad. Two fields to watch for:
|
|
60
|
+
* - `Error Message`: indicates a malformed query OR an invalid key
|
|
61
|
+
* - `Information`: used for rate-limit messages AND for "Invalid API key"
|
|
62
|
+
*
|
|
63
|
+
* The rate-limit form of `Information` is NOT an invalid-key signal, so we
|
|
64
|
+
* only match when the string mentions "invalid api".
|
|
65
|
+
*/
|
|
66
|
+
function classifyAlphaVantageBody(body) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(body);
|
|
69
|
+
const errorMessage = parsed["Error Message"];
|
|
70
|
+
if (typeof errorMessage === "string" && errorMessage.length > 0) {
|
|
71
|
+
return { status: "invalid", message: errorMessage };
|
|
72
|
+
}
|
|
73
|
+
const information = parsed["Information"];
|
|
74
|
+
if (typeof information === "string" && /invalid api/i.test(information)) {
|
|
75
|
+
return { status: "invalid", message: information };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Non-JSON body — fall through and trust the HTTP status.
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate a pasted credential against the provider's API. Returns a
|
|
85
|
+
* ValidationResult describing whether the key should be persisted.
|
|
86
|
+
*
|
|
87
|
+
* This function is side-effect-free beyond the network call: it does not
|
|
88
|
+
* touch the config file, onboarding state, or any in-memory cache.
|
|
89
|
+
*/
|
|
90
|
+
export async function validateCredential(providerId, key) {
|
|
91
|
+
switch (providerId) {
|
|
92
|
+
case "alpha_vantage":
|
|
93
|
+
return validateWithFetch(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=IBM&apikey=${encodeURIComponent(key)}`, { method: "GET" }, classifyAlphaVantageBody);
|
|
94
|
+
case "fred":
|
|
95
|
+
return validateWithFetch(`https://api.stlouisfed.org/fred/series?series_id=GDP&api_key=${encodeURIComponent(key)}&file_type=json`, { method: "GET" });
|
|
96
|
+
case "finnhub":
|
|
97
|
+
return validateWithFetch(`https://finnhub.io/api/v1/quote?symbol=AAPL&token=${encodeURIComponent(key)}`, { method: "GET" });
|
|
98
|
+
case "brave":
|
|
99
|
+
return validateWithFetch("https://api.search.brave.com/res/v1/web/search?q=test&count=1", {
|
|
100
|
+
method: "GET",
|
|
101
|
+
headers: {
|
|
102
|
+
"X-Subscription-Token": key,
|
|
103
|
+
Accept: "application/json",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
case "exa":
|
|
107
|
+
return validateWithFetch("https://api.exa.ai/search", {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"x-api-key": key,
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({ query: "test", numResults: 1 }),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/onboarding/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
|
|
2
|
+
import type { AskUserHandler } from "../types/index.js";
|
|
3
|
+
export interface OpenCandleExtensionOptions {
|
|
4
|
+
askUserHandler?: AskUserHandler;
|
|
5
|
+
}
|
|
6
|
+
export default function openCandleExtension(pi: ExtensionAPI, options?: OpenCandleExtensionOptions): void;
|