botmux 3.2.0 → 3.2.1
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.en.md +1 -0
- package/README.md +1 -0
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +13 -1
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +11 -1
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +6 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +7 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/daemon-lifecycle-env.d.ts +11 -0
- package/dist/cli/daemon-lifecycle-env.d.ts.map +1 -0
- package/dist/cli/daemon-lifecycle-env.js +25 -0
- package/dist/cli/daemon-lifecycle-env.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -4
- package/dist/cli.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts +110 -0
- package/dist/codex-rpc-engine.d.ts.map +1 -0
- package/dist/codex-rpc-engine.js +496 -0
- package/dist/codex-rpc-engine.js.map +1 -0
- package/dist/codex-rpc-lifecycle.d.ts +157 -0
- package/dist/codex-rpc-lifecycle.d.ts.map +1 -0
- package/dist/codex-rpc-lifecycle.js +210 -0
- package/dist/codex-rpc-lifecycle.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -2
- package/dist/config.js.map +1 -1
- package/dist/core/attachment-path.d.ts +3 -0
- package/dist/core/attachment-path.d.ts.map +1 -0
- package/dist/core/attachment-path.js +8 -0
- package/dist/core/attachment-path.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +6 -1
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-images.d.ts +29 -0
- package/dist/core/dashboard-images.d.ts.map +1 -0
- package/dist/core/dashboard-images.js +113 -0
- package/dist/core/dashboard-images.js.map +1 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +13 -1
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/github-auth.d.ts +5 -0
- package/dist/core/github-auth.d.ts.map +1 -1
- package/dist/core/github-auth.js +38 -0
- package/dist/core/github-auth.js.map +1 -1
- package/dist/core/maintenance.d.ts +1 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +9 -1
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/session-create.d.ts +10 -0
- package/dist/core/session-create.d.ts.map +1 -1
- package/dist/core/session-create.js +21 -0
- package/dist/core/session-create.js.map +1 -1
- package/dist/core/session-manager.d.ts +4 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +25 -13
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +3 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +17 -3
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2 -0
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +19 -3
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +19 -0
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +6 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +23 -2
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/create-session-composer.d.ts +35 -0
- package/dist/dashboard/web/create-session-composer.d.ts.map +1 -0
- package/dist/dashboard/web/create-session-composer.js +70 -0
- package/dist/dashboard/web/create-session-composer.js.map +1 -0
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +65 -5
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +147 -1
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +2 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +5 -2
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts +4 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +19 -0
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-4PM4T6QQ.js → bot-defaults-page-O37SAQD5.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-RXVA6XW3.js → chunk-6QHMLMIM.js} +6 -6
- package/dist/dashboard-web/chunks/{chunk-IKCZ677K.js → chunk-B42PCL5P.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-3PMN55Z6.js → chunk-FYALHCPG.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-AIDN2WZB.js → chunk-O2DPQL7C.js} +1 -1
- package/dist/dashboard-web/chunks/connectors-page-DZBQ42MH.js +1 -0
- package/dist/dashboard-web/chunks/{groups-page-TAU2UIVS.js → groups-page-3IEGRZKQ.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-PJ2XVM6T.js → insights-page-X3ADRON3.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-D6445HJO.js → monitor-room-IV36RGQY.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-GUHMCKSO.js → monitoring-page-ADFKVON4.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-6K6U2MS5.js → office-page-CALAOWNS.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-USDYU7U5.js → overview-page-SLMGLK3M.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-O4OUA7ZH.js → roles-page-L7JJ4FFV.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-LNIBXJSZ.js → schedules-page-Z3672POL.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-OKJ5YWUZ.js +3 -0
- package/dist/dashboard-web/chunks/settings-page-P6MI2ZUE.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-M5KYNXBA.js → skills-page-FT7BVZ6X.js} +4 -4
- package/dist/dashboard-web/chunks/{team-federation-page-GT7UFQMN.js → team-federation-page-DEYCPUFB.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-UYZIRJ2C.js → v3-page-4IOWREZG.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-WAR77DQE.js → whiteboards-page-2VMBETZH.js} +1 -1
- package/dist/dashboard-web/style.css +191 -0
- package/dist/dashboard.js +33 -6
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +7 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +2 -0
- package/dist/global-config.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts.map +1 -1
- package/dist/im/lark/ask-card.js +52 -4
- package/dist/im/lark/ask-card.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +2 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +19 -2
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +3 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +706 -693
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts +8 -4
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +13 -6
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/services/connector-store.d.ts +8 -0
- package/dist/services/connector-store.d.ts.map +1 -1
- package/dist/services/connector-store.js.map +1 -1
- package/dist/services/session-store.d.ts +2 -1
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +60 -2
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/skill-registry-store.d.ts +16 -0
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +343 -47
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/trigger-types.d.ts +5 -0
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +12 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/types.d.ts +13 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.js +440 -34
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/connectors-page-2MKF4YTN.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-B6U6TCFF.js +0 -3
- package/dist/dashboard-web/chunks/settings-page-PXLRFF7I.js +0 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from 'node:fs';
|
|
1
|
+
import { closeSync, cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, realpathSync, rmSync, statSync } from 'node:fs';
|
|
2
2
|
import { execFile, execFileSync } from 'node:child_process';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { promisify } from 'node:util';
|
|
6
6
|
import { atomicWriteFileSync } from '../utils/atomic-write.js';
|
|
7
7
|
import { withFileLock, withFileLockSync } from '../utils/file-lock.js';
|
|
8
|
+
import { githubGitAuthEnv } from '../core/github-auth.js';
|
|
8
9
|
import { loadSkillPackage } from '../core/skills/package.js';
|
|
9
10
|
import { skillRegistryPath, skillSourcesDir, skillStoreDir } from '../core/skills/registry-paths.js';
|
|
10
11
|
import { assertAllowedGitProtocol, assertNoGitUrlCredentials, assertSafeGitRef, assertSafeGitSkillPath, githubToGitUrl, redactGitUrlCredentials } from '../core/skills/sources.js';
|
|
@@ -30,6 +31,79 @@ function writeSkillRegistry(registry) {
|
|
|
30
31
|
mkdirSync(dirname(skillRegistryPath()), { recursive: true });
|
|
31
32
|
atomicWriteFileSync(skillRegistryPath(), JSON.stringify(registry, null, 2) + '\n', { mode: 0o600 });
|
|
32
33
|
}
|
|
34
|
+
function shebangRuntime(path) {
|
|
35
|
+
const buffer = Buffer.alloc(256);
|
|
36
|
+
let fd;
|
|
37
|
+
try {
|
|
38
|
+
fd = openSync(path, 'r');
|
|
39
|
+
const length = readSync(fd, buffer, 0, buffer.length, 0);
|
|
40
|
+
const line = buffer.subarray(0, length).toString('utf8').split(/\r?\n/, 1)[0];
|
|
41
|
+
const match = /^#!\s*(\S+)(?:\s+(\S+))?/.exec(line);
|
|
42
|
+
if (!match)
|
|
43
|
+
return undefined;
|
|
44
|
+
const command = basename(match[1]);
|
|
45
|
+
return command === 'env' && match[2] ? basename(match[2]) : command;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
if (fd !== undefined)
|
|
52
|
+
closeSync(fd);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Static, non-executing install audit. Paths are relative to the Skill root;
|
|
56
|
+
* source URLs and absolute host paths are deliberately excluded from logs. */
|
|
57
|
+
export function buildSkillInstallAuditSummary(pkg) {
|
|
58
|
+
let files = 0;
|
|
59
|
+
let directories = 0;
|
|
60
|
+
let symlinks = 0;
|
|
61
|
+
let bytes = 0;
|
|
62
|
+
const executablePaths = [];
|
|
63
|
+
const runtimes = new Set();
|
|
64
|
+
const pending = [''];
|
|
65
|
+
while (pending.length > 0) {
|
|
66
|
+
const relDir = pending.pop();
|
|
67
|
+
const absDir = relDir ? join(pkg.rootDir, relDir) : pkg.rootDir;
|
|
68
|
+
for (const entry of readdirSync(absDir, { withFileTypes: true })) {
|
|
69
|
+
const rel = (relDir ? join(relDir, entry.name) : entry.name).replace(/\\/g, '/');
|
|
70
|
+
const abs = join(pkg.rootDir, rel);
|
|
71
|
+
if (entry.isSymbolicLink()) {
|
|
72
|
+
symlinks++;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (entry.isDirectory()) {
|
|
76
|
+
directories++;
|
|
77
|
+
pending.push(rel);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!entry.isFile())
|
|
81
|
+
continue;
|
|
82
|
+
files++;
|
|
83
|
+
const stat = lstatSync(abs);
|
|
84
|
+
bytes += stat.size;
|
|
85
|
+
if ((stat.mode & 0o111) !== 0) {
|
|
86
|
+
executablePaths.push(rel);
|
|
87
|
+
runtimes.add(shebangRuntime(abs) ?? 'native');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
executablePaths.sort();
|
|
92
|
+
const commit = pkg.source.type === 'git' || pkg.source.type === 'github' ? pkg.source.commit : undefined;
|
|
93
|
+
return {
|
|
94
|
+
name: pkg.name,
|
|
95
|
+
sourceType: pkg.source.type,
|
|
96
|
+
...(commit ? { commit } : {}),
|
|
97
|
+
...(pkg.version ? { version: pkg.version } : {}),
|
|
98
|
+
files,
|
|
99
|
+
directories,
|
|
100
|
+
symlinks,
|
|
101
|
+
bytes,
|
|
102
|
+
executables: executablePaths.slice(0, 32),
|
|
103
|
+
executablesTruncated: executablePaths.length > 32,
|
|
104
|
+
runtimes: [...runtimes].sort(),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
33
107
|
export function installLocalSkill(dir, opts) {
|
|
34
108
|
const sourceDir = resolve(dir);
|
|
35
109
|
const provisional = loadSkillPackage(sourceDir, {
|
|
@@ -288,34 +362,62 @@ function formatGitFailure(args, err) {
|
|
|
288
362
|
// transport ever reached this layer, git itself refuses anything outside the
|
|
289
363
|
// allowlist. GIT_TERMINAL_PROMPT=0 also keeps a private repo from hanging on an
|
|
290
364
|
// interactive credential prompt instead of failing fast.
|
|
291
|
-
function
|
|
365
|
+
function isGithubHttpsUrl(raw) {
|
|
366
|
+
if (!raw)
|
|
367
|
+
return false;
|
|
368
|
+
try {
|
|
369
|
+
const url = new URL(raw);
|
|
370
|
+
return url.protocol === 'https:' && (url.hostname === 'github.com' || url.hostname === 'www.github.com');
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function githubSshUrl(raw) {
|
|
377
|
+
if (!isGithubHttpsUrl(raw))
|
|
378
|
+
return null;
|
|
379
|
+
try {
|
|
380
|
+
const url = new URL(raw);
|
|
381
|
+
const path = url.pathname.replace(/^\/+/, '');
|
|
382
|
+
return path ? `git@github.com:${path}` : null;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function isGitAuthenticationFailure(err) {
|
|
389
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
390
|
+
return /authentication failed|could not read username|terminal prompts disabled|repository not found|unauthor|\b401\b|\b403\b/i.test(message);
|
|
391
|
+
}
|
|
392
|
+
function gitEnv(extraEnv) {
|
|
292
393
|
return {
|
|
293
394
|
...process.env,
|
|
294
395
|
GIT_ALLOW_PROTOCOL: 'https:http:ssh:git:file',
|
|
295
396
|
GIT_TERMINAL_PROMPT: '0',
|
|
397
|
+
...extraEnv,
|
|
296
398
|
};
|
|
297
399
|
}
|
|
298
|
-
function git(args, cwd) {
|
|
400
|
+
function git(args, cwd, extraEnv) {
|
|
299
401
|
try {
|
|
300
402
|
return execFileSync('git', args, {
|
|
301
403
|
cwd,
|
|
302
404
|
encoding: 'utf-8',
|
|
303
405
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
304
406
|
timeout: gitTimeoutMs(),
|
|
305
|
-
env: gitEnv(),
|
|
407
|
+
env: gitEnv(extraEnv),
|
|
306
408
|
}).trim();
|
|
307
409
|
}
|
|
308
410
|
catch (err) {
|
|
309
411
|
throw formatGitFailure(args, err);
|
|
310
412
|
}
|
|
311
413
|
}
|
|
312
|
-
async function gitAsync(args, cwd) {
|
|
414
|
+
async function gitAsync(args, cwd, extraEnv) {
|
|
313
415
|
try {
|
|
314
416
|
const result = await execFileAsync('git', args, {
|
|
315
417
|
cwd,
|
|
316
418
|
encoding: 'utf-8',
|
|
317
419
|
timeout: gitTimeoutMs(),
|
|
318
|
-
env: gitEnv(),
|
|
420
|
+
env: gitEnv(extraEnv),
|
|
319
421
|
});
|
|
320
422
|
return String(result.stdout ?? '').trim();
|
|
321
423
|
}
|
|
@@ -323,16 +425,144 @@ async function gitAsync(args, cwd) {
|
|
|
323
425
|
throw formatGitFailure(args, err);
|
|
324
426
|
}
|
|
325
427
|
}
|
|
428
|
+
function gitErrorMessage(error) {
|
|
429
|
+
return error instanceof Error ? error.message : String(error);
|
|
430
|
+
}
|
|
431
|
+
function gitFallbackFailure(httpsError, anonymousError, sshError) {
|
|
432
|
+
return new Error([
|
|
433
|
+
gitErrorMessage(httpsError),
|
|
434
|
+
...(anonymousError && anonymousError !== httpsError
|
|
435
|
+
? [`anonymous HTTPS retry failed: ${gitErrorMessage(anonymousError)}`]
|
|
436
|
+
: []),
|
|
437
|
+
...(sshError ? [`ssh fallback failed: ${gitErrorMessage(sshError)}`] : []),
|
|
438
|
+
].join('; '));
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* For GitHub HTTPS, try the botmux-resolved token first. If that credential is
|
|
442
|
+
* stale, retry the same operation without botmux's header so public repos and
|
|
443
|
+
* system credential helpers still work, then use SSH only after the anonymous
|
|
444
|
+
* HTTPS attempt also proves to be an authentication failure.
|
|
445
|
+
*/
|
|
446
|
+
function withGithubHttpsFallback(url, attemptHttps, attemptSsh) {
|
|
447
|
+
const sshUrl = githubSshUrl(url);
|
|
448
|
+
const authEnv = sshUrl ? githubGitAuthEnv() : {};
|
|
449
|
+
const hasBotmuxAuth = Object.keys(authEnv).length > 0;
|
|
450
|
+
try {
|
|
451
|
+
return attemptHttps(hasBotmuxAuth ? authEnv : undefined);
|
|
452
|
+
}
|
|
453
|
+
catch (httpsError) {
|
|
454
|
+
if (!sshUrl || !isGitAuthenticationFailure(httpsError))
|
|
455
|
+
throw httpsError;
|
|
456
|
+
let anonymousError = httpsError;
|
|
457
|
+
if (hasBotmuxAuth) {
|
|
458
|
+
try {
|
|
459
|
+
return attemptHttps();
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
anonymousError = error;
|
|
463
|
+
if (!isGitAuthenticationFailure(error)) {
|
|
464
|
+
throw gitFallbackFailure(httpsError, anonymousError);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
return attemptSsh(sshUrl);
|
|
470
|
+
}
|
|
471
|
+
catch (sshError) {
|
|
472
|
+
throw gitFallbackFailure(httpsError, anonymousError, sshError);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async function withGithubHttpsFallbackAsync(url, attemptHttps, attemptSsh) {
|
|
477
|
+
const sshUrl = githubSshUrl(url);
|
|
478
|
+
const authEnv = sshUrl ? githubGitAuthEnv() : {};
|
|
479
|
+
const hasBotmuxAuth = Object.keys(authEnv).length > 0;
|
|
480
|
+
try {
|
|
481
|
+
return await attemptHttps(hasBotmuxAuth ? authEnv : undefined);
|
|
482
|
+
}
|
|
483
|
+
catch (httpsError) {
|
|
484
|
+
if (!sshUrl || !isGitAuthenticationFailure(httpsError))
|
|
485
|
+
throw httpsError;
|
|
486
|
+
let anonymousError = httpsError;
|
|
487
|
+
if (hasBotmuxAuth) {
|
|
488
|
+
try {
|
|
489
|
+
return await attemptHttps();
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
anonymousError = error;
|
|
493
|
+
if (!isGitAuthenticationFailure(error)) {
|
|
494
|
+
throw gitFallbackFailure(httpsError, anonymousError);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
return await attemptSsh(sshUrl);
|
|
500
|
+
}
|
|
501
|
+
catch (sshError) {
|
|
502
|
+
throw gitFallbackFailure(httpsError, anonymousError, sshError);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function cloneGitSource(url, dir) {
|
|
507
|
+
withGithubHttpsFallback(url, authEnv => {
|
|
508
|
+
rmSync(dir, { recursive: true, force: true });
|
|
509
|
+
git(['clone', '--', url, dir], skillSourcesDir(), authEnv);
|
|
510
|
+
}, sshUrl => {
|
|
511
|
+
rmSync(dir, { recursive: true, force: true });
|
|
512
|
+
git(['clone', '--', sshUrl, dir], skillSourcesDir());
|
|
513
|
+
// The cache identity and registry keep the canonical HTTPS source. Future
|
|
514
|
+
// updates re-evaluate HTTPS credentials before falling back to SSH again.
|
|
515
|
+
git(['remote', 'set-url', 'origin', url], dir);
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
async function cloneGitSourceAsync(url, dir) {
|
|
519
|
+
await withGithubHttpsFallbackAsync(url, async (authEnv) => {
|
|
520
|
+
rmSync(dir, { recursive: true, force: true });
|
|
521
|
+
await gitAsync(['clone', '--', url, dir], skillSourcesDir(), authEnv);
|
|
522
|
+
}, async (sshUrl) => {
|
|
523
|
+
rmSync(dir, { recursive: true, force: true });
|
|
524
|
+
await gitAsync(['clone', '--', sshUrl, dir], skillSourcesDir());
|
|
525
|
+
await gitAsync(['remote', 'set-url', 'origin', url], dir);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
function fetchGitSource(url, dir, args) {
|
|
529
|
+
if (isGithubHttpsUrl(url))
|
|
530
|
+
git(['remote', 'set-url', 'origin', url], dir);
|
|
531
|
+
return withGithubHttpsFallback(url, authEnv => git(args, dir, authEnv), sshUrl => {
|
|
532
|
+
git(['remote', 'set-url', 'origin', sshUrl], dir);
|
|
533
|
+
try {
|
|
534
|
+
return git(args, dir);
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
git(['remote', 'set-url', 'origin', url], dir);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async function fetchGitSourceAsync(url, dir, args) {
|
|
542
|
+
if (isGithubHttpsUrl(url))
|
|
543
|
+
await gitAsync(['remote', 'set-url', 'origin', url], dir);
|
|
544
|
+
return withGithubHttpsFallbackAsync(url, authEnv => gitAsync(args, dir, authEnv), async (sshUrl) => {
|
|
545
|
+
await gitAsync(['remote', 'set-url', 'origin', sshUrl], dir);
|
|
546
|
+
try {
|
|
547
|
+
return await gitAsync(args, dir);
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
await gitAsync(['remote', 'set-url', 'origin', url], dir);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
}
|
|
326
554
|
function ensureGitSource(url) {
|
|
327
555
|
assertNoGitUrlCredentials(url);
|
|
328
556
|
assertAllowedGitProtocol(url);
|
|
329
557
|
const dir = join(skillSourcesDir(), sourceId(url));
|
|
330
558
|
mkdirSync(skillSourcesDir(), { recursive: true });
|
|
331
559
|
if (existsSync(join(dir, '.git'))) {
|
|
332
|
-
|
|
560
|
+
fetchGitSource(url, dir, ['fetch', '--tags', '--prune']);
|
|
333
561
|
}
|
|
334
562
|
else {
|
|
335
|
-
|
|
563
|
+
// Never inherit the daemon's cwd for clone: a daemon can outlive the
|
|
564
|
+
// checkout it was launched from, leaving process.cwd() deleted.
|
|
565
|
+
cloneGitSource(url, dir);
|
|
336
566
|
}
|
|
337
567
|
return dir;
|
|
338
568
|
}
|
|
@@ -341,7 +571,7 @@ function checkoutGitSource(url, refValue) {
|
|
|
341
571
|
const sourceDir = ensureGitSource(url);
|
|
342
572
|
const ref = refValue ?? 'HEAD';
|
|
343
573
|
if (ref === 'HEAD') {
|
|
344
|
-
|
|
574
|
+
fetchGitSource(url, sourceDir, ['fetch', 'origin', 'HEAD']);
|
|
345
575
|
git(['checkout', 'FETCH_HEAD'], sourceDir);
|
|
346
576
|
}
|
|
347
577
|
else {
|
|
@@ -355,10 +585,12 @@ async function ensureGitSourceAsync(url) {
|
|
|
355
585
|
const dir = join(skillSourcesDir(), sourceId(url));
|
|
356
586
|
mkdirSync(skillSourcesDir(), { recursive: true });
|
|
357
587
|
if (existsSync(join(dir, '.git'))) {
|
|
358
|
-
await
|
|
588
|
+
await fetchGitSourceAsync(url, dir, ['fetch', '--tags', '--prune']);
|
|
359
589
|
}
|
|
360
590
|
else {
|
|
361
|
-
|
|
591
|
+
// See the synchronous path above: dashboard jobs must also be independent
|
|
592
|
+
// from a stale/deleted daemon launch directory.
|
|
593
|
+
await cloneGitSourceAsync(url, dir);
|
|
362
594
|
}
|
|
363
595
|
return dir;
|
|
364
596
|
}
|
|
@@ -367,7 +599,7 @@ async function checkoutGitSourceAsync(url, refValue) {
|
|
|
367
599
|
const sourceDir = await ensureGitSourceAsync(url);
|
|
368
600
|
const ref = refValue ?? 'HEAD';
|
|
369
601
|
if (ref === 'HEAD') {
|
|
370
|
-
await
|
|
602
|
+
await fetchGitSourceAsync(url, sourceDir, ['fetch', 'origin', 'HEAD']);
|
|
371
603
|
await gitAsync(['checkout', 'FETCH_HEAD'], sourceDir);
|
|
372
604
|
}
|
|
373
605
|
else {
|
|
@@ -375,31 +607,89 @@ async function checkoutGitSourceAsync(url, refValue) {
|
|
|
375
607
|
}
|
|
376
608
|
return { sourceDir, ref, commit: await gitAsync(['rev-parse', 'HEAD'], sourceDir) };
|
|
377
609
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
610
|
+
function discoverCheckedOutGitSource(sourceDir, commit, opts) {
|
|
611
|
+
if (opts.path) {
|
|
612
|
+
const candidate = candidateFromDir(sourceDir, gitSkillDir(sourceDir, opts.path));
|
|
613
|
+
return { commit, skills: candidate ? [candidate] : [] };
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
commit,
|
|
617
|
+
skills: discoverLocalSkillCandidates(sourceDir, { fullDepth: opts.fullDepth }).skills,
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function checkoutTemporaryGitSource(url, refValue) {
|
|
621
|
+
assertNoGitUrlCredentials(url);
|
|
622
|
+
assertAllowedGitProtocol(url);
|
|
623
|
+
assertSafeGitRef(refValue);
|
|
624
|
+
mkdirSync(skillSourcesDir(), { recursive: true });
|
|
625
|
+
const staging = mkdtempSync(join(skillSourcesDir(), '.discover-'));
|
|
626
|
+
const sourceDir = join(staging, 'repo');
|
|
627
|
+
try {
|
|
628
|
+
cloneGitSource(url, sourceDir);
|
|
629
|
+
const ref = refValue ?? 'HEAD';
|
|
630
|
+
if (ref === 'HEAD') {
|
|
631
|
+
fetchGitSource(url, sourceDir, ['fetch', 'origin', 'HEAD']);
|
|
632
|
+
git(['checkout', 'FETCH_HEAD'], sourceDir);
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
git(['checkout', ref], sourceDir);
|
|
384
636
|
}
|
|
385
637
|
return {
|
|
386
|
-
|
|
387
|
-
|
|
638
|
+
sourceDir,
|
|
639
|
+
commit: git(['rev-parse', 'HEAD'], sourceDir),
|
|
640
|
+
cleanup: () => rmSync(staging, { recursive: true, force: true }),
|
|
388
641
|
};
|
|
389
|
-
}
|
|
642
|
+
}
|
|
643
|
+
catch (error) {
|
|
644
|
+
rmSync(staging, { recursive: true, force: true });
|
|
645
|
+
throw error;
|
|
646
|
+
}
|
|
390
647
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
648
|
+
async function checkoutTemporaryGitSourceAsync(url, refValue) {
|
|
649
|
+
assertNoGitUrlCredentials(url);
|
|
650
|
+
assertAllowedGitProtocol(url);
|
|
651
|
+
assertSafeGitRef(refValue);
|
|
652
|
+
mkdirSync(skillSourcesDir(), { recursive: true });
|
|
653
|
+
const staging = mkdtempSync(join(skillSourcesDir(), '.discover-'));
|
|
654
|
+
const sourceDir = join(staging, 'repo');
|
|
655
|
+
try {
|
|
656
|
+
await cloneGitSourceAsync(url, sourceDir);
|
|
657
|
+
const ref = refValue ?? 'HEAD';
|
|
658
|
+
if (ref === 'HEAD') {
|
|
659
|
+
await fetchGitSourceAsync(url, sourceDir, ['fetch', 'origin', 'HEAD']);
|
|
660
|
+
await gitAsync(['checkout', 'FETCH_HEAD'], sourceDir);
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
await gitAsync(['checkout', ref], sourceDir);
|
|
397
664
|
}
|
|
398
665
|
return {
|
|
399
|
-
|
|
400
|
-
|
|
666
|
+
sourceDir,
|
|
667
|
+
commit: await gitAsync(['rev-parse', 'HEAD'], sourceDir),
|
|
668
|
+
cleanup: () => rmSync(staging, { recursive: true, force: true }),
|
|
401
669
|
};
|
|
402
|
-
}
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
rmSync(staging, { recursive: true, force: true });
|
|
673
|
+
throw error;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
export function discoverGitSkillCandidates(opts) {
|
|
677
|
+
const checkout = checkoutTemporaryGitSource(opts.url, opts.ref);
|
|
678
|
+
try {
|
|
679
|
+
return discoverCheckedOutGitSource(checkout.sourceDir, checkout.commit, opts);
|
|
680
|
+
}
|
|
681
|
+
finally {
|
|
682
|
+
checkout.cleanup();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
export async function discoverGitSkillCandidatesAsync(opts) {
|
|
686
|
+
const checkout = await checkoutTemporaryGitSourceAsync(opts.url, opts.ref);
|
|
687
|
+
try {
|
|
688
|
+
return discoverCheckedOutGitSource(checkout.sourceDir, checkout.commit, opts);
|
|
689
|
+
}
|
|
690
|
+
finally {
|
|
691
|
+
checkout.cleanup();
|
|
692
|
+
}
|
|
403
693
|
}
|
|
404
694
|
export function installGitSkill(opts) {
|
|
405
695
|
return withGitSourceLockSync(opts.url, () => installGitSkillLocked(opts));
|
|
@@ -457,28 +747,34 @@ function sourceOverrideForCandidate(source, candidate) {
|
|
|
457
747
|
return source;
|
|
458
748
|
}
|
|
459
749
|
export function installGitSkillsFromSource(opts) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
ref: opts.ref,
|
|
466
|
-
sourceOverride: sourceOverrideForCandidate(opts.sourceOverride, candidate),
|
|
467
|
-
}));
|
|
468
|
-
}
|
|
469
|
-
export async function installGitSkillsFromSourceAsync(opts) {
|
|
470
|
-
const discovery = await discoverGitSkillCandidatesAsync({ url: opts.url, ref: opts.ref, fullDepth: opts.fullDepth });
|
|
471
|
-
const selected = selectDiscoveredSkills(discovery.skills, opts);
|
|
472
|
-
const installed = [];
|
|
473
|
-
for (const candidate of selected) {
|
|
474
|
-
installed.push(await installGitSkillAsync({
|
|
750
|
+
return withGitSourceLockSync(opts.url, () => {
|
|
751
|
+
const checkout = checkoutGitSource(opts.url, opts.ref);
|
|
752
|
+
const discovery = discoverCheckedOutGitSource(checkout.sourceDir, checkout.commit, { fullDepth: opts.fullDepth });
|
|
753
|
+
const selected = selectDiscoveredSkills(discovery.skills, opts);
|
|
754
|
+
return selected.map(candidate => installGitSkillLocked({
|
|
475
755
|
url: opts.url,
|
|
476
756
|
path: candidate.path,
|
|
477
757
|
ref: opts.ref,
|
|
478
758
|
sourceOverride: sourceOverrideForCandidate(opts.sourceOverride, candidate),
|
|
479
759
|
}));
|
|
480
|
-
}
|
|
481
|
-
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
export async function installGitSkillsFromSourceAsync(opts) {
|
|
763
|
+
return withGitSourceLock(opts.url, async () => {
|
|
764
|
+
const checkout = await checkoutGitSourceAsync(opts.url, opts.ref);
|
|
765
|
+
const discovery = discoverCheckedOutGitSource(checkout.sourceDir, checkout.commit, { fullDepth: opts.fullDepth });
|
|
766
|
+
const selected = selectDiscoveredSkills(discovery.skills, opts);
|
|
767
|
+
const installed = [];
|
|
768
|
+
for (const candidate of selected) {
|
|
769
|
+
installed.push(await installGitSkillAsyncLocked({
|
|
770
|
+
url: opts.url,
|
|
771
|
+
path: candidate.path,
|
|
772
|
+
ref: opts.ref,
|
|
773
|
+
sourceOverride: sourceOverrideForCandidate(opts.sourceOverride, candidate),
|
|
774
|
+
}));
|
|
775
|
+
}
|
|
776
|
+
return installed;
|
|
777
|
+
});
|
|
482
778
|
}
|
|
483
779
|
// --- agentbuddy (external CLI) skill source ---------------------------------
|
|
484
780
|
//
|