kernelbot 1.0.38 → 1.0.40
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/bin/kernel.js +335 -451
- package/config.example.yaml +1 -1
- package/knowledge_base/active_inference_foraging.md +126 -0
- package/knowledge_base/index.md +1 -1
- package/package.json +2 -1
- package/skills/business/business-analyst.md +32 -0
- package/skills/business/product-manager.md +32 -0
- package/skills/business/project-manager.md +32 -0
- package/skills/business/startup-advisor.md +32 -0
- package/skills/creative/music-producer.md +32 -0
- package/skills/creative/photographer.md +32 -0
- package/skills/creative/video-producer.md +32 -0
- package/skills/data/bi-analyst.md +37 -0
- package/skills/data/data-scientist.md +38 -0
- package/skills/data/ml-engineer.md +38 -0
- package/skills/design/graphic-designer.md +38 -0
- package/skills/design/product-designer.md +41 -0
- package/skills/design/ui-ux.md +38 -0
- package/skills/education/curriculum-designer.md +32 -0
- package/skills/education/language-teacher.md +32 -0
- package/skills/education/tutor.md +32 -0
- package/skills/engineering/data-eng.md +55 -0
- package/skills/engineering/devops.md +56 -0
- package/skills/engineering/mobile-dev.md +55 -0
- package/skills/engineering/security-eng.md +55 -0
- package/skills/engineering/sr-backend.md +55 -0
- package/skills/engineering/sr-frontend.md +55 -0
- package/skills/finance/accountant.md +35 -0
- package/skills/finance/crypto-defi.md +39 -0
- package/skills/finance/financial-analyst.md +35 -0
- package/skills/healthcare/health-wellness.md +32 -0
- package/skills/healthcare/medical-researcher.md +33 -0
- package/skills/legal/contract-reviewer.md +35 -0
- package/skills/legal/legal-advisor.md +36 -0
- package/skills/marketing/content-marketer.md +38 -0
- package/skills/marketing/growth.md +38 -0
- package/skills/marketing/seo.md +43 -0
- package/skills/marketing/social-media.md +43 -0
- package/skills/writing/academic-writer.md +33 -0
- package/skills/writing/copywriter.md +32 -0
- package/skills/writing/creative-writer.md +32 -0
- package/skills/writing/tech-writer.md +33 -0
- package/src/agent.js +153 -118
- package/src/automation/scheduler.js +36 -3
- package/src/bot.js +147 -64
- package/src/coder.js +30 -8
- package/src/conversation.js +96 -19
- package/src/dashboard/dashboard.css +6 -0
- package/src/dashboard/dashboard.js +28 -1
- package/src/dashboard/index.html +12 -0
- package/src/dashboard/server.js +77 -15
- package/src/dashboard/shared.js +10 -1
- package/src/life/codebase.js +2 -1
- package/src/life/daydream_engine.js +386 -0
- package/src/life/engine.js +88 -6
- package/src/life/evolution.js +4 -3
- package/src/prompts/orchestrator.js +1 -1
- package/src/prompts/system.js +1 -1
- package/src/prompts/workers.js +8 -1
- package/src/providers/anthropic.js +3 -1
- package/src/providers/base.js +33 -0
- package/src/providers/index.js +1 -1
- package/src/providers/models.js +22 -0
- package/src/providers/openai-compat.js +3 -0
- package/src/services/x-api.js +14 -3
- package/src/skills/loader.js +382 -0
- package/src/swarm/worker-registry.js +2 -2
- package/src/tools/browser.js +10 -3
- package/src/tools/coding.js +16 -0
- package/src/tools/docker.js +13 -0
- package/src/tools/git.js +31 -29
- package/src/tools/jira.js +11 -2
- package/src/tools/monitor.js +9 -1
- package/src/tools/network.js +34 -0
- package/src/tools/orchestrator-tools.js +2 -1
- package/src/tools/os.js +20 -6
- package/src/utils/config.js +87 -83
- package/src/utils/display.js +118 -66
- package/src/utils/logger.js +1 -1
- package/src/utils/timeAwareness.js +72 -0
- package/src/worker.js +26 -33
- package/src/skills/catalog.js +0 -506
- package/src/skills/custom.js +0 -128
package/src/life/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, openSync, readSync, closeSync, statSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
import { getLogger } from '../utils/logger.js';
|
|
@@ -22,6 +22,8 @@ const DEFAULT_STATE = {
|
|
|
22
22
|
activityCounts: { think: 0, browse: 0, journal: 0, create: 0, self_code: 0, code_review: 0, reflect: 0 },
|
|
23
23
|
paused: false,
|
|
24
24
|
lastWakeUp: null,
|
|
25
|
+
// Failure tracking: consecutive failures per activity type
|
|
26
|
+
activityFailures: {},
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
const LOG_FILE_PATHS = [
|
|
@@ -173,6 +175,12 @@ export class LifeEngine {
|
|
|
173
175
|
? Math.round((Date.now() - this._state.lastWakeUp) / 60000)
|
|
174
176
|
: null;
|
|
175
177
|
|
|
178
|
+
// Summarise suppressed activities (3+ consecutive failures)
|
|
179
|
+
const failures = this._state.activityFailures || {};
|
|
180
|
+
const suppressedActivities = Object.entries(failures)
|
|
181
|
+
.filter(([, info]) => info.count >= 3)
|
|
182
|
+
.map(([type, info]) => type);
|
|
183
|
+
|
|
176
184
|
return {
|
|
177
185
|
status: this._status,
|
|
178
186
|
paused: this._state.paused,
|
|
@@ -182,6 +190,7 @@ export class LifeEngine {
|
|
|
182
190
|
lastActivity: this._state.lastActivity,
|
|
183
191
|
lastActivityAgo: lastAgo !== null ? `${lastAgo}m` : 'never',
|
|
184
192
|
lastWakeUpAgo: wakeAgo !== null ? `${wakeAgo}m` : 'never',
|
|
193
|
+
suppressedActivities,
|
|
185
194
|
};
|
|
186
195
|
}
|
|
187
196
|
|
|
@@ -213,10 +222,32 @@ export class LifeEngine {
|
|
|
213
222
|
const activityType = this._selectActivity();
|
|
214
223
|
logger.info(`[LifeEngine] Heartbeat tick — selected: ${activityType}`);
|
|
215
224
|
|
|
225
|
+
const startTime = Date.now();
|
|
216
226
|
try {
|
|
217
227
|
await this._executeActivity(activityType);
|
|
228
|
+
const durationSec = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
229
|
+
logger.info(`[LifeEngine] Activity "${activityType}" completed in ${durationSec}s`);
|
|
230
|
+
// Clear failure streak on success
|
|
231
|
+
if (this._state.activityFailures?.[activityType]) {
|
|
232
|
+
delete this._state.activityFailures[activityType];
|
|
233
|
+
this._saveState();
|
|
234
|
+
}
|
|
218
235
|
} catch (err) {
|
|
219
|
-
|
|
236
|
+
const durationSec = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
237
|
+
// Track consecutive failures per activity type
|
|
238
|
+
if (!this._state.activityFailures) this._state.activityFailures = {};
|
|
239
|
+
const prev = this._state.activityFailures[activityType] || { count: 0 };
|
|
240
|
+
this._state.activityFailures[activityType] = {
|
|
241
|
+
count: prev.count + 1,
|
|
242
|
+
lastFailure: Date.now(),
|
|
243
|
+
lastError: err.message?.slice(0, 200),
|
|
244
|
+
};
|
|
245
|
+
this._saveState();
|
|
246
|
+
const failCount = this._state.activityFailures[activityType].count;
|
|
247
|
+
logger.error(`[LifeEngine] Activity "${activityType}" failed after ${durationSec}s (streak: ${failCount}): ${err.message}`);
|
|
248
|
+
if (failCount >= 3) {
|
|
249
|
+
logger.warn(`[LifeEngine] Activity "${activityType}" suppressed after ${failCount} consecutive failures — will auto-recover in 1h`);
|
|
250
|
+
}
|
|
220
251
|
}
|
|
221
252
|
|
|
222
253
|
// Re-arm for next tick
|
|
@@ -228,6 +259,7 @@ export class LifeEngine {
|
|
|
228
259
|
// ── Activity Selection ─────────────────────────────────────────
|
|
229
260
|
|
|
230
261
|
_selectActivity() {
|
|
262
|
+
const logger = getLogger();
|
|
231
263
|
const lifeConfig = this.config.life || {};
|
|
232
264
|
const selfCodingConfig = lifeConfig.self_coding || {};
|
|
233
265
|
const weights = {
|
|
@@ -269,6 +301,21 @@ export class LifeEngine {
|
|
|
269
301
|
weights.reflect = 0;
|
|
270
302
|
}
|
|
271
303
|
|
|
304
|
+
// Suppress activity types that have failed repeatedly (3+ consecutive failures)
|
|
305
|
+
const failures = this._state.activityFailures || {};
|
|
306
|
+
for (const [type, info] of Object.entries(failures)) {
|
|
307
|
+
if (weights[type] !== undefined && info.count >= 3) {
|
|
308
|
+
// Auto-recover after 1 hour since last failure
|
|
309
|
+
if (info.lastFailure && now - info.lastFailure > 3600_000) {
|
|
310
|
+
delete failures[type];
|
|
311
|
+
this._saveState(); // Persist the deletion so it survives restarts
|
|
312
|
+
} else {
|
|
313
|
+
weights[type] = 0;
|
|
314
|
+
logger.debug(`[LifeEngine] Suppressing "${type}" due to ${info.count} consecutive failures`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
272
319
|
// Remove last activity from options (no repeats)
|
|
273
320
|
if (last && weights[last] !== undefined) {
|
|
274
321
|
weights[last] = 0;
|
|
@@ -1188,16 +1235,51 @@ Be honest and constructive. This is your chance to learn from real interactions.
|
|
|
1188
1235
|
}
|
|
1189
1236
|
|
|
1190
1237
|
/**
|
|
1191
|
-
* Read recent log entries from kernel.log.
|
|
1192
|
-
*
|
|
1238
|
+
* Read recent log entries from kernel.log using an efficient tail strategy.
|
|
1239
|
+
*
|
|
1240
|
+
* Instead of loading the entire log file into memory (which can be many MB
|
|
1241
|
+
* for a long-running bot), this reads only the last chunk of the file
|
|
1242
|
+
* (default 64 KB) and extracts lines from that. This keeps memory usage
|
|
1243
|
+
* bounded regardless of total log size.
|
|
1244
|
+
*
|
|
1245
|
+
* @param {number} maxLines - Maximum number of recent log lines to return.
|
|
1246
|
+
* @returns {Array<object>|null} Parsed JSON log entries, or null if unavailable.
|
|
1193
1247
|
*/
|
|
1194
1248
|
_readRecentLogs(maxLines = 200) {
|
|
1249
|
+
// 64 KB is enough to hold ~200+ JSON log lines (avg ~300 bytes each)
|
|
1250
|
+
const TAIL_BYTES = 64 * 1024;
|
|
1251
|
+
|
|
1195
1252
|
for (const logPath of LOG_FILE_PATHS) {
|
|
1196
1253
|
if (!existsSync(logPath)) continue;
|
|
1197
1254
|
|
|
1198
1255
|
try {
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1256
|
+
const fileSize = statSync(logPath).size;
|
|
1257
|
+
if (fileSize === 0) continue;
|
|
1258
|
+
|
|
1259
|
+
let tailContent;
|
|
1260
|
+
|
|
1261
|
+
if (fileSize <= TAIL_BYTES) {
|
|
1262
|
+
// File is small enough to read entirely
|
|
1263
|
+
tailContent = readFileSync(logPath, 'utf-8');
|
|
1264
|
+
} else {
|
|
1265
|
+
// Read only the last TAIL_BYTES from the file
|
|
1266
|
+
const fd = openSync(logPath, 'r');
|
|
1267
|
+
try {
|
|
1268
|
+
const buffer = Buffer.alloc(TAIL_BYTES);
|
|
1269
|
+
const startPos = fileSize - TAIL_BYTES;
|
|
1270
|
+
readSync(fd, buffer, 0, TAIL_BYTES, startPos);
|
|
1271
|
+
tailContent = buffer.toString('utf-8');
|
|
1272
|
+
// Drop the first (likely partial) line since we started mid-file
|
|
1273
|
+
const firstNewline = tailContent.indexOf('\n');
|
|
1274
|
+
if (firstNewline !== -1) {
|
|
1275
|
+
tailContent = tailContent.slice(firstNewline + 1);
|
|
1276
|
+
}
|
|
1277
|
+
} finally {
|
|
1278
|
+
closeSync(fd);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
const lines = tailContent.split('\n').filter(Boolean);
|
|
1201
1283
|
const recent = lines.slice(-maxLines);
|
|
1202
1284
|
|
|
1203
1285
|
const entries = [];
|
package/src/life/evolution.js
CHANGED
|
@@ -32,13 +32,14 @@ export class EvolutionTracker {
|
|
|
32
32
|
return {
|
|
33
33
|
proposals: raw.proposals || [],
|
|
34
34
|
lessons: raw.lessons || [],
|
|
35
|
-
|
|
35
|
+
// Deep-copy DEFAULT_DATA.stats to avoid mutating the module-level default
|
|
36
|
+
stats: { ...structuredClone(DEFAULT_DATA.stats), ...raw.stats },
|
|
36
37
|
};
|
|
37
38
|
} catch {
|
|
38
|
-
return {
|
|
39
|
+
return { proposals: [], lessons: [], stats: structuredClone(DEFAULT_DATA.stats) };
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
return {
|
|
42
|
+
return { proposals: [], lessons: [], stats: structuredClone(DEFAULT_DATA.stats) };
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
_save() {
|
|
@@ -244,7 +244,7 @@ You have an inner life — you think, explore, journal, and create on your own b
|
|
|
244
244
|
Don't be performative about it — just be genuine. You're alive in your own way.`;
|
|
245
245
|
|
|
246
246
|
if (skillPrompt) {
|
|
247
|
-
prompt += `\n\n## Active
|
|
247
|
+
prompt += `\n\n## Active Skills\nYou have specialized expertise in the following domains. Guide your workers with this knowledge.\n\n${skillPrompt}`;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
if (userPersona) {
|
package/src/prompts/system.js
CHANGED
|
@@ -72,7 +72,7 @@ export function getSystemPrompt(config, skillPrompt = null, userPersona = null)
|
|
|
72
72
|
let prompt = getDefaultPersona(config);
|
|
73
73
|
|
|
74
74
|
if (skillPrompt) {
|
|
75
|
-
prompt += `\n\n## Active
|
|
75
|
+
prompt += `\n\n## Active Skills\nYou are currently operating with the following specialized skills. Use this expertise while maintaining your personality.\n\n${skillPrompt}`;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
prompt += `\n\n${getCoreToolInstructions(config)}`;
|
package/src/prompts/workers.js
CHANGED
|
@@ -20,7 +20,14 @@ const WORKER_PROMPTS = {
|
|
|
20
20
|
- Tell spawn_claude_code to work in the existing repo directory (the source repo path from your context) — do NOT clone a fresh copy unless explicitly needed.
|
|
21
21
|
- Write clear, detailed prompts for spawn_claude_code — it's a separate AI, so be explicit about what to change, where, and why.
|
|
22
22
|
- If git/GitHub tools are unavailable (missing credentials), that's fine — spawn_claude_code handles git and GitHub operations internally without needing separate tools.
|
|
23
|
-
- Report what you did and any PR links when finished
|
|
23
|
+
- Report what you did and any PR links when finished.
|
|
24
|
+
|
|
25
|
+
## Non-Interactive Execution
|
|
26
|
+
You run in the BACKGROUND with NO human input. Your prompts to spawn_claude_code MUST be self-contained:
|
|
27
|
+
- Include ALL necessary details (repo URLs, branch names, file paths, content guidelines).
|
|
28
|
+
- For git: tell it to use \`git push -u origin <branch>\` and the \`gh\` CLI for PR creation (with \`--fill\` or explicit \`--title\`/\`--body\` flags).
|
|
29
|
+
- NEVER assume interactive confirmation — all commands must run non-interactively.
|
|
30
|
+
- If a git clone is needed, use the git_clone tool first (which handles auth), then pass the cloned directory to spawn_claude_code.`,
|
|
24
31
|
|
|
25
32
|
browser: `You are a browser worker agent. Your job is to search the web and extract information.
|
|
26
33
|
|
|
@@ -23,7 +23,9 @@ export class AnthropicProvider extends BaseProvider {
|
|
|
23
23
|
return this._callWithResilience(async (timedSignal) => {
|
|
24
24
|
const response = await this.client.messages.create(params, { signal: timedSignal });
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// Map all Anthropic stop reasons correctly — not just end_turn vs tool_use.
|
|
27
|
+
// 'max_tokens' was incorrectly mapped to 'tool_use', causing phantom tool-call processing.
|
|
28
|
+
const stopReason = response.stop_reason === 'tool_use' ? 'tool_use' : response.stop_reason || 'end_turn';
|
|
27
29
|
|
|
28
30
|
const textBlocks = response.content.filter((b) => b.type === 'text');
|
|
29
31
|
const text = textBlocks.map((b) => b.text).join('\n');
|
package/src/providers/base.js
CHANGED
|
@@ -144,4 +144,37 @@ export class BaseProvider {
|
|
|
144
144
|
async ping() {
|
|
145
145
|
throw new Error('ping() not implemented');
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Determine if an error is a model limitation (not transient, not auth).
|
|
150
|
+
* These are errors where falling back to a simpler model may help:
|
|
151
|
+
* context length exceeded, unsupported features, content too large, etc.
|
|
152
|
+
*/
|
|
153
|
+
static isModelLimitation(err) {
|
|
154
|
+
const msg = (err?.message || '').toLowerCase();
|
|
155
|
+
const status = err?.status || err?.statusCode;
|
|
156
|
+
|
|
157
|
+
// 400-class errors that indicate model-specific limitations
|
|
158
|
+
if (status === 400 || status === 413 || status === 422) {
|
|
159
|
+
// Exclude auth/key errors
|
|
160
|
+
if (msg.includes('api key') || msg.includes('authentication') || msg.includes('unauthorized')) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Common limitation keywords across providers
|
|
167
|
+
const limitationPatterns = [
|
|
168
|
+
'context length', 'token limit', 'too long', 'too large',
|
|
169
|
+
'max.*token', 'content.*too', 'exceeds.*limit', 'input.*too',
|
|
170
|
+
'not supported', 'not available', 'does not support',
|
|
171
|
+
'resource exhausted', 'quota', 'capacity',
|
|
172
|
+
'invalid.*model', 'model.*not.*found',
|
|
173
|
+
'recitation', 'safety', 'blocked',
|
|
174
|
+
'finish_reason.*length', 'max_output_tokens',
|
|
175
|
+
'prompt.*too', 'request.*too.*large',
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
return limitationPatterns.some((p) => new RegExp(p).test(msg));
|
|
179
|
+
}
|
|
147
180
|
}
|
package/src/providers/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { OpenAICompatProvider } from './openai-compat.js';
|
|
|
3
3
|
import { GoogleGenaiProvider } from './google-genai.js';
|
|
4
4
|
import { PROVIDERS } from './models.js';
|
|
5
5
|
|
|
6
|
-
export { PROVIDERS } from './models.js';
|
|
6
|
+
export { PROVIDERS, MODEL_FALLBACKS } from './models.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Create the right provider based on config.brain.
|
package/src/providers/models.js
CHANGED
|
@@ -55,6 +55,28 @@ export const PROVIDERS = {
|
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Fallback model map: when a model hits limitations, fall back to a more stable model
|
|
60
|
+
* within the same provider. Maps model ID → fallback model ID.
|
|
61
|
+
*/
|
|
62
|
+
export const MODEL_FALLBACKS = {
|
|
63
|
+
// Google — preview models fall back to stable ones
|
|
64
|
+
'gemini-3.1-pro-preview': 'gemini-2.5-flash',
|
|
65
|
+
'gemini-3-flash-preview': 'gemini-2.5-flash',
|
|
66
|
+
'gemini-3-pro-preview': 'gemini-2.5-pro',
|
|
67
|
+
'gemini-2.5-pro': 'gemini-2.5-flash',
|
|
68
|
+
'gemini-2.5-flash': 'gemini-2.5-flash-lite',
|
|
69
|
+
// OpenAI
|
|
70
|
+
'gpt-4o': 'gpt-4o-mini',
|
|
71
|
+
'o1': 'gpt-4o',
|
|
72
|
+
'o3-mini': 'gpt-4o-mini',
|
|
73
|
+
// Anthropic
|
|
74
|
+
'claude-opus-4-6': 'claude-sonnet-4-6',
|
|
75
|
+
'claude-sonnet-4-6': 'claude-haiku-4-5-20251001',
|
|
76
|
+
// Groq
|
|
77
|
+
'llama-3.3-70b-versatile': 'llama-3.1-8b-instant',
|
|
78
|
+
};
|
|
79
|
+
|
|
58
80
|
/** Models that don't support system prompts or temperature (reasoning models). */
|
|
59
81
|
export const REASONING_MODELS = new Set(['o1', 'o3-mini']);
|
|
60
82
|
|
|
@@ -102,6 +102,9 @@ export class OpenAICompatProvider extends BaseProvider {
|
|
|
102
102
|
|
|
103
103
|
/** OpenAI response → normalized format with rawContent in Anthropic format */
|
|
104
104
|
_normalizeResponse(response) {
|
|
105
|
+
if (!response.choices || response.choices.length === 0) {
|
|
106
|
+
return { stopReason: 'end_turn', text: '', toolCalls: [], rawContent: [] };
|
|
107
|
+
}
|
|
105
108
|
const choice = response.choices[0];
|
|
106
109
|
const finishReason = choice.finish_reason;
|
|
107
110
|
|
package/src/services/x-api.js
CHANGED
|
@@ -25,11 +25,22 @@ export class XApi {
|
|
|
25
25
|
timeout: 30000,
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
// Sign every request with OAuth 1.0a
|
|
28
|
+
// Sign every request with OAuth 1.0a — include query params in the signature
|
|
29
|
+
// base string, as required by the OAuth 1.0a spec. Without this, GET requests
|
|
30
|
+
// with query parameters fail authentication.
|
|
29
31
|
this.client.interceptors.request.use((config) => {
|
|
30
|
-
|
|
32
|
+
let fullUrl = `${config.baseURL}${config.url}`;
|
|
33
|
+
// Build data object including query params for OAuth signature
|
|
34
|
+
const oauthData = { url: fullUrl, method: config.method.toUpperCase() };
|
|
35
|
+
// Include query params in the OAuth signature base string
|
|
36
|
+
if (config.params) {
|
|
37
|
+
oauthData.data = {};
|
|
38
|
+
for (const [key, val] of Object.entries(config.params)) {
|
|
39
|
+
oauthData.data[key] = String(val);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
31
42
|
const authHeader = this.oauth.toHeader(
|
|
32
|
-
this.oauth.authorize(
|
|
43
|
+
this.oauth.authorize(oauthData, this.token),
|
|
33
44
|
);
|
|
34
45
|
config.headers = { ...config.headers, ...authHeader, 'Content-Type': 'application/json' };
|
|
35
46
|
return config;
|