termify-agent 1.0.48 → 1.0.49
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/mcp/nanobanana-mcp-bundle.mjs +1415 -1339
- package/mcp/termify-mcp-bundle.mjs +26 -2
- package/package.json +1 -1
- package/scripts/postinstall.js +92 -27
- package/scripts/postinstall.test.mjs +96 -0
|
@@ -21474,6 +21474,29 @@ function loadConfigFile() {
|
|
|
21474
21474
|
}
|
|
21475
21475
|
}
|
|
21476
21476
|
var _fileConfig = loadConfigFile();
|
|
21477
|
+
function resolveConfig(config2) {
|
|
21478
|
+
if (config2.token) {
|
|
21479
|
+
return {
|
|
21480
|
+
token: config2.token,
|
|
21481
|
+
apiUrl: config2.apiUrl || "http://localhost:3001"
|
|
21482
|
+
};
|
|
21483
|
+
}
|
|
21484
|
+
if (!Array.isArray(config2.endpoints) || config2.endpoints.length === 0) {
|
|
21485
|
+
return null;
|
|
21486
|
+
}
|
|
21487
|
+
const validEndpoints = config2.endpoints.filter((endpoint2) => {
|
|
21488
|
+
return Boolean(endpoint2?.apiUrl && endpoint2?.token);
|
|
21489
|
+
});
|
|
21490
|
+
if (validEndpoints.length === 0) {
|
|
21491
|
+
return null;
|
|
21492
|
+
}
|
|
21493
|
+
const activeEndpoint = config2.syncEndpoint ? validEndpoints.find((endpoint2) => endpoint2.name === config2.syncEndpoint) : void 0;
|
|
21494
|
+
const endpoint = activeEndpoint || validEndpoints[0];
|
|
21495
|
+
return {
|
|
21496
|
+
token: endpoint.token,
|
|
21497
|
+
apiUrl: endpoint.apiUrl
|
|
21498
|
+
};
|
|
21499
|
+
}
|
|
21477
21500
|
var TERMIFY_STATUS_URL = process.env.TERMIFY_STATUS_URL || "";
|
|
21478
21501
|
var TERMINAL_ID = process.env.TERMIFY_TERMINAL_ID || "";
|
|
21479
21502
|
var SESSION_ID = process.env.TERMIFY_SESSION_ID || "";
|
|
@@ -21481,10 +21504,11 @@ var _api = null;
|
|
|
21481
21504
|
function getApi() {
|
|
21482
21505
|
if (_api)
|
|
21483
21506
|
return _api;
|
|
21484
|
-
const
|
|
21507
|
+
const resolvedConfig = resolveConfig(_fileConfig);
|
|
21508
|
+
const token = process.env.TERMIFY_TOKEN || process.env.TERMIFY_API_KEY || resolvedConfig?.token || "";
|
|
21485
21509
|
if (!token)
|
|
21486
21510
|
return null;
|
|
21487
|
-
const url = process.env.TERMIFY_API_URL ||
|
|
21511
|
+
const url = process.env.TERMIFY_API_URL || resolvedConfig?.apiUrl || "http://localhost:3001";
|
|
21488
21512
|
_api = new TermifyApiService(url, token);
|
|
21489
21513
|
return _api;
|
|
21490
21514
|
}
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* `termify-agent start` for faster npm install times. See src/setup.ts.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { execSync
|
|
18
|
-
import { createWriteStream, mkdirSync, chmodSync, existsSync, unlinkSync, copyFileSync, rmSync, readFileSync, writeFileSync, symlinkSync, lstatSync } from 'fs';
|
|
19
|
-
import { join, dirname } from 'path';
|
|
17
|
+
import { execSync } from 'child_process';
|
|
18
|
+
import { createWriteStream, mkdirSync, chmodSync, existsSync, unlinkSync, copyFileSync, rmSync, readFileSync, readlinkSync, writeFileSync, symlinkSync, lstatSync } from 'fs';
|
|
19
|
+
import { isAbsolute, join, dirname, resolve } from 'path';
|
|
20
20
|
import { homedir, platform, arch } from 'os';
|
|
21
21
|
import { get } from 'https';
|
|
22
22
|
import { fileURLToPath } from 'url';
|
|
@@ -422,37 +422,100 @@ function downloadFile(url, dest, redirects = 0) {
|
|
|
422
422
|
/**
|
|
423
423
|
* Create /usr/local/bin/termify-agent so `sudo termify-agent` works.
|
|
424
424
|
*/
|
|
425
|
-
function
|
|
426
|
-
|
|
425
|
+
function resolveCurrentPackageBinPath() {
|
|
426
|
+
const packagedBinPath = join(__dirname, '..', 'bin', 'termify-agent.js');
|
|
427
|
+
return existsSync(packagedBinPath) ? packagedBinPath : null;
|
|
428
|
+
}
|
|
427
429
|
|
|
428
|
-
|
|
430
|
+
function normalizeLinkTarget(target, linkTarget) {
|
|
431
|
+
return isAbsolute(linkTarget) ? linkTarget : resolve(dirname(target), linkTarget);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function isLegacyWrapperLink(linkTarget) {
|
|
435
|
+
return linkTarget.includes('/.termify/agent/');
|
|
436
|
+
}
|
|
429
437
|
|
|
438
|
+
function isLegacyWrapperFile(target) {
|
|
430
439
|
try {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
return;
|
|
440
|
+
const contents = readFileSync(target, 'utf8');
|
|
441
|
+
return contents.includes('TERMIFY_DIR=') && contents.includes('/agent/bin/termify-agent.js');
|
|
434
442
|
} catch {
|
|
435
|
-
|
|
443
|
+
return false;
|
|
436
444
|
}
|
|
445
|
+
}
|
|
437
446
|
|
|
438
|
-
|
|
447
|
+
export function detectGlobalWrapperState(target, desiredPath) {
|
|
439
448
|
try {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
449
|
+
const stat = lstatSync(target);
|
|
450
|
+
|
|
451
|
+
if (stat.isSymbolicLink()) {
|
|
452
|
+
const linkTarget = readlinkSync(target);
|
|
453
|
+
const normalizedTarget = normalizeLinkTarget(target, linkTarget);
|
|
454
|
+
|
|
455
|
+
if (desiredPath && normalizedTarget === desiredPath) {
|
|
456
|
+
return 'ready';
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (isLegacyWrapperLink(linkTarget) || isLegacyWrapperLink(normalizedTarget)) {
|
|
460
|
+
return 'legacy';
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return 'conflict';
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (stat.isFile() && isLegacyWrapperFile(target)) {
|
|
467
|
+
return 'legacy';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return 'conflict';
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
|
|
473
|
+
return 'missing';
|
|
446
474
|
}
|
|
475
|
+
|
|
476
|
+
throw error;
|
|
447
477
|
}
|
|
478
|
+
}
|
|
448
479
|
|
|
449
|
-
|
|
480
|
+
export function ensureGlobalSymlink(options = {}) {
|
|
481
|
+
if (IS_WIN) {
|
|
482
|
+
return { status: 'skipped', action: 'windows' };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const target = options.target || '/usr/local/bin/termify-agent';
|
|
486
|
+
const log = options.log || console.log;
|
|
487
|
+
const desiredPath = options.desiredPath || resolveCurrentPackageBinPath();
|
|
488
|
+
|
|
489
|
+
if (!desiredPath || !existsSync(desiredPath)) {
|
|
490
|
+
log('[termify-agent] Could not locate termify-agent binary for symlink.');
|
|
491
|
+
return { status: 'skipped', action: 'missing_bin' };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const wrapperState = detectGlobalWrapperState(target, desiredPath);
|
|
495
|
+
if (wrapperState === 'ready') {
|
|
496
|
+
log('[termify-agent] /usr/local/bin/termify-agent already points to the current npm install.');
|
|
497
|
+
return { status: 'ready', action: 'already_ready' };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (wrapperState === 'conflict') {
|
|
501
|
+
log('[termify-agent] /usr/local/bin/termify-agent already exists, skipping.');
|
|
502
|
+
return { status: 'skipped', action: 'conflict' };
|
|
503
|
+
}
|
|
450
504
|
|
|
451
505
|
try {
|
|
452
|
-
|
|
453
|
-
|
|
506
|
+
if (wrapperState === 'legacy') {
|
|
507
|
+
unlinkSync(target);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
symlinkSync(desiredPath, target);
|
|
511
|
+
log(`[termify-agent] Symlink ${wrapperState === 'legacy' ? 'updated' : 'created'}: ${target} \u2192 ${desiredPath}`);
|
|
512
|
+
return {
|
|
513
|
+
status: 'ready',
|
|
514
|
+
action: wrapperState === 'legacy' ? 'replaced_legacy' : 'created',
|
|
515
|
+
};
|
|
454
516
|
} catch {
|
|
455
|
-
|
|
517
|
+
log(`[termify-agent] Tip: run 'sudo ln -sf "${desiredPath}" ${target}' to enable sudo access`);
|
|
518
|
+
return { status: 'skipped', action: 'permission_denied' };
|
|
456
519
|
}
|
|
457
520
|
}
|
|
458
521
|
|
|
@@ -522,8 +585,8 @@ async function main() {
|
|
|
522
585
|
// Step 5: Global symlink
|
|
523
586
|
const s3 = createSpinner('Creating global symlink...');
|
|
524
587
|
try {
|
|
525
|
-
ensureGlobalSymlink();
|
|
526
|
-
if (
|
|
588
|
+
const symlinkResult = ensureGlobalSymlink();
|
|
589
|
+
if (symlinkResult?.status === 'ready') {
|
|
527
590
|
s3.succeed('Global symlink ready');
|
|
528
591
|
} else {
|
|
529
592
|
s3.warn(`Symlink skipped ${DIM}(run: sudo ln -sf $(which termify-agent) /usr/local/bin/)${RESET}`);
|
|
@@ -539,7 +602,9 @@ async function main() {
|
|
|
539
602
|
console.log('');
|
|
540
603
|
}
|
|
541
604
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
605
|
+
if (process.argv[1] && resolve(process.argv[1]) === __filename) {
|
|
606
|
+
main().catch((err) => {
|
|
607
|
+
console.warn(`\n ${WARN} Postinstall warning: ${err.message}\n`);
|
|
608
|
+
process.exit(0);
|
|
609
|
+
});
|
|
610
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdirSync, lstatSync, mkdtempSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { detectGlobalWrapperState, ensureGlobalSymlink } from './postinstall.js';
|
|
8
|
+
|
|
9
|
+
function makeTempDir() {
|
|
10
|
+
return mkdtempSync(join(tmpdir(), 'termify-agent-postinstall-'));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
test('ensureGlobalSymlink replaces the legacy curl wrapper with the npm package bin', (t) => {
|
|
14
|
+
const tempDir = makeTempDir();
|
|
15
|
+
const target = join(tempDir, 'usr-local-bin-termify-agent');
|
|
16
|
+
const desiredDir = join(tempDir, 'package', 'bin');
|
|
17
|
+
const desiredPath = join(desiredDir, 'termify-agent.js');
|
|
18
|
+
|
|
19
|
+
t.after(() => {
|
|
20
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
mkdirSync(desiredDir, { recursive: true });
|
|
24
|
+
writeFileSync(desiredPath, '#!/usr/bin/env node\n');
|
|
25
|
+
writeFileSync(
|
|
26
|
+
target,
|
|
27
|
+
[
|
|
28
|
+
'#!/bin/sh',
|
|
29
|
+
'TERMIFY_DIR="$HOME/.termify"',
|
|
30
|
+
'NODE_BIN="$TERMIFY_DIR/node/bin/node"',
|
|
31
|
+
'exec "$NODE_BIN" "$TERMIFY_DIR/agent/bin/termify-agent.js" "$@"',
|
|
32
|
+
'',
|
|
33
|
+
].join('\n')
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
assert.equal(detectGlobalWrapperState(target, desiredPath), 'legacy');
|
|
37
|
+
|
|
38
|
+
const result = ensureGlobalSymlink({
|
|
39
|
+
target,
|
|
40
|
+
desiredPath,
|
|
41
|
+
log: () => {},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
assert.equal(result.status, 'ready');
|
|
45
|
+
assert.equal(result.action, 'replaced_legacy');
|
|
46
|
+
assert.equal(lstatSync(target).isSymbolicLink(), true);
|
|
47
|
+
assert.equal(resolve(dirname(target), readlinkSync(target)), desiredPath);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('ensureGlobalSymlink does not overwrite a non-legacy existing target', (t) => {
|
|
51
|
+
const tempDir = makeTempDir();
|
|
52
|
+
const target = join(tempDir, 'usr-local-bin-termify-agent');
|
|
53
|
+
const desiredDir = join(tempDir, 'package', 'bin');
|
|
54
|
+
const desiredPath = join(desiredDir, 'termify-agent.js');
|
|
55
|
+
const customPath = join(tempDir, 'custom', 'termify-agent.js');
|
|
56
|
+
|
|
57
|
+
t.after(() => {
|
|
58
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
mkdirSync(desiredDir, { recursive: true });
|
|
62
|
+
mkdirSync(dirname(customPath), { recursive: true });
|
|
63
|
+
writeFileSync(desiredPath, '#!/usr/bin/env node\n');
|
|
64
|
+
writeFileSync(customPath, '#!/usr/bin/env node\n');
|
|
65
|
+
symlinkSync(customPath, target);
|
|
66
|
+
|
|
67
|
+
assert.equal(detectGlobalWrapperState(target, desiredPath), 'conflict');
|
|
68
|
+
|
|
69
|
+
const result = ensureGlobalSymlink({
|
|
70
|
+
target,
|
|
71
|
+
desiredPath,
|
|
72
|
+
log: () => {},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
assert.equal(result.status, 'skipped');
|
|
76
|
+
assert.equal(result.action, 'conflict');
|
|
77
|
+
assert.equal(resolve(dirname(target), readlinkSync(target)), customPath);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('detectGlobalWrapperState treats an up-to-date symlink as ready', (t) => {
|
|
81
|
+
const tempDir = makeTempDir();
|
|
82
|
+
const target = join(tempDir, 'usr-local-bin-termify-agent');
|
|
83
|
+
const desiredDir = join(tempDir, 'package', 'bin');
|
|
84
|
+
const desiredPath = join(desiredDir, 'termify-agent.js');
|
|
85
|
+
|
|
86
|
+
t.after(() => {
|
|
87
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
mkdirSync(desiredDir, { recursive: true });
|
|
91
|
+
writeFileSync(desiredPath, '#!/usr/bin/env node\n');
|
|
92
|
+
symlinkSync(desiredPath, target);
|
|
93
|
+
|
|
94
|
+
assert.equal(detectGlobalWrapperState(target, desiredPath), 'ready');
|
|
95
|
+
assert.equal(readFileSync(desiredPath, 'utf8'), '#!/usr/bin/env node\n');
|
|
96
|
+
});
|