newmark-agent 0.4.5 → 0.4.7
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/assets/app-icon-dark.svg +6 -0
- package/dist/assets/app-icon-dark.svg +6 -0
- package/dist/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +90 -7
- package/dist/cli-discovery.js +10 -0
- package/dist/conversation-utility-host.bundle.cjs +293 -148
- package/dist/core/agent.d.ts +38 -4
- package/dist/core/agent.js +231 -49
- package/dist/core/agentKernelRunner.d.ts +3 -0
- package/dist/core/agentKernelRunner.js +49 -83
- package/dist/core/config.js +3 -0
- package/dist/core/dshCompatibility.d.ts +23 -6
- package/dist/core/dshCompatibility.js +99 -1
- package/dist/core/installUpdate.d.ts +67 -0
- package/dist/core/installUpdate.js +268 -0
- package/dist/core/mobilePairing.d.ts +47 -0
- package/dist/core/mobilePairing.js +221 -0
- package/dist/core/subagent.d.ts +10 -3
- package/dist/core/subagent.js +23 -8
- package/dist/core/toolPolicy.js +11 -3
- package/dist/launcher.js +14 -11
- package/dist/main.js +64 -3
- package/dist/preload.js +5 -0
- package/dist/providers/chat-completions.adapter.js +6 -2
- package/dist/providers/responses.adapter.js +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +721 -3
- package/dist/toolchain/registry-seeder.js +3 -1
- package/dist/tools/index.js +11 -5
- package/dist/tools/nativeTools.js +1 -1
- package/dist/tui/src/adapters/core-runtime-adapter.js +17 -1
- package/dist/tui/src/app.js +41 -0
- package/dist/tui/src/data.js +1 -0
- package/dist/tui/src/render.js +11 -0
- package/dist/tui/src/settings-schema.js +3 -1
- package/dist/tui/src/state.js +21 -1
- package/dist/ui/index.html +530 -101
- package/dist/ui/lucide-sprite.svg +10 -0
- package/dist/wsl-agent-host.bundle.cjs +293 -148
- package/package.json +14 -8
|
@@ -39,6 +39,15 @@ exports.normalizeReleaseVersion = normalizeReleaseVersion;
|
|
|
39
39
|
exports.compareSemver = compareSemver;
|
|
40
40
|
exports.checkGitHubUpdate = checkGitHubUpdate;
|
|
41
41
|
exports.applyGitHubUpdate = applyGitHubUpdate;
|
|
42
|
+
exports.listRunningNewmarkProcesses = listRunningNewmarkProcesses;
|
|
43
|
+
exports.stopNewmarkProcesses = stopNewmarkProcesses;
|
|
44
|
+
exports.listInstalledNewmarkProducts = listInstalledNewmarkProducts;
|
|
45
|
+
exports.uninstallNewmarkProduct = uninstallNewmarkProduct;
|
|
46
|
+
exports.installMsiPackage = installMsiPackage;
|
|
47
|
+
exports.findLegacyNewmarkExecutables = findLegacyNewmarkExecutables;
|
|
48
|
+
exports.removeLegacyNewmarkExecutables = removeLegacyNewmarkExecutables;
|
|
49
|
+
exports.planManagedMsiInstall = planManagedMsiInstall;
|
|
50
|
+
exports.executeManagedMsiInstall = executeManagedMsiInstall;
|
|
42
51
|
const fs = __importStar(require("fs"));
|
|
43
52
|
const path = __importStar(require("path"));
|
|
44
53
|
const os = __importStar(require("os"));
|
|
@@ -518,4 +527,263 @@ async function applyGitHubUpdate(options) {
|
|
|
518
527
|
};
|
|
519
528
|
}
|
|
520
529
|
}
|
|
530
|
+
const NEWMARK_PROCESS_NAMES = ['Newmark Agent.exe', 'Newmark.exe', 'Newmark Console Runtime.exe'];
|
|
531
|
+
const NEWMARK_LEGACY_EXECUTABLES = new Set(['newmark.exe', 'newmark agent.exe']);
|
|
532
|
+
function normalizeWindowsPathForCompare(value) {
|
|
533
|
+
return path.resolve(String(value)).toLowerCase();
|
|
534
|
+
}
|
|
535
|
+
function isWithinOrEqual(candidate, parent) {
|
|
536
|
+
const child = normalizeWindowsPathForCompare(candidate);
|
|
537
|
+
const rootPath = normalizeWindowsPathForCompare(parent);
|
|
538
|
+
return child === rootPath || child.startsWith(rootPath.endsWith(path.sep) ? rootPath : rootPath + path.sep);
|
|
539
|
+
}
|
|
540
|
+
function runPowerShellJson(script) {
|
|
541
|
+
const args = ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script];
|
|
542
|
+
const stdout = (0, child_process_1.execFileSync)('powershell.exe', args, { encoding: 'utf-8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
543
|
+
const trimmed = String(stdout || '').replace(/^\uFEFF/, '').trim();
|
|
544
|
+
if (!trimmed)
|
|
545
|
+
return [];
|
|
546
|
+
const parsed = JSON.parse(trimmed);
|
|
547
|
+
return Array.isArray(parsed) ? parsed.map(item => item) : [parsed];
|
|
548
|
+
}
|
|
549
|
+
function listRunningNewmarkProcesses() {
|
|
550
|
+
const nameFilter = NEWMARK_PROCESS_NAMES.map(name => `$_.Name -eq '${name.replace(/'/g, "''")}'`).join(' -or ');
|
|
551
|
+
const rows = runPowerShellJson(`Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object { ${nameFilter} } | Select-Object ProcessId,Name,ExecutablePath | ConvertTo-Json -Compress`);
|
|
552
|
+
const skipPid = Number(process.env.NEWMARK_SKIP_PROCESS_PID || process.pid);
|
|
553
|
+
return rows
|
|
554
|
+
.map(row => ({
|
|
555
|
+
pid: Number(row.ProcessId || 0),
|
|
556
|
+
name: String(row.Name || ''),
|
|
557
|
+
executablePath: String(row.ExecutablePath || ''),
|
|
558
|
+
}))
|
|
559
|
+
.filter(proc => proc.pid > 0 && proc.pid !== skipPid);
|
|
560
|
+
}
|
|
561
|
+
function stopNewmarkProcesses(pids) {
|
|
562
|
+
const ids = Array.from(new Set((pids || []).map(Number).filter(pid => Number.isFinite(pid) && pid > 0)));
|
|
563
|
+
if (!ids.length)
|
|
564
|
+
return { stopped: [], errors: [] };
|
|
565
|
+
const script = `$ids = @(${ids.join(',')}); foreach ($id in $ids) { try { Stop-Process -Id $id -Force -ErrorAction Stop; Write-Output ('stopped:' + $id) } catch { Write-Output ('error:' + $id + ':' + $_.Exception.Message) } }`;
|
|
566
|
+
const args = ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script];
|
|
567
|
+
const stdout = String((0, child_process_1.execFileSync)('powershell.exe', args, { encoding: 'utf-8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }) || '');
|
|
568
|
+
const stopped = [];
|
|
569
|
+
const errors = [];
|
|
570
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
571
|
+
const trimmed = line.trim();
|
|
572
|
+
if (!trimmed)
|
|
573
|
+
continue;
|
|
574
|
+
const stoppedMatch = trimmed.match(/^stopped:(\d+)$/);
|
|
575
|
+
const errorMatch = trimmed.match(/^error:(\d+):(.*)$/);
|
|
576
|
+
if (stoppedMatch)
|
|
577
|
+
stopped.push(Number(stoppedMatch[1]));
|
|
578
|
+
else if (errorMatch)
|
|
579
|
+
errors.push(`PID ${errorMatch[1]}: ${errorMatch[2]}`);
|
|
580
|
+
}
|
|
581
|
+
return { stopped, errors };
|
|
582
|
+
}
|
|
583
|
+
function listInstalledNewmarkProducts() {
|
|
584
|
+
const rows = runPowerShellJson(`$paths = @('HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*','HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*'); Get-ItemProperty $paths -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like '*Newmark Agent*' } | Select-Object PSChildName,DisplayName,InstallLocation,UninstallString,QuietUninstallString | ConvertTo-Json -Compress`);
|
|
585
|
+
return rows
|
|
586
|
+
.map(row => ({
|
|
587
|
+
productCode: String(row.PSChildName || ''),
|
|
588
|
+
displayName: String(row.DisplayName || ''),
|
|
589
|
+
installLocation: String(row.InstallLocation || ''),
|
|
590
|
+
uninstallString: String(row.UninstallString || row.QuietUninstallString || ''),
|
|
591
|
+
}))
|
|
592
|
+
.filter(product => product.productCode);
|
|
593
|
+
}
|
|
594
|
+
function runMsiExec(args) {
|
|
595
|
+
const result = (0, child_process_1.spawnSync)('msiexec.exe', args, { encoding: 'utf-8', windowsHide: true });
|
|
596
|
+
return {
|
|
597
|
+
exitCode: result.status === null ? -1 : result.status,
|
|
598
|
+
stdout: String(result.stdout || ''),
|
|
599
|
+
stderr: String(result.stderr || ''),
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function windowsCommandQuote(value) {
|
|
603
|
+
const text = String(value);
|
|
604
|
+
return /\s/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
|
|
605
|
+
}
|
|
606
|
+
function runElevatedMsiExec(args) {
|
|
607
|
+
const argumentString = args.map(arg => windowsCommandQuote(arg)).join(' ');
|
|
608
|
+
const script = [
|
|
609
|
+
`$argumentList = '${argumentString.replace(/'/g, "''")}'`,
|
|
610
|
+
'$process = Start-Process -FilePath "msiexec.exe" -ArgumentList $argumentList -Verb RunAs -Wait -PassThru',
|
|
611
|
+
'Write-Output ("EXITCODE:" + $process.ExitCode)',
|
|
612
|
+
].join('; ');
|
|
613
|
+
const result = (0, child_process_1.spawnSync)('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], { encoding: 'utf-8', windowsHide: true });
|
|
614
|
+
const stdout = String(result.stdout || '');
|
|
615
|
+
const match = stdout.match(/EXITCODE:(\d+)/);
|
|
616
|
+
return {
|
|
617
|
+
exitCode: match ? Number(match[1]) : (result.status === null ? -1 : result.status),
|
|
618
|
+
stdout,
|
|
619
|
+
stderr: String(result.stderr || ''),
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function uninstallNewmarkProduct(productCode, logPath) {
|
|
623
|
+
const args = ['/x', productCode, '/qn', '/norestart', '/l*v', logPath];
|
|
624
|
+
let result = runMsiExec(args);
|
|
625
|
+
if (result.exitCode !== 0 && result.exitCode !== 3010)
|
|
626
|
+
result = runElevatedMsiExec(args);
|
|
627
|
+
const ok = result.exitCode === 0 || result.exitCode === 3010;
|
|
628
|
+
return {
|
|
629
|
+
ok,
|
|
630
|
+
exitCode: result.exitCode,
|
|
631
|
+
logPath,
|
|
632
|
+
error: ok ? undefined : `msiexec uninstall exited ${result.exitCode}`,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function installMsiPackage(msiPath, options = {}) {
|
|
636
|
+
const logPath = path.join(options.logDir || os.tmpdir(), `newmark-msi-install-${process.pid}-${Date.now()}.log`);
|
|
637
|
+
const args = ['/i', path.resolve(msiPath), '/qn', '/norestart', '/l*v', logPath];
|
|
638
|
+
let result = runMsiExec(args);
|
|
639
|
+
if (result.exitCode !== 0 && result.exitCode !== 3010 && options.allowElevate !== false)
|
|
640
|
+
result = runElevatedMsiExec(args);
|
|
641
|
+
const ok = result.exitCode === 0 || result.exitCode === 3010;
|
|
642
|
+
return {
|
|
643
|
+
ok,
|
|
644
|
+
exitCode: result.exitCode,
|
|
645
|
+
logPath,
|
|
646
|
+
error: ok ? undefined : `msiexec install exited ${result.exitCode}`,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function findLegacyNewmarkExecutables(excludeRoots = []) {
|
|
650
|
+
const found = new Map();
|
|
651
|
+
for (const name of NEWMARK_PROCESS_NAMES) {
|
|
652
|
+
try {
|
|
653
|
+
const stdout = String((0, child_process_1.execFileSync)('where.exe', [name], { encoding: 'utf-8', windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }) || '');
|
|
654
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
655
|
+
const candidate = String(line || '').trim();
|
|
656
|
+
if (!candidate)
|
|
657
|
+
continue;
|
|
658
|
+
const key = normalizeWindowsPathForCompare(candidate);
|
|
659
|
+
if (NEWMARK_LEGACY_EXECUTABLES.has(path.basename(candidate).toLowerCase()) && !found.has(key))
|
|
660
|
+
found.set(key, candidate);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
// where.exe returns non-zero when an executable is not found on PATH.
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
const exclude = (excludeRoots || []).filter(Boolean).map(root => normalizeWindowsPathForCompare(root));
|
|
668
|
+
return Array.from(found.values())
|
|
669
|
+
.filter(candidate => !exclude.some(root => isWithinOrEqual(candidate, root)))
|
|
670
|
+
.sort();
|
|
671
|
+
}
|
|
672
|
+
function removeLegacyNewmarkExecutables(paths) {
|
|
673
|
+
const removed = [];
|
|
674
|
+
const errors = [];
|
|
675
|
+
for (const candidate of Array.from(new Set(paths || []))) {
|
|
676
|
+
const full = path.resolve(candidate);
|
|
677
|
+
if (!NEWMARK_LEGACY_EXECUTABLES.has(path.basename(full).toLowerCase())) {
|
|
678
|
+
errors.push(`refusing non-Newmark executable: ${full}`);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
fs.unlinkSync(full);
|
|
683
|
+
removed.push(full);
|
|
684
|
+
}
|
|
685
|
+
catch (e) {
|
|
686
|
+
errors.push(`${full}: ${e instanceof Error ? e.message : String(e)}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { removed, errors };
|
|
690
|
+
}
|
|
691
|
+
function planManagedMsiInstall(msiPath, options = {}) {
|
|
692
|
+
const fullPath = path.resolve(msiPath);
|
|
693
|
+
try {
|
|
694
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile() || path.extname(fullPath).toLowerCase() !== '.msi') {
|
|
695
|
+
return {
|
|
696
|
+
ok: false,
|
|
697
|
+
msiPath: fullPath,
|
|
698
|
+
runningProcesses: [],
|
|
699
|
+
installedProducts: [],
|
|
700
|
+
legacyExecutables: [],
|
|
701
|
+
needsStopConfirmation: false,
|
|
702
|
+
needsLegacyRemovalConfirmation: false,
|
|
703
|
+
error: `MSI package does not exist or is not a .msi file: ${fullPath}`,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const runningProcesses = listRunningNewmarkProcesses();
|
|
707
|
+
const installedProducts = listInstalledNewmarkProducts();
|
|
708
|
+
const excludeRoots = [
|
|
709
|
+
...(options.excludeRoots || []),
|
|
710
|
+
process.cwd(),
|
|
711
|
+
path.dirname(process.execPath || ''),
|
|
712
|
+
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Newmark Agent'),
|
|
713
|
+
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Newmark Agent'),
|
|
714
|
+
...installedProducts.map(product => product.installLocation),
|
|
715
|
+
];
|
|
716
|
+
const legacyExecutables = findLegacyNewmarkExecutables(excludeRoots);
|
|
717
|
+
return {
|
|
718
|
+
ok: true,
|
|
719
|
+
msiPath: fullPath,
|
|
720
|
+
runningProcesses,
|
|
721
|
+
installedProducts,
|
|
722
|
+
legacyExecutables,
|
|
723
|
+
needsStopConfirmation: runningProcesses.length > 0 && options.stopConfirmed !== true,
|
|
724
|
+
needsLegacyRemovalConfirmation: legacyExecutables.length > 0 && options.removeLegacyConfirmed !== true,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
catch (e) {
|
|
728
|
+
return {
|
|
729
|
+
ok: false,
|
|
730
|
+
msiPath: fullPath,
|
|
731
|
+
runningProcesses: [],
|
|
732
|
+
installedProducts: [],
|
|
733
|
+
legacyExecutables: [],
|
|
734
|
+
needsStopConfirmation: false,
|
|
735
|
+
needsLegacyRemovalConfirmation: false,
|
|
736
|
+
error: e instanceof Error ? e.message : String(e),
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function executeManagedMsiInstall(msiPath, options = {}) {
|
|
741
|
+
const plan = planManagedMsiInstall(msiPath, options);
|
|
742
|
+
if (!plan.ok) {
|
|
743
|
+
return { ok: false, plan, stopped: [], uninstalled: [], removedLegacy: [], error: plan.error };
|
|
744
|
+
}
|
|
745
|
+
if (plan.needsStopConfirmation) {
|
|
746
|
+
return { ok: false, plan, stopped: [], uninstalled: [], removedLegacy: [], error: 'Running Newmark processes require confirmation before they can be stopped.' };
|
|
747
|
+
}
|
|
748
|
+
if (plan.needsLegacyRemovalConfirmation) {
|
|
749
|
+
return { ok: false, plan, stopped: [], uninstalled: [], removedLegacy: [], error: 'Legacy Newmark executables require confirmation before they can be removed.' };
|
|
750
|
+
}
|
|
751
|
+
const stopResult = plan.runningProcesses.length
|
|
752
|
+
? stopNewmarkProcesses(plan.runningProcesses.map(process => process.pid))
|
|
753
|
+
: { stopped: [], errors: [] };
|
|
754
|
+
const uninstalled = [];
|
|
755
|
+
if (options.uninstallPrevious !== false) {
|
|
756
|
+
for (const product of plan.installedProducts) {
|
|
757
|
+
const logPath = path.join(options.logDir || os.tmpdir(), `newmark-msi-uninstall-${product.productCode}-${process.pid}-${Date.now()}.log`);
|
|
758
|
+
const uninstallResult = uninstallNewmarkProduct(product.productCode, logPath);
|
|
759
|
+
if (!uninstallResult.ok) {
|
|
760
|
+
return {
|
|
761
|
+
ok: false,
|
|
762
|
+
plan,
|
|
763
|
+
stopped: stopResult.stopped,
|
|
764
|
+
uninstalled,
|
|
765
|
+
removedLegacy: [],
|
|
766
|
+
exitCode: uninstallResult.exitCode,
|
|
767
|
+
logPath: uninstallResult.logPath,
|
|
768
|
+
error: `Failed to uninstall previous Newmark version ${product.productCode}: ${uninstallResult.error}`,
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
uninstalled.push(product.productCode);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
const removeResult = plan.legacyExecutables.length
|
|
775
|
+
? removeLegacyNewmarkExecutables(plan.legacyExecutables)
|
|
776
|
+
: { removed: [], errors: [] };
|
|
777
|
+
const installResult = installMsiPackage(plan.msiPath, { logDir: options.logDir, allowElevate: options.allowElevate });
|
|
778
|
+
return {
|
|
779
|
+
ok: installResult.ok,
|
|
780
|
+
plan,
|
|
781
|
+
stopped: stopResult.stopped,
|
|
782
|
+
uninstalled,
|
|
783
|
+
removedLegacy: removeResult.removed,
|
|
784
|
+
exitCode: installResult.exitCode,
|
|
785
|
+
logPath: installResult.logPath,
|
|
786
|
+
error: installResult.error,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
521
789
|
//# sourceMappingURL=installUpdate.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export declare const MOBILE_TOKEN_FILENAME = ".newmark-mobile-token";
|
|
2
|
+
export declare const MOBILE_PAIRING_FILENAME = ".newmark-mobile-pairing.json";
|
|
3
|
+
export declare const MOBILE_PORT = 47890;
|
|
4
|
+
export declare const MOBILE_PAIRING_TTL_MS = 120000;
|
|
5
|
+
export interface PairingSession {
|
|
6
|
+
pairingId: string;
|
|
7
|
+
token: string;
|
|
8
|
+
host: string;
|
|
9
|
+
hostname: string;
|
|
10
|
+
port: number;
|
|
11
|
+
issuedAt: number;
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
url: string;
|
|
14
|
+
confirmed?: boolean;
|
|
15
|
+
confirmedAt?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface PairingStatus {
|
|
18
|
+
pairingId: string;
|
|
19
|
+
issuedAt: number;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
confirmed: boolean;
|
|
22
|
+
confirmedAt: number;
|
|
23
|
+
active: boolean;
|
|
24
|
+
expired: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function ensureMobileToken(root: string): string;
|
|
27
|
+
export declare function tailscaleIpv4(): string | null;
|
|
28
|
+
export declare function lanIpv4(): string | null;
|
|
29
|
+
export declare function pairingHost(): string;
|
|
30
|
+
export declare function createPairingSession(root: string, ttlMs?: number): PairingSession;
|
|
31
|
+
export declare function pairingUrl(root: string): string;
|
|
32
|
+
export declare function pairingTokenPath(root: string): string;
|
|
33
|
+
export declare function pairingStatus(root: string): PairingStatus;
|
|
34
|
+
export declare function confirmPairing(root: string, pairingId: string, token: string): {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
error?: string;
|
|
37
|
+
status: PairingStatus;
|
|
38
|
+
};
|
|
39
|
+
export declare function pairingQrDataUrl(root: string, ttlMs?: number): Promise<{
|
|
40
|
+
dataUrl: string;
|
|
41
|
+
session: PairingSession;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function pairingQrAscii(root: string, ttlMs?: number): Promise<{
|
|
44
|
+
ascii: string;
|
|
45
|
+
session: PairingSession;
|
|
46
|
+
}>;
|
|
47
|
+
//# sourceMappingURL=mobilePairing.d.ts.map
|
|
@@ -0,0 +1,221 @@
|
|
|
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.MOBILE_PAIRING_TTL_MS = exports.MOBILE_PORT = exports.MOBILE_PAIRING_FILENAME = exports.MOBILE_TOKEN_FILENAME = void 0;
|
|
37
|
+
exports.ensureMobileToken = ensureMobileToken;
|
|
38
|
+
exports.tailscaleIpv4 = tailscaleIpv4;
|
|
39
|
+
exports.lanIpv4 = lanIpv4;
|
|
40
|
+
exports.pairingHost = pairingHost;
|
|
41
|
+
exports.createPairingSession = createPairingSession;
|
|
42
|
+
exports.pairingUrl = pairingUrl;
|
|
43
|
+
exports.pairingTokenPath = pairingTokenPath;
|
|
44
|
+
exports.pairingStatus = pairingStatus;
|
|
45
|
+
exports.confirmPairing = confirmPairing;
|
|
46
|
+
exports.pairingQrDataUrl = pairingQrDataUrl;
|
|
47
|
+
exports.pairingQrAscii = pairingQrAscii;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const crypto_1 = require("crypto");
|
|
52
|
+
const child_process_1 = require("child_process");
|
|
53
|
+
const QRCode = __importStar(require("qrcode"));
|
|
54
|
+
exports.MOBILE_TOKEN_FILENAME = '.newmark-mobile-token';
|
|
55
|
+
exports.MOBILE_PAIRING_FILENAME = '.newmark-mobile-pairing.json';
|
|
56
|
+
exports.MOBILE_PORT = 47890;
|
|
57
|
+
exports.MOBILE_PAIRING_TTL_MS = 120_000;
|
|
58
|
+
function pairingStatePath(root) {
|
|
59
|
+
return path.join(root, exports.MOBILE_PAIRING_FILENAME);
|
|
60
|
+
}
|
|
61
|
+
function readPairingState(root) {
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(fs.readFileSync(pairingStatePath(root), 'utf-8'));
|
|
64
|
+
if (!parsed || !parsed.pairingId || !parsed.token)
|
|
65
|
+
return null;
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function writePairingState(root, state) {
|
|
73
|
+
fs.mkdirSync(root, { recursive: true });
|
|
74
|
+
fs.writeFileSync(pairingStatePath(root), JSON.stringify(state, null, 2), 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
function ensureMobileToken(root) {
|
|
77
|
+
const tokenPath = path.join(root, exports.MOBILE_TOKEN_FILENAME);
|
|
78
|
+
try {
|
|
79
|
+
const existing = fs.readFileSync(tokenPath, 'utf-8').replace(/\s+/g, '').trim();
|
|
80
|
+
if (existing.length >= 32)
|
|
81
|
+
return existing;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// first start: no token file yet
|
|
85
|
+
}
|
|
86
|
+
const generated = (0, crypto_1.randomBytes)(24).toString('hex');
|
|
87
|
+
try {
|
|
88
|
+
fs.mkdirSync(root, { recursive: true });
|
|
89
|
+
fs.writeFileSync(tokenPath, generated, { encoding: 'utf-8', mode: 0o600 });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// keep the generated token for this process even if persistence fails
|
|
93
|
+
}
|
|
94
|
+
return generated;
|
|
95
|
+
}
|
|
96
|
+
function tailscaleIpv4() {
|
|
97
|
+
const exe = process.platform === 'win32' ? 'tailscale.exe' : 'tailscale';
|
|
98
|
+
try {
|
|
99
|
+
const result = (0, child_process_1.spawnSync)(exe, ['ip', '-4'], { encoding: 'utf-8', windowsHide: true, timeout: 3000 });
|
|
100
|
+
if (result.error || result.status !== 0)
|
|
101
|
+
return null;
|
|
102
|
+
const lines = String(result.stdout || '').split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
103
|
+
return lines[0] || null;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function lanIpv4() {
|
|
110
|
+
const interfaces = os.networkInterfaces();
|
|
111
|
+
const candidates = [];
|
|
112
|
+
for (const name of Object.keys(interfaces)) {
|
|
113
|
+
for (const info of interfaces[name] || []) {
|
|
114
|
+
if (info.family !== 'IPv4' || info.internal)
|
|
115
|
+
continue;
|
|
116
|
+
if (/^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(info.address))
|
|
117
|
+
candidates.push(info.address);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return candidates.sort()[0] || null;
|
|
121
|
+
}
|
|
122
|
+
function pairingHost() {
|
|
123
|
+
return tailscaleIpv4() || lanIpv4() || '127.0.0.1';
|
|
124
|
+
}
|
|
125
|
+
function buildPairingUrl(session) {
|
|
126
|
+
const query = new URLSearchParams({
|
|
127
|
+
token: session.token,
|
|
128
|
+
host: session.hostname,
|
|
129
|
+
port: String(session.port),
|
|
130
|
+
pairingId: session.pairingId,
|
|
131
|
+
issuedAt: String(session.issuedAt),
|
|
132
|
+
expiresAt: String(session.expiresAt),
|
|
133
|
+
});
|
|
134
|
+
return `newmark-pair://${session.host}:${session.port}?${query.toString()}`;
|
|
135
|
+
}
|
|
136
|
+
function createPairingSession(root, ttlMs = exports.MOBILE_PAIRING_TTL_MS) {
|
|
137
|
+
const token = ensureMobileToken(root);
|
|
138
|
+
const host = pairingHost();
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const session = {
|
|
141
|
+
pairingId: (0, crypto_1.randomBytes)(12).toString('hex'),
|
|
142
|
+
token,
|
|
143
|
+
host,
|
|
144
|
+
hostname: os.hostname(),
|
|
145
|
+
port: exports.MOBILE_PORT,
|
|
146
|
+
issuedAt: now,
|
|
147
|
+
expiresAt: now + Math.max(1000, Number(ttlMs) || exports.MOBILE_PAIRING_TTL_MS),
|
|
148
|
+
url: '',
|
|
149
|
+
confirmed: false,
|
|
150
|
+
confirmedAt: 0,
|
|
151
|
+
};
|
|
152
|
+
session.url = buildPairingUrl(session);
|
|
153
|
+
writePairingState(root, session);
|
|
154
|
+
return session;
|
|
155
|
+
}
|
|
156
|
+
function pairingUrl(root) {
|
|
157
|
+
return createPairingSession(root).url;
|
|
158
|
+
}
|
|
159
|
+
function pairingTokenPath(root) {
|
|
160
|
+
return path.join(root, exports.MOBILE_TOKEN_FILENAME);
|
|
161
|
+
}
|
|
162
|
+
function pairingStatus(root) {
|
|
163
|
+
const state = readPairingState(root);
|
|
164
|
+
if (!state) {
|
|
165
|
+
return { pairingId: '', issuedAt: 0, expiresAt: 0, confirmed: false, confirmedAt: 0, active: false, expired: true };
|
|
166
|
+
}
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const confirmed = state.confirmed === true;
|
|
169
|
+
const expired = now >= Number(state.expiresAt || 0);
|
|
170
|
+
return {
|
|
171
|
+
pairingId: state.pairingId,
|
|
172
|
+
issuedAt: Number(state.issuedAt || 0),
|
|
173
|
+
expiresAt: Number(state.expiresAt || 0),
|
|
174
|
+
confirmed,
|
|
175
|
+
confirmedAt: Number(state.confirmedAt || 0),
|
|
176
|
+
active: !confirmed && !expired,
|
|
177
|
+
expired: !confirmed && expired,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function confirmPairing(root, pairingId, token) {
|
|
181
|
+
const state = readPairingState(root);
|
|
182
|
+
const status = pairingStatus(root);
|
|
183
|
+
if (!state || !state.pairingId) {
|
|
184
|
+
return { ok: false, error: 'No pairing window is active.', status };
|
|
185
|
+
}
|
|
186
|
+
if (state.token !== token) {
|
|
187
|
+
return { ok: false, error: 'Pairing token does not match.', status };
|
|
188
|
+
}
|
|
189
|
+
if (state.pairingId !== pairingId) {
|
|
190
|
+
return { ok: false, error: 'Pairing window is stale.', status };
|
|
191
|
+
}
|
|
192
|
+
if (status.expired) {
|
|
193
|
+
return { ok: false, error: 'Pairing window expired.', status };
|
|
194
|
+
}
|
|
195
|
+
if (status.confirmed) {
|
|
196
|
+
return { ok: true, status };
|
|
197
|
+
}
|
|
198
|
+
const updated = { ...state, confirmed: true, confirmedAt: Date.now() };
|
|
199
|
+
writePairingState(root, updated);
|
|
200
|
+
return { ok: true, status: pairingStatus(root) };
|
|
201
|
+
}
|
|
202
|
+
async function pairingQrDataUrl(root, ttlMs) {
|
|
203
|
+
const session = createPairingSession(root, ttlMs);
|
|
204
|
+
const dataUrl = await QRCode.toDataURL(session.url, {
|
|
205
|
+
width: 420,
|
|
206
|
+
margin: 1,
|
|
207
|
+
errorCorrectionLevel: 'M',
|
|
208
|
+
color: { dark: '#101828', light: '#FFFFFF' },
|
|
209
|
+
});
|
|
210
|
+
return { dataUrl, session };
|
|
211
|
+
}
|
|
212
|
+
async function pairingQrAscii(root, ttlMs) {
|
|
213
|
+
const session = createPairingSession(root, ttlMs);
|
|
214
|
+
const ascii = await QRCode.toString(session.url, {
|
|
215
|
+
type: 'terminal',
|
|
216
|
+
small: true,
|
|
217
|
+
errorCorrectionLevel: 'M',
|
|
218
|
+
});
|
|
219
|
+
return { ascii, session };
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=mobilePairing.js.map
|
package/dist/core/subagent.d.ts
CHANGED
|
@@ -54,6 +54,10 @@ export interface SubagentInstance {
|
|
|
54
54
|
name: string;
|
|
55
55
|
conversationId: string;
|
|
56
56
|
createdByAgentId: string;
|
|
57
|
+
/** Root Build Block that created this peer. Empty only for legacy/direct API records. */
|
|
58
|
+
buildRunId?: string;
|
|
59
|
+
/** Intelligence tier captured at creation so the enforced 4/16 ceiling is auditable. */
|
|
60
|
+
intelligenceTier?: string;
|
|
57
61
|
prompt: string;
|
|
58
62
|
model: string;
|
|
59
63
|
inputMode: string;
|
|
@@ -172,7 +176,7 @@ export declare class SubagentManager {
|
|
|
172
176
|
private running;
|
|
173
177
|
private schedulingPaused;
|
|
174
178
|
private nextSequence;
|
|
175
|
-
private
|
|
179
|
+
private concurrency;
|
|
176
180
|
private executor?;
|
|
177
181
|
private onChange?;
|
|
178
182
|
private persist?;
|
|
@@ -186,9 +190,9 @@ export declare class SubagentManager {
|
|
|
186
190
|
hasRecords(): boolean;
|
|
187
191
|
reset(): void;
|
|
188
192
|
constructor(options?: SubagentManagerOptions);
|
|
189
|
-
bind(options: Pick<SubagentManagerOptions, 'executor' | 'onChange' | 'persist' | 'onMailboxMessage' | 'onRootInboxMessage' | 'onSettled'>): void;
|
|
193
|
+
bind(options: Pick<SubagentManagerOptions, 'concurrency' | 'executor' | 'onChange' | 'persist' | 'onMailboxMessage' | 'onRootInboxMessage' | 'onSettled'>): void;
|
|
190
194
|
removeRootInboxListener(listener: (message: SubagentRootMessage) => boolean): void;
|
|
191
|
-
create(name: string, prompt: string, model?: string, inputMode?: string, agentMode?: AgentMode, createdByAgentId?: string, flowName?: string, goalObjective?: string, flowPc?: number): string;
|
|
195
|
+
create(name: string, prompt: string, model?: string, inputMode?: string, agentMode?: AgentMode, createdByAgentId?: string, flowName?: string, goalObjective?: string, flowPc?: number, buildRunId?: string, intelligenceTier?: string): string;
|
|
192
196
|
get(id: string): SubagentInstance | undefined;
|
|
193
197
|
send(id: string, prompt: string): boolean;
|
|
194
198
|
sendMessage(fromAgentId: string, toAgentId: string, body: string, kind?: SubagentMessageKind, details?: {
|
|
@@ -231,6 +235,9 @@ export declare class SubagentManager {
|
|
|
231
235
|
boundedResultTranscript(idOrName: string): string;
|
|
232
236
|
listActive(): SubagentInstance[];
|
|
233
237
|
listAll(): SubagentInstance[];
|
|
238
|
+
activeCountForBuild(buildRunId: string): number;
|
|
239
|
+
setConcurrencyLimit(value: number): void;
|
|
240
|
+
concurrencyLimit(): number;
|
|
234
241
|
pauseScheduling(): void;
|
|
235
242
|
resumeScheduling(): void;
|
|
236
243
|
isSchedulingPaused(): boolean;
|
package/dist/core/subagent.js
CHANGED
|
@@ -93,6 +93,8 @@ class SubagentManager {
|
|
|
93
93
|
queueMicrotask(() => this.pump());
|
|
94
94
|
}
|
|
95
95
|
bind(options) {
|
|
96
|
+
if (options.concurrency !== undefined)
|
|
97
|
+
this.setConcurrencyLimit(options.concurrency);
|
|
96
98
|
if (options.executor)
|
|
97
99
|
this.executor = options.executor;
|
|
98
100
|
if (options.onChange)
|
|
@@ -115,16 +117,16 @@ class SubagentManager {
|
|
|
115
117
|
removeRootInboxListener(listener) {
|
|
116
118
|
this.rootInboxListeners.delete(listener);
|
|
117
119
|
}
|
|
118
|
-
create(name, prompt, model, inputMode, agentMode = 'build', createdByAgentId = this.rootAgentId, flowName = '', goalObjective = '', flowPc = 0) {
|
|
120
|
+
create(name, prompt, model, inputMode, agentMode = 'build', createdByAgentId = this.rootAgentId, flowName = '', goalObjective = '', flowPc = 0, buildRunId = '', intelligenceTier = '') {
|
|
119
121
|
const id = (0, crypto_1.randomUUID)();
|
|
120
122
|
const shortId = id.replace(/-/g, '').slice(0, 8);
|
|
121
123
|
const slug = natureSlug(name);
|
|
122
|
-
// The
|
|
123
|
-
//
|
|
124
|
-
// the
|
|
125
|
-
|
|
126
|
-
const displayName =
|
|
127
|
-
const qualifiedName = `${
|
|
124
|
+
// The monitoring label is exactly the caller-created human-readable name.
|
|
125
|
+
// UUID-bearing identity stays in id/qualifiedName and is never appended to
|
|
126
|
+
// the right-sidebar title.
|
|
127
|
+
const createdName = String(name || 'SubAgent').replace(/\s+/g, ' ').trim().slice(0, 160) || 'SubAgent';
|
|
128
|
+
const displayName = createdName;
|
|
129
|
+
const qualifiedName = `${slug}--${id}`;
|
|
128
130
|
const stamp = now();
|
|
129
131
|
const record = {
|
|
130
132
|
id,
|
|
@@ -132,9 +134,11 @@ class SubagentManager {
|
|
|
132
134
|
natureSlug: slug,
|
|
133
135
|
displayName,
|
|
134
136
|
qualifiedName,
|
|
135
|
-
name:
|
|
137
|
+
name: createdName,
|
|
136
138
|
conversationId: this.conversationId,
|
|
137
139
|
createdByAgentId,
|
|
140
|
+
buildRunId: String(buildRunId || '').trim() || undefined,
|
|
141
|
+
intelligenceTier: String(intelligenceTier || '').trim() || undefined,
|
|
138
142
|
prompt,
|
|
139
143
|
model: model || 'default',
|
|
140
144
|
inputMode: inputMode || 'guide',
|
|
@@ -504,6 +508,17 @@ class SubagentManager {
|
|
|
504
508
|
}
|
|
505
509
|
listActive() { return this.listAll().filter(item => item.status !== 'closed'); }
|
|
506
510
|
listAll() { return [...this.subs.values()].map(cloneRecord); }
|
|
511
|
+
activeCountForBuild(buildRunId) {
|
|
512
|
+
const target = String(buildRunId || '').trim();
|
|
513
|
+
if (!target)
|
|
514
|
+
return 0;
|
|
515
|
+
return [...this.subs.values()].filter(record => record.buildRunId === target && (record.status === 'queued' || record.status === 'working')).length;
|
|
516
|
+
}
|
|
517
|
+
setConcurrencyLimit(value) {
|
|
518
|
+
this.concurrency = Math.max(1, Math.min(16, Math.floor(Number(value) || 4)));
|
|
519
|
+
this.pump();
|
|
520
|
+
}
|
|
521
|
+
concurrencyLimit() { return this.concurrency; }
|
|
507
522
|
pauseScheduling() {
|
|
508
523
|
if (this.schedulingPaused)
|
|
509
524
|
return;
|