aura-code 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/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/agent/context.d.ts +13 -0
- package/dist/agent/context.js +183 -0
- package/dist/agent/context.js.map +1 -0
- package/dist/agent/loop.d.ts +60 -0
- package/dist/agent/loop.js +228 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session-store.d.ts +30 -0
- package/dist/agent/session-store.js +178 -0
- package/dist/agent/session-store.js.map +1 -0
- package/dist/agent/spawner.d.ts +57 -0
- package/dist/agent/spawner.js +112 -0
- package/dist/agent/spawner.js.map +1 -0
- package/dist/agent/system-prompt.d.ts +3 -0
- package/dist/agent/system-prompt.js +100 -0
- package/dist/agent/system-prompt.js.map +1 -0
- package/dist/architect/engine.d.ts +47 -0
- package/dist/architect/engine.js +217 -0
- package/dist/architect/engine.js.map +1 -0
- package/dist/architect/types.d.ts +61 -0
- package/dist/architect/types.js +6 -0
- package/dist/architect/types.js.map +1 -0
- package/dist/cli/diamond.d.ts +1 -0
- package/dist/cli/diamond.js +23 -0
- package/dist/cli/diamond.js.map +1 -0
- package/dist/cli/display.d.ts +44 -0
- package/dist/cli/display.js +234 -0
- package/dist/cli/display.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +1734 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/defaults.d.ts +46 -0
- package/dist/config/defaults.js +106 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/project-config.d.ts +90 -0
- package/dist/config/project-config.js +141 -0
- package/dist/config/project-config.js.map +1 -0
- package/dist/harness/proposer.d.ts +26 -0
- package/dist/harness/proposer.js +204 -0
- package/dist/harness/proposer.js.map +1 -0
- package/dist/harness/weakness-miner.d.ts +28 -0
- package/dist/harness/weakness-miner.js +359 -0
- package/dist/harness/weakness-miner.js.map +1 -0
- package/dist/integrations/harnesses.d.ts +23 -0
- package/dist/integrations/harnesses.js +161 -0
- package/dist/integrations/harnesses.js.map +1 -0
- package/dist/orchestration/competence.d.ts +53 -0
- package/dist/orchestration/competence.js +181 -0
- package/dist/orchestration/competence.js.map +1 -0
- package/dist/orchestration/executor.d.ts +42 -0
- package/dist/orchestration/executor.js +206 -0
- package/dist/orchestration/executor.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.js +39 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
- package/dist/orchestration/orchestrator-prompts.js +153 -0
- package/dist/orchestration/orchestrator-prompts.js.map +1 -0
- package/dist/orchestration/orchestrator.d.ts +27 -0
- package/dist/orchestration/orchestrator.js +169 -0
- package/dist/orchestration/orchestrator.js.map +1 -0
- package/dist/orchestration/plan-store.d.ts +53 -0
- package/dist/orchestration/plan-store.js +176 -0
- package/dist/orchestration/plan-store.js.map +1 -0
- package/dist/orchestration/router-prompts.d.ts +6 -0
- package/dist/orchestration/router-prompts.js +55 -0
- package/dist/orchestration/router-prompts.js.map +1 -0
- package/dist/orchestration/router.d.ts +23 -0
- package/dist/orchestration/router.js +70 -0
- package/dist/orchestration/router.js.map +1 -0
- package/dist/orchestration/ruby-detect.d.ts +6 -0
- package/dist/orchestration/ruby-detect.js +130 -0
- package/dist/orchestration/ruby-detect.js.map +1 -0
- package/dist/orchestration/ruby-types.d.ts +44 -0
- package/dist/orchestration/ruby-types.js +6 -0
- package/dist/orchestration/ruby-types.js.map +1 -0
- package/dist/orchestration/specialist-prompts.d.ts +19 -0
- package/dist/orchestration/specialist-prompts.js +143 -0
- package/dist/orchestration/specialist-prompts.js.map +1 -0
- package/dist/orchestration/specialists.d.ts +66 -0
- package/dist/orchestration/specialists.js +175 -0
- package/dist/orchestration/specialists.js.map +1 -0
- package/dist/orchestration/types.d.ts +82 -0
- package/dist/orchestration/types.js +6 -0
- package/dist/orchestration/types.js.map +1 -0
- package/dist/perception/extractor.d.ts +7 -0
- package/dist/perception/extractor.js +521 -0
- package/dist/perception/extractor.js.map +1 -0
- package/dist/perception/graph-store.d.ts +25 -0
- package/dist/perception/graph-store.js +117 -0
- package/dist/perception/graph-store.js.map +1 -0
- package/dist/perception/index.d.ts +4 -0
- package/dist/perception/index.js +18 -0
- package/dist/perception/index.js.map +1 -0
- package/dist/perception/queries.d.ts +29 -0
- package/dist/perception/queries.js +354 -0
- package/dist/perception/queries.js.map +1 -0
- package/dist/perception/types.d.ts +103 -0
- package/dist/perception/types.js +6 -0
- package/dist/perception/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +11 -0
- package/dist/providers/anthropic.js +172 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +58 -0
- package/dist/providers/factory.js +354 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/fallback.d.ts +24 -0
- package/dist/providers/fallback.js +72 -0
- package/dist/providers/fallback.js.map +1 -0
- package/dist/providers/google.d.ts +11 -0
- package/dist/providers/google.js +129 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +24 -0
- package/dist/providers/openai-compatible.js +216 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/providers/resilient-factory.d.ts +24 -0
- package/dist/providers/resilient-factory.js +57 -0
- package/dist/providers/resilient-factory.js.map +1 -0
- package/dist/providers/resilient.d.ts +63 -0
- package/dist/providers/resilient.js +145 -0
- package/dist/providers/resilient.js.map +1 -0
- package/dist/providers/types.d.ts +84 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/ruby/alternator.d.ts +40 -0
- package/dist/ruby/alternator.js +233 -0
- package/dist/ruby/alternator.js.map +1 -0
- package/dist/ruby/competence.d.ts +34 -0
- package/dist/ruby/competence.js +264 -0
- package/dist/ruby/competence.js.map +1 -0
- package/dist/ruby/episode-capture.d.ts +56 -0
- package/dist/ruby/episode-capture.js +193 -0
- package/dist/ruby/episode-capture.js.map +1 -0
- package/dist/ruby/fine-tune.d.ts +16 -0
- package/dist/ruby/fine-tune.js +253 -0
- package/dist/ruby/fine-tune.js.map +1 -0
- package/dist/ruby/index.d.ts +10 -0
- package/dist/ruby/index.js +26 -0
- package/dist/ruby/index.js.map +1 -0
- package/dist/ruby/ruby-model.d.ts +36 -0
- package/dist/ruby/ruby-model.js +81 -0
- package/dist/ruby/ruby-model.js.map +1 -0
- package/dist/ruby/training-data.d.ts +11 -0
- package/dist/ruby/training-data.js +107 -0
- package/dist/ruby/training-data.js.map +1 -0
- package/dist/ruby/types.d.ts +136 -0
- package/dist/ruby/types.js +20 -0
- package/dist/ruby/types.js.map +1 -0
- package/dist/safety/permissions.d.ts +32 -0
- package/dist/safety/permissions.js +304 -0
- package/dist/safety/permissions.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +342 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/session.d.ts +22 -0
- package/dist/server/session.js +16 -0
- package/dist/server/session.js.map +1 -0
- package/dist/setup/first-run.d.ts +41 -0
- package/dist/setup/first-run.js +386 -0
- package/dist/setup/first-run.js.map +1 -0
- package/dist/setup/global-config.d.ts +19 -0
- package/dist/setup/global-config.js +97 -0
- package/dist/setup/global-config.js.map +1 -0
- package/dist/tools/audio-transcribe.d.ts +8 -0
- package/dist/tools/audio-transcribe.js +156 -0
- package/dist/tools/audio-transcribe.js.map +1 -0
- package/dist/tools/browser.d.ts +15 -0
- package/dist/tools/browser.js +241 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/calendar.d.ts +12 -0
- package/dist/tools/calendar.js +196 -0
- package/dist/tools/calendar.js.map +1 -0
- package/dist/tools/clipboard.d.ts +7 -0
- package/dist/tools/clipboard.js +90 -0
- package/dist/tools/clipboard.js.map +1 -0
- package/dist/tools/cron.d.ts +10 -0
- package/dist/tools/cron.js +167 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/edit-file.d.ts +6 -0
- package/dist/tools/edit-file.js +98 -0
- package/dist/tools/edit-file.js.map +1 -0
- package/dist/tools/email.d.ts +11 -0
- package/dist/tools/email.js +153 -0
- package/dist/tools/email.js.map +1 -0
- package/dist/tools/git.d.ts +1 -0
- package/dist/tools/git.js +7 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/http-request.d.ts +12 -0
- package/dist/tools/http-request.js +91 -0
- package/dist/tools/http-request.js.map +1 -0
- package/dist/tools/image-read.d.ts +7 -0
- package/dist/tools/image-read.js +125 -0
- package/dist/tools/image-read.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +201 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir.d.ts +1 -0
- package/dist/tools/list-dir.js +6 -0
- package/dist/tools/list-dir.js.map +1 -0
- package/dist/tools/mcp.d.ts +11 -0
- package/dist/tools/mcp.js +356 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.js +124 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/notify.d.ts +9 -0
- package/dist/tools/notify.js +40 -0
- package/dist/tools/notify.js.map +1 -0
- package/dist/tools/read-file.d.ts +6 -0
- package/dist/tools/read-file.js +76 -0
- package/dist/tools/read-file.js.map +1 -0
- package/dist/tools/run-shell.d.ts +1 -0
- package/dist/tools/run-shell.js +6 -0
- package/dist/tools/run-shell.js.map +1 -0
- package/dist/tools/run-tests.d.ts +1 -0
- package/dist/tools/run-tests.js +6 -0
- package/dist/tools/run-tests.js.map +1 -0
- package/dist/tools/search-code.d.ts +1 -0
- package/dist/tools/search-code.js +6 -0
- package/dist/tools/search-code.js.map +1 -0
- package/dist/tools/telegram-bot.d.ts +2 -0
- package/dist/tools/telegram-bot.js +693 -0
- package/dist/tools/telegram-bot.js.map +1 -0
- package/dist/tools/telegram-safety.d.ts +52 -0
- package/dist/tools/telegram-safety.js +189 -0
- package/dist/tools/telegram-safety.js.map +1 -0
- package/dist/tools/telegram.d.ts +16 -0
- package/dist/tools/telegram.js +227 -0
- package/dist/tools/telegram.js.map +1 -0
- package/dist/tools/tools.d.ts +48 -0
- package/dist/tools/tools.js +328 -0
- package/dist/tools/tools.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +11 -0
- package/dist/tools/web-fetch.js +119 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +8 -0
- package/dist/tools/web-search.js +81 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/whatsapp.d.ts +10 -0
- package/dist/tools/whatsapp.js +222 -0
- package/dist/tools/whatsapp.js.map +1 -0
- package/dist/tools/write-file.d.ts +1 -0
- package/dist/tools/write-file.js +6 -0
- package/dist/tools/write-file.js.map +1 -0
- package/dist/tools/youtube-transcript.d.ts +9 -0
- package/dist/tools/youtube-transcript.js +200 -0
- package/dist/tools/youtube-transcript.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +44 -0
- package/dist/util/circuit-breaker.js +78 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/env.d.ts +18 -0
- package/dist/util/env.js +39 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/errors.d.ts +42 -0
- package/dist/util/errors.js +121 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/rate-limiter.d.ts +71 -0
- package/dist/util/rate-limiter.js +141 -0
- package/dist/util/rate-limiter.js.map +1 -0
- package/dist/util/retry.d.ts +30 -0
- package/dist/util/retry.js +57 -0
- package/dist/util/retry.js.map +1 -0
- package/dist/util/sanitize.d.ts +16 -0
- package/dist/util/sanitize.js +255 -0
- package/dist/util/sanitize.js.map +1 -0
- package/dist/verify/checks.d.ts +21 -0
- package/dist/verify/checks.js +311 -0
- package/dist/verify/checks.js.map +1 -0
- package/dist/verify/index.d.ts +33 -0
- package/dist/verify/index.js +127 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/types.d.ts +20 -0
- package/dist/verify/types.js +3 -0
- package/dist/verify/types.js.map +1 -0
- package/dist/versioncheck.d.ts +22 -0
- package/dist/versioncheck.js +203 -0
- package/dist/versioncheck.js.map +1 -0
- package/dist/viz/index.d.ts +2 -0
- package/dist/viz/index.js +733 -0
- package/dist/viz/index.js.map +1 -0
- package/dist/workflows/engine.d.ts +46 -0
- package/dist/workflows/engine.js +320 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/types.d.ts +92 -0
- package/dist/workflows/types.js +6 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WHATSAPP_DEFINITION = void 0;
|
|
37
|
+
exports.whatsAppTool = whatsAppTool;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
exports.WHATSAPP_DEFINITION = {
|
|
42
|
+
name: 'whatsapp',
|
|
43
|
+
description: 'Send WhatsApp messages via Twilio API or configured HTTP gateway. ' +
|
|
44
|
+
'Configure ~/.aura/whatsapp.json with { "provider": "twilio", "account_sid": "...", "auth_token": "...", "from": "whatsapp:+14155238886" }. ' +
|
|
45
|
+
'Or { "provider": "gateway", "url": "http://localhost:3000/send" }.',
|
|
46
|
+
parameters: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
action: { type: 'string', description: 'Action: send, send_media, status' },
|
|
50
|
+
to: { type: 'string', description: 'Recipient phone number with country code (e.g., +381641234567)' },
|
|
51
|
+
message: { type: 'string', description: 'Message text' },
|
|
52
|
+
media_url: { type: 'string', description: 'URL of media to send (for send_media)' },
|
|
53
|
+
from: { type: 'string', description: 'Sender WhatsApp number (overrides config)' },
|
|
54
|
+
},
|
|
55
|
+
required: ['action'],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
function loadConfig() {
|
|
59
|
+
const p = path.join(os.homedir(), '.aura', 'whatsapp.json');
|
|
60
|
+
if (!fs.existsSync(p))
|
|
61
|
+
return null;
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
70
|
+
// Providers
|
|
71
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
72
|
+
function normalizePhone(phone) {
|
|
73
|
+
// Ensure it starts with + and has whatsapp: prefix for Twilio
|
|
74
|
+
const cleaned = phone.replace(/\s/g, '');
|
|
75
|
+
if (cleaned.startsWith('whatsapp:'))
|
|
76
|
+
return cleaned;
|
|
77
|
+
return `whatsapp:${cleaned.startsWith('+') ? cleaned : '+' + cleaned}`;
|
|
78
|
+
}
|
|
79
|
+
async function sendViaTwilio(config, to, message, from) {
|
|
80
|
+
if (!config.account_sid || !config.auth_token) {
|
|
81
|
+
return 'Error: Twilio account_sid and auth_token required in config';
|
|
82
|
+
}
|
|
83
|
+
const fromNumber = normalizePhone(from ?? config.from ?? '');
|
|
84
|
+
const toNumber = normalizePhone(to);
|
|
85
|
+
const url = `https://api.twilio.com/2010-04-01/Accounts/${config.account_sid}/Messages.json`;
|
|
86
|
+
const params = new URLSearchParams({
|
|
87
|
+
From: fromNumber,
|
|
88
|
+
To: toNumber,
|
|
89
|
+
Body: message,
|
|
90
|
+
});
|
|
91
|
+
const response = await fetch(url, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
95
|
+
'Authorization': 'Basic ' + Buffer.from(`${config.account_sid}:${config.auth_token}`).toString('base64'),
|
|
96
|
+
},
|
|
97
|
+
body: params.toString(),
|
|
98
|
+
signal: AbortSignal.timeout(15_000),
|
|
99
|
+
});
|
|
100
|
+
const data = await response.json();
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
return `Error: Twilio API error: ${data.message ?? 'Unknown error'} (code: ${data.code})`;
|
|
103
|
+
}
|
|
104
|
+
return `WhatsApp message sent to ${to} (SID: ${data.sid}, status: ${data.status})`;
|
|
105
|
+
}
|
|
106
|
+
async function sendViaTwilioMedia(config, to, message, mediaUrl, from) {
|
|
107
|
+
if (!config.account_sid || !config.auth_token) {
|
|
108
|
+
return 'Error: Twilio account_sid and auth_token required in config';
|
|
109
|
+
}
|
|
110
|
+
const fromNumber = normalizePhone(from ?? config.from ?? '');
|
|
111
|
+
const toNumber = normalizePhone(to);
|
|
112
|
+
const url = `https://api.twilio.com/2010-04-01/Accounts/${config.account_sid}/Messages.json`;
|
|
113
|
+
const params = new URLSearchParams({
|
|
114
|
+
From: fromNumber,
|
|
115
|
+
To: toNumber,
|
|
116
|
+
Body: message,
|
|
117
|
+
MediaUrl: mediaUrl,
|
|
118
|
+
});
|
|
119
|
+
const response = await fetch(url, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
123
|
+
'Authorization': 'Basic ' + Buffer.from(`${config.account_sid}:${config.auth_token}`).toString('base64'),
|
|
124
|
+
},
|
|
125
|
+
body: params.toString(),
|
|
126
|
+
signal: AbortSignal.timeout(15_000),
|
|
127
|
+
});
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
return `Error: Twilio API error: ${data.message ?? 'Unknown error'}`;
|
|
131
|
+
}
|
|
132
|
+
return `WhatsApp media sent to ${to} (SID: ${data.sid})`;
|
|
133
|
+
}
|
|
134
|
+
async function sendViaGateway(config, to, message) {
|
|
135
|
+
if (!config.url)
|
|
136
|
+
return 'Error: Gateway URL required in config';
|
|
137
|
+
const response = await fetch(config.url, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers: {
|
|
140
|
+
'Content-Type': 'application/json',
|
|
141
|
+
...(config.api_key ? { 'Authorization': `Bearer ${config.api_key}` } : {}),
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({ to, message }),
|
|
144
|
+
signal: AbortSignal.timeout(15_000),
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
const text = await response.text();
|
|
148
|
+
return `Error: Gateway returned HTTP ${response.status}: ${text.slice(0, 200)}`;
|
|
149
|
+
}
|
|
150
|
+
return `WhatsApp message sent to ${to} via gateway`;
|
|
151
|
+
}
|
|
152
|
+
async function checkStatus(config) {
|
|
153
|
+
if (config.provider === 'twilio') {
|
|
154
|
+
if (!config.account_sid || !config.auth_token)
|
|
155
|
+
return 'Error: Twilio credentials not configured';
|
|
156
|
+
const url = `https://api.twilio.com/2010-04-01/Accounts/${config.account_sid}.json`;
|
|
157
|
+
const response = await fetch(url, {
|
|
158
|
+
headers: {
|
|
159
|
+
'Authorization': 'Basic ' + Buffer.from(`${config.account_sid}:${config.auth_token}`).toString('base64'),
|
|
160
|
+
},
|
|
161
|
+
signal: AbortSignal.timeout(10_000),
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok)
|
|
164
|
+
return 'Error: Cannot connect to Twilio';
|
|
165
|
+
const data = await response.json();
|
|
166
|
+
return `Twilio connected. Account: ${data.friendly_name} (${data.status})`;
|
|
167
|
+
}
|
|
168
|
+
if (config.provider === 'gateway') {
|
|
169
|
+
try {
|
|
170
|
+
const response = await fetch(config.url.replace(/\/send$/, '/health'), { signal: AbortSignal.timeout(5000) });
|
|
171
|
+
return response.ok ? 'Gateway is reachable' : `Gateway returned HTTP ${response.status}`;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return 'Gateway is not reachable';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return 'Error: Unknown provider';
|
|
178
|
+
}
|
|
179
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
180
|
+
// Main executor
|
|
181
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
182
|
+
async function whatsAppTool(input) {
|
|
183
|
+
const config = loadConfig();
|
|
184
|
+
if (!config) {
|
|
185
|
+
return 'Error: WhatsApp not configured. Create ~/.aura/whatsapp.json with { "provider": "twilio", "account_sid": "...", "auth_token": "...", "from": "whatsapp:+14155238886" } or { "provider": "gateway", "url": "http://localhost:3000/send" }.';
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
switch (input.action) {
|
|
189
|
+
case 'send': {
|
|
190
|
+
if (!input.to)
|
|
191
|
+
return 'Error: to (phone number) required';
|
|
192
|
+
if (!input.message)
|
|
193
|
+
return 'Error: message required';
|
|
194
|
+
if (config.provider === 'twilio')
|
|
195
|
+
return await sendViaTwilio(config, input.to, input.message, input.from);
|
|
196
|
+
if (config.provider === 'gateway')
|
|
197
|
+
return await sendViaGateway(config, input.to, input.message);
|
|
198
|
+
return 'Error: Unknown provider in config';
|
|
199
|
+
}
|
|
200
|
+
case 'send_media': {
|
|
201
|
+
if (!input.to)
|
|
202
|
+
return 'Error: to required';
|
|
203
|
+
if (!input.message)
|
|
204
|
+
return 'Error: message required';
|
|
205
|
+
if (!input.media_url)
|
|
206
|
+
return 'Error: media_url required';
|
|
207
|
+
if (config.provider === 'twilio')
|
|
208
|
+
return await sendViaTwilioMedia(config, input.to, input.message, input.media_url, input.from);
|
|
209
|
+
return 'Error: send_media only supported for Twilio provider';
|
|
210
|
+
}
|
|
211
|
+
case 'status': {
|
|
212
|
+
return await checkStatus(config);
|
|
213
|
+
}
|
|
214
|
+
default:
|
|
215
|
+
return `Error: Unknown whatsapp action: ${input.action}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
return `WhatsApp error (${input.action}): ${e?.message ?? String(e)}`;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=whatsapp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../src/tools/whatsapp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { writeFile } from './tools.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeFile = void 0;
|
|
4
|
+
var tools_js_1 = require("./tools.js");
|
|
5
|
+
Object.defineProperty(exports, "writeFile", { enumerable: true, get: function () { return tools_js_1.writeFile; } });
|
|
6
|
+
//# sourceMappingURL=write-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-file.js","sourceRoot":"","sources":["../../src/tools/write-file.ts"],"names":[],"mappings":";;;AAAA,uCAAuC;AAA9B,qGAAA,SAAS,OAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../providers/types.js';
|
|
2
|
+
export interface YouTubeTranscriptInput {
|
|
3
|
+
url: string;
|
|
4
|
+
lang?: string;
|
|
5
|
+
format?: 'text' | 'timestamps' | 'srt';
|
|
6
|
+
max_chars?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const YOUTUBE_TRANSCRIPT_DEFINITION: ToolDefinition;
|
|
9
|
+
export declare function youtubeTranscript(input: YouTubeTranscriptInput): Promise<string>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.YOUTUBE_TRANSCRIPT_DEFINITION = void 0;
|
|
4
|
+
exports.youtubeTranscript = youtubeTranscript;
|
|
5
|
+
const sanitize_js_1 = require("../util/sanitize.js");
|
|
6
|
+
exports.YOUTUBE_TRANSCRIPT_DEFINITION = {
|
|
7
|
+
name: 'youtube_transcript',
|
|
8
|
+
description: 'Extract transcript/captions from a YouTube video. Works with regular videos and Shorts. ' +
|
|
9
|
+
'No API key required. Supports multiple languages and output formats.',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
url: { type: 'string', description: 'YouTube video URL or video ID' },
|
|
14
|
+
lang: { type: 'string', description: 'Caption language code (default: en). Use "list" to see available languages.' },
|
|
15
|
+
format: { type: 'string', description: 'Output format: "text" (plain text), "timestamps" (with timestamps), "srt" (SRT subtitle format). Default: timestamps' },
|
|
16
|
+
max_chars: { type: 'number', description: 'Max characters to return (default: 50000)' },
|
|
17
|
+
},
|
|
18
|
+
required: ['url'],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Helpers
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
function extractVideoId(input) {
|
|
25
|
+
// Handle direct video IDs
|
|
26
|
+
if (/^[a-zA-Z0-9_-]{11}$/.test(input))
|
|
27
|
+
return input;
|
|
28
|
+
const patterns = [
|
|
29
|
+
/(?:youtube\.com\/watch\?.*v=)([a-zA-Z0-9_-]{11})/,
|
|
30
|
+
/(?:youtube\.com\/shorts\/)([a-zA-Z0-9_-]{11})/,
|
|
31
|
+
/(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/,
|
|
32
|
+
/(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
|
|
33
|
+
/(?:youtube\.com\/v\/)([a-zA-Z0-9_-]{11})/,
|
|
34
|
+
];
|
|
35
|
+
for (const p of patterns) {
|
|
36
|
+
const m = input.match(p);
|
|
37
|
+
if (m)
|
|
38
|
+
return m[1];
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function parseTimedText(xml) {
|
|
43
|
+
const segments = [];
|
|
44
|
+
// YouTube returns XML like: <p t="0" d="5000">Hello world</p>
|
|
45
|
+
// or newer format: <text start="0" dur="5">Hello world</text>
|
|
46
|
+
const pRegex = /<p\s[^>]*t="(\d+)"[^>]*d="(\d+)"[^>]*>([\s\S]*?)<\/p>/g;
|
|
47
|
+
let m;
|
|
48
|
+
while ((m = pRegex.exec(xml)) !== null) {
|
|
49
|
+
const start = parseInt(m[1], 10) / 1000;
|
|
50
|
+
const duration = parseInt(m[2], 10) / 1000;
|
|
51
|
+
const text = (0, sanitize_js_1.decodeEntities)((0, sanitize_js_1.stripHtml)(m[3]))
|
|
52
|
+
.replace(/\n/g, ' ')
|
|
53
|
+
.trim();
|
|
54
|
+
if (text)
|
|
55
|
+
segments.push({ start, duration, text });
|
|
56
|
+
}
|
|
57
|
+
// Fallback: try <text start="..." dur="..."> format
|
|
58
|
+
if (segments.length === 0) {
|
|
59
|
+
const textRegex = /<text\s[^>]*start="([\d.]+)"[^>]*dur="([\d.]+)"[^>]*>([\s\S]*?)<\/text>/g;
|
|
60
|
+
while ((m = textRegex.exec(xml)) !== null) {
|
|
61
|
+
const start = parseFloat(m[1]);
|
|
62
|
+
const duration = parseFloat(m[2]);
|
|
63
|
+
const text = (0, sanitize_js_1.decodeEntities)((0, sanitize_js_1.stripHtml)(m[3]))
|
|
64
|
+
.replace(/\n/g, ' ')
|
|
65
|
+
.trim();
|
|
66
|
+
if (text)
|
|
67
|
+
segments.push({ start, duration, text });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return segments;
|
|
71
|
+
}
|
|
72
|
+
function formatTimestamp(seconds) {
|
|
73
|
+
const h = Math.floor(seconds / 3600);
|
|
74
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
75
|
+
const s = Math.floor(seconds % 60);
|
|
76
|
+
if (h > 0)
|
|
77
|
+
return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
|
78
|
+
return `${m}:${String(s).padStart(2, '0')}`;
|
|
79
|
+
}
|
|
80
|
+
function formatSrtTimestamp(seconds) {
|
|
81
|
+
const h = Math.floor(seconds / 3600);
|
|
82
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
83
|
+
const s = Math.floor(seconds % 60);
|
|
84
|
+
const ms = Math.floor((seconds % 1) * 1000);
|
|
85
|
+
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')},${String(ms).padStart(3, '0')}`;
|
|
86
|
+
}
|
|
87
|
+
function formatTranscript(segments, format) {
|
|
88
|
+
switch (format) {
|
|
89
|
+
case 'text':
|
|
90
|
+
return segments.map(s => s.text).join(' ');
|
|
91
|
+
case 'srt':
|
|
92
|
+
return segments.map((s, i) => {
|
|
93
|
+
const end = s.start + s.duration;
|
|
94
|
+
return `${i + 1}\n${formatSrtTimestamp(s.start)} --> ${formatSrtTimestamp(end)}\n${s.text}\n`;
|
|
95
|
+
}).join('\n');
|
|
96
|
+
case 'timestamps':
|
|
97
|
+
default:
|
|
98
|
+
return segments.map(s => `[${formatTimestamp(s.start)}] ${s.text}`).join('\n');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
+
// Main executor
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
104
|
+
async function youtubeTranscript(input) {
|
|
105
|
+
const maxChars = input.max_chars ?? 50_000;
|
|
106
|
+
const format = input.format ?? 'timestamps';
|
|
107
|
+
const lang = input.lang ?? 'en';
|
|
108
|
+
// 1. Extract video ID
|
|
109
|
+
const videoId = extractVideoId(input.url);
|
|
110
|
+
if (!videoId) {
|
|
111
|
+
return `Error: Could not extract video ID from: "${input.url}"\nSupported formats: https://youtube.com/watch?v=ID, https://youtu.be/ID, https://youtube.com/shorts/ID`;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
// 2. Fetch the YouTube page to find caption tracks
|
|
115
|
+
const pageUrl = `https://www.youtube.com/watch?v=${videoId}`;
|
|
116
|
+
const response = await fetch(pageUrl, {
|
|
117
|
+
headers: {
|
|
118
|
+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
119
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
120
|
+
},
|
|
121
|
+
signal: AbortSignal.timeout(15_000),
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
return `Error: YouTube returned HTTP ${response.status}`;
|
|
125
|
+
}
|
|
126
|
+
const html = await response.text();
|
|
127
|
+
// 3. Extract ytInitialPlayerResponse from the page
|
|
128
|
+
// Use [\s\S] instead of /s flag for compatibility with older TS targets
|
|
129
|
+
const playerMatch = html.match(/ytInitialPlayerResponse\s*=\s*(\{[\s\S]+?\});\s*(?:var\s|<\/script)/);
|
|
130
|
+
if (!playerMatch) {
|
|
131
|
+
return 'Error: Could not extract player data from YouTube page. The video may be private, age-restricted, or YouTube changed their page structure.';
|
|
132
|
+
}
|
|
133
|
+
const playerJson = playerMatch[1];
|
|
134
|
+
let playerData;
|
|
135
|
+
try {
|
|
136
|
+
playerData = JSON.parse(playerJson);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return 'Error: Failed to parse YouTube player data JSON.';
|
|
140
|
+
}
|
|
141
|
+
// 4. Navigate to caption tracks
|
|
142
|
+
const captions = playerData?.captions?.playerCaptionsTracklistRenderer?.captionTracks;
|
|
143
|
+
if (!captions || captions.length === 0) {
|
|
144
|
+
const videoDetails = playerData?.videoDetails;
|
|
145
|
+
const title = videoDetails?.title ?? 'Unknown';
|
|
146
|
+
return `No captions available for: "${title}" (${videoId})\nThe video may not have subtitles or auto-generated captions.`;
|
|
147
|
+
}
|
|
148
|
+
// 5. If lang=list, show available languages
|
|
149
|
+
if (lang === 'list') {
|
|
150
|
+
const tracks = captions.map((t) => ` - ${t.languageCode}: ${t.name}${t.isTranslatable ? ' (translatable)' : ''}`);
|
|
151
|
+
return `Available caption tracks for ${videoId}:\n${tracks.join('\n')}`;
|
|
152
|
+
}
|
|
153
|
+
// 6. Find the requested language track
|
|
154
|
+
let track = captions.find((t) => t.languageCode === lang);
|
|
155
|
+
if (!track) {
|
|
156
|
+
// Try partial match (e.g., "en" matches "en-US")
|
|
157
|
+
track = captions.find((t) => t.languageCode.startsWith(lang));
|
|
158
|
+
}
|
|
159
|
+
if (!track) {
|
|
160
|
+
// Fall back to first available
|
|
161
|
+
track = captions[0];
|
|
162
|
+
if (!track) {
|
|
163
|
+
return `Error: No caption track found for language "${lang}".`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// 7. Fetch the caption XML
|
|
167
|
+
// Remove fmt=srv3 to get simpler XML
|
|
168
|
+
const captionUrl = track.baseUrl.replace(/&fmt=\w+/, '');
|
|
169
|
+
const captionResponse = await fetch(captionUrl, {
|
|
170
|
+
headers: {
|
|
171
|
+
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
|
|
172
|
+
},
|
|
173
|
+
signal: AbortSignal.timeout(10_000),
|
|
174
|
+
});
|
|
175
|
+
if (!captionResponse.ok) {
|
|
176
|
+
return `Error: Failed to fetch captions (HTTP ${captionResponse.status})`;
|
|
177
|
+
}
|
|
178
|
+
const captionXml = await captionResponse.text();
|
|
179
|
+
// 8. Parse the timed text
|
|
180
|
+
const segments = parseTimedText(captionXml);
|
|
181
|
+
if (segments.length === 0) {
|
|
182
|
+
return `Error: No caption segments found in the response for language "${track.languageCode}".`;
|
|
183
|
+
}
|
|
184
|
+
// 9. Format and return
|
|
185
|
+
const videoDetails = playerData?.videoDetails;
|
|
186
|
+
const title = videoDetails?.title ?? 'Unknown';
|
|
187
|
+
const duration = videoDetails?.lengthSeconds ? `${Math.floor(parseInt(videoDetails.lengthSeconds) / 60)}m${parseInt(videoDetails.lengthSeconds) % 60}s` : '?';
|
|
188
|
+
const header = `YouTube Transcript: "${title}" (${duration})\nLanguage: ${track.languageCode} | Segments: ${segments.length}\nURL: ${pageUrl}\n\n`;
|
|
189
|
+
const body = formatTranscript(segments, format);
|
|
190
|
+
const result = header + body;
|
|
191
|
+
if (result.length > maxChars) {
|
|
192
|
+
return result.slice(0, maxChars) + `\n\n... [truncated at ${maxChars} chars]`;
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
return `Error fetching YouTube transcript: ${e?.message ?? String(e)}`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=youtube-transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtube-transcript.js","sourceRoot":"","sources":["../../src/tools/youtube-transcript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit breaker. Trips after N consecutive failures within a time window,
|
|
3
|
+
* then refuses new calls for a cooldown period. Half-open after cooldown:
|
|
4
|
+
* a single trial call decides whether to close again or stay open.
|
|
5
|
+
*/
|
|
6
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
7
|
+
export interface CircuitBreakerOptions {
|
|
8
|
+
/** Trip after this many consecutive failures. Default 5. */
|
|
9
|
+
failureThreshold?: number;
|
|
10
|
+
/** Time in ms before trying again after the breaker opens. Default 30_000. */
|
|
11
|
+
cooldownMs?: number;
|
|
12
|
+
/** Called on every state transition — for observability. */
|
|
13
|
+
onStateChange?: (state: CircuitState, info: {
|
|
14
|
+
failures: number;
|
|
15
|
+
reason?: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
/** Sleep function for testing. */
|
|
18
|
+
sleep?: (ms: number) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare class CircuitOpenError extends Error {
|
|
21
|
+
readonly until: number;
|
|
22
|
+
constructor(until: number);
|
|
23
|
+
}
|
|
24
|
+
export declare class CircuitBreaker {
|
|
25
|
+
private state;
|
|
26
|
+
private failures;
|
|
27
|
+
private openedAt;
|
|
28
|
+
private readonly threshold;
|
|
29
|
+
private readonly cooldown;
|
|
30
|
+
private readonly onStateChange?;
|
|
31
|
+
private readonly sleep;
|
|
32
|
+
constructor(opts?: CircuitBreakerOptions);
|
|
33
|
+
/**
|
|
34
|
+
* Run `fn`. If the breaker is open and still cooling down, throws CircuitOpenError
|
|
35
|
+
* without calling `fn`. Records success/failure and manages state.
|
|
36
|
+
*/
|
|
37
|
+
call<T>(fn: () => Promise<T>): Promise<T>;
|
|
38
|
+
/** Synchronous state check (mostly for tests). */
|
|
39
|
+
getState(): CircuitState;
|
|
40
|
+
getFailureCount(): number;
|
|
41
|
+
private onSuccess;
|
|
42
|
+
private onFailure;
|
|
43
|
+
private transition;
|
|
44
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CircuitBreaker = exports.CircuitOpenError = void 0;
|
|
4
|
+
class CircuitOpenError extends Error {
|
|
5
|
+
until;
|
|
6
|
+
constructor(until) {
|
|
7
|
+
super(`Circuit breaker open until ${new Date(until).toISOString()}`);
|
|
8
|
+
this.until = until;
|
|
9
|
+
this.name = 'CircuitOpenError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.CircuitOpenError = CircuitOpenError;
|
|
13
|
+
class CircuitBreaker {
|
|
14
|
+
state = 'closed';
|
|
15
|
+
failures = 0;
|
|
16
|
+
openedAt = 0;
|
|
17
|
+
threshold;
|
|
18
|
+
cooldown;
|
|
19
|
+
onStateChange;
|
|
20
|
+
sleep;
|
|
21
|
+
constructor(opts = {}) {
|
|
22
|
+
this.threshold = opts.failureThreshold ?? 5;
|
|
23
|
+
this.cooldown = opts.cooldownMs ?? 30_000;
|
|
24
|
+
this.onStateChange = opts.onStateChange;
|
|
25
|
+
this.sleep = opts.sleep ?? defaultSleep;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run `fn`. If the breaker is open and still cooling down, throws CircuitOpenError
|
|
29
|
+
* without calling `fn`. Records success/failure and manages state.
|
|
30
|
+
*/
|
|
31
|
+
async call(fn) {
|
|
32
|
+
if (this.state === 'open') {
|
|
33
|
+
if (Date.now() - this.openedAt >= this.cooldown) {
|
|
34
|
+
this.transition('half-open', 'cooldown elapsed');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw new CircuitOpenError(this.openedAt + this.cooldown);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const result = await fn();
|
|
42
|
+
this.onSuccess();
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
this.onFailure(e);
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Synchronous state check (mostly for tests). */
|
|
51
|
+
getState() { return this.state; }
|
|
52
|
+
getFailureCount() { return this.failures; }
|
|
53
|
+
onSuccess() {
|
|
54
|
+
this.failures = 0;
|
|
55
|
+
if (this.state !== 'closed')
|
|
56
|
+
this.transition('closed', 'call succeeded');
|
|
57
|
+
}
|
|
58
|
+
onFailure(e) {
|
|
59
|
+
this.failures++;
|
|
60
|
+
if (this.state === 'half-open' || (this.state === 'closed' && this.failures >= this.threshold)) {
|
|
61
|
+
this.openedAt = Date.now();
|
|
62
|
+
this.transition('open', e instanceof Error ? e.message : String(e));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
transition(to, reason) {
|
|
66
|
+
if (this.state === to)
|
|
67
|
+
return;
|
|
68
|
+
this.state = to;
|
|
69
|
+
if (to === 'closed')
|
|
70
|
+
this.failures = 0;
|
|
71
|
+
this.onStateChange?.(to, { failures: this.failures, reason });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.CircuitBreaker = CircuitBreaker;
|
|
75
|
+
function defaultSleep(ms) {
|
|
76
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/util/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-agnostic env-var reader.
|
|
3
|
+
*
|
|
4
|
+
* Tries, in order:
|
|
5
|
+
* 1. The canonical UPPER_SNAKE_CASE var (most SDKs read this)
|
|
6
|
+
* 2. The lowercase variant (some shells / dotenv loaders normalise to this)
|
|
7
|
+
* 3. Common alternates passed as `aliases`
|
|
8
|
+
*
|
|
9
|
+
* Returns `undefined` if none are set, never throws. Returns `undefined`
|
|
10
|
+
* (NOT '') for empty / whitespace / placeholder values, so callers can use
|
|
11
|
+
* the `??` operator and have it fall through to the next fallback.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getApiKey(canonical: string, ...aliases: string[]): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Same idea for non-secret env vars (base URLs, model names).
|
|
16
|
+
* Returns `undefined` for unset / empty / whitespace, so `??` chains work.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getEnv(canonical: string, ...aliases: string[]): string | undefined;
|