takomi 2.1.34 → 2.1.35
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/.pi/extensions/takomi-runtime/context-panel.ts +37 -13
- package/.pi/extensions/takomi-runtime/model-routing-defaults.ts +35 -5
- package/.pi/extensions/takomi-runtime/routing-policy.ts +35 -7
- package/.pi/extensions/takomi-runtime/takomi-stats.js +13 -3
- package/.pi/extensions/takomi-subagents/index.ts +3 -3
- package/.pi/extensions/takomi-subagents/native-render.ts +50 -5
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +32 -9
- package/.pi/extensions/takomi-subagents/tool-runner.ts +61 -15
- package/assets/.agent/skills/photo-book-builder/SKILL.md +51 -7
- package/package.json +18 -2
- package/src/cli.js +41 -11
- package/src/harness.js +12 -42
- package/src/owned-tree.js +86 -0
- package/src/pi-installer.js +12 -36
- package/src/store.js +3 -40
- package/src/takomi-stats.js +14 -3
- package/src/utils.js +81 -44
package/src/utils.js
CHANGED
|
@@ -2,10 +2,10 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import https from 'https';
|
|
5
|
-
import {
|
|
5
|
+
import { execFile } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
9
|
|
|
10
10
|
// Reduced timeout to fail fast on bad connections
|
|
11
11
|
const FETCH_TIMEOUT = 10000;
|
|
@@ -17,9 +17,18 @@ const __dirname = path.dirname(__filename);
|
|
|
17
17
|
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
18
18
|
|
|
19
19
|
// GitHub repository configuration
|
|
20
|
-
const GITHUB_REPO = '
|
|
20
|
+
const GITHUB_REPO = 'J-StaR-Films-Studios/VibeCode-Protocol-Suite';
|
|
21
21
|
const GITHUB_BRANCH = 'main';
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
function githubRef(options = {}) {
|
|
24
|
+
const repo = options.repo || GITHUB_REPO;
|
|
25
|
+
const branch = options.branch || GITHUB_BRANCH;
|
|
26
|
+
return {
|
|
27
|
+
repo,
|
|
28
|
+
branch,
|
|
29
|
+
rawBaseUrl: `https://raw.githubusercontent.com/${repo}/${branch}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
23
32
|
|
|
24
33
|
export const PATHS = {
|
|
25
34
|
root: PACKAGE_ROOT,
|
|
@@ -134,9 +143,10 @@ function delay(ms) {
|
|
|
134
143
|
* @param {number} retries - Number of retries (default: 3)
|
|
135
144
|
* @returns {Promise<string>} File content
|
|
136
145
|
*/
|
|
137
|
-
export async function fetchFromGitHub(relativePath, retries = 3) {
|
|
146
|
+
export async function fetchFromGitHub(relativePath, retries = 3, options = {}) {
|
|
138
147
|
// Use raw GitHub content URL to avoid API rate limits
|
|
139
|
-
const
|
|
148
|
+
const { rawBaseUrl } = githubRef(options);
|
|
149
|
+
const rawUrl = `${rawBaseUrl}/${relativePath}`;
|
|
140
150
|
|
|
141
151
|
return new Promise((resolve, reject) => {
|
|
142
152
|
const attempt = (remainingRetries) => {
|
|
@@ -146,6 +156,19 @@ export async function fetchFromGitHub(relativePath, retries = 3) {
|
|
|
146
156
|
'User-Agent': 'takomi-cli'
|
|
147
157
|
}
|
|
148
158
|
}, (res) => {
|
|
159
|
+
if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location) {
|
|
160
|
+
res.resume();
|
|
161
|
+
const redirectedUrl = new URL(res.headers.location, rawUrl).toString();
|
|
162
|
+
https.get(redirectedUrl, {
|
|
163
|
+
timeout: FETCH_TIMEOUT,
|
|
164
|
+
headers: { 'User-Agent': 'takomi-cli' }
|
|
165
|
+
}, (redirectRes) => {
|
|
166
|
+
let redirectedData = '';
|
|
167
|
+
redirectRes.on('data', chunk => redirectedData += chunk);
|
|
168
|
+
redirectRes.on('end', () => redirectRes.statusCode === 200 ? resolve(redirectedData) : reject(new Error(`HTTP ${redirectRes.statusCode}`)));
|
|
169
|
+
}).on('error', reject);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
149
172
|
let data = '';
|
|
150
173
|
res.on('data', chunk => data += chunk);
|
|
151
174
|
res.on('end', () => {
|
|
@@ -167,8 +190,8 @@ export async function fetchFromGitHub(relativePath, retries = 3) {
|
|
|
167
190
|
} else {
|
|
168
191
|
// Fallback to curl if Node https fails (e.g. proxy/network issues)
|
|
169
192
|
try {
|
|
170
|
-
// Ensure we use a timeout with curl too
|
|
171
|
-
const { stdout } = await
|
|
193
|
+
// Ensure we use a timeout with curl too, without invoking a shell.
|
|
194
|
+
const { stdout } = await execFileAsync('curl', ['-sL', '--max-time', String(CURL_TIMEOUT), rawUrl]);
|
|
172
195
|
if (stdout) resolve(stdout);
|
|
173
196
|
else reject(err);
|
|
174
197
|
} catch (curlErr) {
|
|
@@ -192,36 +215,49 @@ export async function fetchFromGitHub(relativePath, retries = 3) {
|
|
|
192
215
|
* @param {string} relativePath - Path relative to repo root
|
|
193
216
|
* @returns {Promise<Array>} Array of file objects
|
|
194
217
|
*/
|
|
195
|
-
export async function fetchDirectoryListing(relativePath) {
|
|
196
|
-
const
|
|
218
|
+
export async function fetchDirectoryListing(relativePath, options = {}) {
|
|
219
|
+
const { repo, branch } = githubRef(options);
|
|
220
|
+
const apiUrl = `https://api.github.com/repos/${repo}/contents/${relativePath}?ref=${branch}`;
|
|
197
221
|
|
|
198
222
|
return new Promise((resolve, reject) => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}, (res) => {
|
|
206
|
-
let data = '';
|
|
207
|
-
res.on('data', chunk => data += chunk);
|
|
208
|
-
res.on('end', () => {
|
|
209
|
-
try {
|
|
210
|
-
if (res.statusCode === 200) {
|
|
211
|
-
const items = JSON.parse(data);
|
|
212
|
-
resolve(Array.isArray(items) ? items : []);
|
|
213
|
-
} else if (res.statusCode === 404) {
|
|
214
|
-
resolve([]);
|
|
215
|
-
} else if (res.statusCode === 403) {
|
|
216
|
-
reject(new Error('GitHub API rate limit exceeded. Please try again later.'));
|
|
217
|
-
} else {
|
|
218
|
-
reject(new Error(`GitHub API error: ${res.statusCode}`));
|
|
219
|
-
}
|
|
220
|
-
} catch (e) {
|
|
221
|
-
reject(new Error('Failed to parse GitHub API response'));
|
|
223
|
+
const requestUrl = (url, redirectsRemaining = 3) => {
|
|
224
|
+
const req = https.get(url, {
|
|
225
|
+
timeout: 10000,
|
|
226
|
+
headers: {
|
|
227
|
+
'User-Agent': 'takomi-cli',
|
|
228
|
+
'Accept': 'application/vnd.github.v3+json'
|
|
222
229
|
}
|
|
230
|
+
}, (res) => {
|
|
231
|
+
if ([301, 302, 307, 308].includes(res.statusCode) && res.headers.location && redirectsRemaining > 0) {
|
|
232
|
+
res.resume();
|
|
233
|
+
requestUrl(new URL(res.headers.location, url).toString(), redirectsRemaining - 1);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let data = '';
|
|
238
|
+
res.on('data', chunk => data += chunk);
|
|
239
|
+
res.on('end', () => {
|
|
240
|
+
try {
|
|
241
|
+
if (res.statusCode === 200) {
|
|
242
|
+
const items = JSON.parse(data);
|
|
243
|
+
resolve(Array.isArray(items) ? items : []);
|
|
244
|
+
} else if (res.statusCode === 404) {
|
|
245
|
+
resolve([]);
|
|
246
|
+
} else if (res.statusCode === 403) {
|
|
247
|
+
reject(new Error('GitHub API rate limit exceeded. Please try again later.'));
|
|
248
|
+
} else {
|
|
249
|
+
reject(new Error(`GitHub API error: ${res.statusCode}`));
|
|
250
|
+
}
|
|
251
|
+
} catch (e) {
|
|
252
|
+
reject(new Error('Failed to parse GitHub API response'));
|
|
253
|
+
}
|
|
254
|
+
});
|
|
223
255
|
});
|
|
224
|
-
|
|
256
|
+
req.on('timeout', () => req.destroy(new Error('Request timed out')));
|
|
257
|
+
req.on('error', reject);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
requestUrl(apiUrl);
|
|
225
261
|
});
|
|
226
262
|
}
|
|
227
263
|
|
|
@@ -231,9 +267,9 @@ export async function fetchDirectoryListing(relativePath) {
|
|
|
231
267
|
* @param {string} destPath - Local destination path
|
|
232
268
|
* @returns {Promise<boolean>} Success status
|
|
233
269
|
*/
|
|
234
|
-
export async function downloadFromGitHub(relativePath, destPath) {
|
|
270
|
+
export async function downloadFromGitHub(relativePath, destPath, options = {}) {
|
|
235
271
|
try {
|
|
236
|
-
const content = await fetchFromGitHub(relativePath);
|
|
272
|
+
const content = await fetchFromGitHub(relativePath, 3, options);
|
|
237
273
|
await fs.ensureDir(path.dirname(destPath));
|
|
238
274
|
await fs.writeFile(destPath, content, 'utf8');
|
|
239
275
|
return true;
|
|
@@ -265,11 +301,11 @@ export async function downloadFromGitHub(relativePath, destPath) {
|
|
|
265
301
|
* @param {number} delayMs - Delay between file downloads in ms (default: 100)
|
|
266
302
|
* @returns {Promise<number>} Number of files downloaded
|
|
267
303
|
*/
|
|
268
|
-
export async function downloadDirectoryFromGitHub(relativePath, destPath, filter = null, delayMs = 250) {
|
|
304
|
+
export async function downloadDirectoryFromGitHub(relativePath, destPath, filter = null, delayMs = 250, options = {}) {
|
|
269
305
|
let count = 0;
|
|
270
306
|
|
|
271
307
|
try {
|
|
272
|
-
const items = await fetchDirectoryListing(relativePath);
|
|
308
|
+
const items = await fetchDirectoryListing(relativePath, options);
|
|
273
309
|
|
|
274
310
|
for (const item of items) {
|
|
275
311
|
if (!useGitHub) break; // Allow aborting if we disabled GitHub midway
|
|
@@ -277,7 +313,7 @@ export async function downloadDirectoryFromGitHub(relativePath, destPath, filter
|
|
|
277
313
|
|
|
278
314
|
if (item.type === 'file') {
|
|
279
315
|
const fileDest = path.join(destPath, item.name);
|
|
280
|
-
const success = await downloadFromGitHub(item.path, fileDest);
|
|
316
|
+
const success = await downloadFromGitHub(item.path, fileDest, options);
|
|
281
317
|
if (success) {
|
|
282
318
|
count++;
|
|
283
319
|
// Add small delay between file downloads to be nice to GitHub
|
|
@@ -287,12 +323,13 @@ export async function downloadDirectoryFromGitHub(relativePath, destPath, filter
|
|
|
287
323
|
}
|
|
288
324
|
} else if (item.type === 'dir') {
|
|
289
325
|
const subDirDest = path.join(destPath, item.name);
|
|
290
|
-
const subCount = await downloadDirectoryFromGitHub(item.path, subDirDest, filter, delayMs);
|
|
326
|
+
const subCount = await downloadDirectoryFromGitHub(item.path, subDirDest, filter, delayMs, options);
|
|
291
327
|
count += subCount;
|
|
292
328
|
}
|
|
293
329
|
}
|
|
294
330
|
} catch (error) {
|
|
295
331
|
console.error(`Error downloading directory ${relativePath}: ${error.message}`);
|
|
332
|
+
if (options.throwOnError) throw error;
|
|
296
333
|
}
|
|
297
334
|
|
|
298
335
|
return count;
|
|
@@ -459,7 +496,7 @@ export async function copyLegacyManual(destFolder) {
|
|
|
459
496
|
export async function updateWorkflows(destFolder) {
|
|
460
497
|
console.log('📡 Fetching latest workflows from GitHub...');
|
|
461
498
|
await fs.ensureDir(destFolder);
|
|
462
|
-
|
|
499
|
+
return downloadDirectoryFromGitHub('assets/.agent/workflows', destFolder, null, 250, { throwOnError: true });
|
|
463
500
|
}
|
|
464
501
|
|
|
465
502
|
/**
|
|
@@ -468,7 +505,7 @@ export async function updateWorkflows(destFolder) {
|
|
|
468
505
|
export async function updateSkills(destFolder) {
|
|
469
506
|
console.log('📡 Fetching latest skills from GitHub...');
|
|
470
507
|
await fs.ensureDir(destFolder);
|
|
471
|
-
|
|
508
|
+
return downloadDirectoryFromGitHub('assets/.agent/skills', destFolder, null, 250, { throwOnError: true });
|
|
472
509
|
}
|
|
473
510
|
|
|
474
511
|
/**
|
|
@@ -477,7 +514,7 @@ export async function updateSkills(destFolder) {
|
|
|
477
514
|
export async function updateAgentYamls(destFolder) {
|
|
478
515
|
console.log('📡 Fetching latest Agent YAMLs from GitHub...');
|
|
479
516
|
await fs.ensureDir(destFolder);
|
|
480
|
-
|
|
517
|
+
return downloadDirectoryFromGitHub('assets/Takomi-Agents', destFolder, null, 250, { throwOnError: true });
|
|
481
518
|
}
|
|
482
519
|
|
|
483
520
|
/**
|
|
@@ -486,5 +523,5 @@ export async function updateAgentYamls(destFolder) {
|
|
|
486
523
|
export async function updateLegacyManual(destFolder) {
|
|
487
524
|
console.log('📡 Fetching latest Legacy Protocols from GitHub...');
|
|
488
525
|
await fs.ensureDir(destFolder);
|
|
489
|
-
|
|
526
|
+
return downloadDirectoryFromGitHub('assets/Legacy', destFolder, null, 250, { throwOnError: true });
|
|
490
527
|
}
|