host-execution-mcp 1.0.0

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/LICENSE +21 -0
  3. package/README.md +92 -0
  4. package/SECURITY.md +72 -0
  5. package/dist/access/docker-runtime.d.ts +38 -0
  6. package/dist/access/docker-runtime.js +61 -0
  7. package/dist/access/docker-runtime.js.map +1 -0
  8. package/dist/access/index.d.ts +110 -0
  9. package/dist/access/index.js +174 -0
  10. package/dist/access/index.js.map +1 -0
  11. package/dist/access/paradi-auth.d.ts +33 -0
  12. package/dist/access/paradi-auth.js +89 -0
  13. package/dist/access/paradi-auth.js.map +1 -0
  14. package/dist/access/paradi-transport.d.ts +10 -0
  15. package/dist/access/paradi-transport.js +39 -0
  16. package/dist/access/paradi-transport.js.map +1 -0
  17. package/dist/access/paradi-username.d.ts +5 -0
  18. package/dist/access/paradi-username.js +14 -0
  19. package/dist/access/paradi-username.js.map +1 -0
  20. package/dist/access/probe.d.ts +14 -0
  21. package/dist/access/probe.js +63 -0
  22. package/dist/access/probe.js.map +1 -0
  23. package/dist/access/session-pool.d.ts +25 -0
  24. package/dist/access/session-pool.js +46 -0
  25. package/dist/access/session-pool.js.map +1 -0
  26. package/dist/access/ssh-transport.d.ts +16 -0
  27. package/dist/access/ssh-transport.js +74 -0
  28. package/dist/access/ssh-transport.js.map +1 -0
  29. package/dist/access/types.d.ts +43 -0
  30. package/dist/access/types.js +21 -0
  31. package/dist/access/types.js.map +1 -0
  32. package/dist/config.d.ts +52 -0
  33. package/dist/config.js +179 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +19 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/instructions.d.ts +4 -0
  39. package/dist/instructions.js +93 -0
  40. package/dist/instructions.js.map +1 -0
  41. package/dist/kubectl/runner.d.ts +32 -0
  42. package/dist/kubectl/runner.js +79 -0
  43. package/dist/kubectl/runner.js.map +1 -0
  44. package/dist/server.d.ts +3 -0
  45. package/dist/server.js +24 -0
  46. package/dist/server.js.map +1 -0
  47. package/dist/ssh/access-client.d.ts +62 -0
  48. package/dist/ssh/access-client.js +470 -0
  49. package/dist/ssh/access-client.js.map +1 -0
  50. package/dist/ssh/access-errors.d.ts +53 -0
  51. package/dist/ssh/access-errors.js +180 -0
  52. package/dist/ssh/access-errors.js.map +1 -0
  53. package/dist/ssh/access-sftp.d.ts +14 -0
  54. package/dist/ssh/access-sftp.js +456 -0
  55. package/dist/ssh/access-sftp.js.map +1 -0
  56. package/dist/ssh/bastion-client.d.ts +60 -0
  57. package/dist/ssh/bastion-client.js +423 -0
  58. package/dist/ssh/bastion-client.js.map +1 -0
  59. package/dist/ssh/bastion-errors.d.ts +62 -0
  60. package/dist/ssh/bastion-errors.js +190 -0
  61. package/dist/ssh/bastion-errors.js.map +1 -0
  62. package/dist/ssh/hop1-sftp.d.ts +14 -0
  63. package/dist/ssh/hop1-sftp.js +437 -0
  64. package/dist/ssh/hop1-sftp.js.map +1 -0
  65. package/dist/tools/copy-job-schemas.d.ts +709 -0
  66. package/dist/tools/copy-job-schemas.js +48 -0
  67. package/dist/tools/copy-job-schemas.js.map +1 -0
  68. package/dist/tools/register.d.ts +14 -0
  69. package/dist/tools/register.js +619 -0
  70. package/dist/tools/register.js.map +1 -0
  71. package/dist/tools/remote-schemas.d.ts +879 -0
  72. package/dist/tools/remote-schemas.js +214 -0
  73. package/dist/tools/remote-schemas.js.map +1 -0
  74. package/dist/utils/copy-executor.d.ts +41 -0
  75. package/dist/utils/copy-executor.js +231 -0
  76. package/dist/utils/copy-executor.js.map +1 -0
  77. package/dist/utils/copy-job-manager.d.ts +34 -0
  78. package/dist/utils/copy-job-manager.js +400 -0
  79. package/dist/utils/copy-job-manager.js.map +1 -0
  80. package/dist/utils/copy-job-store.d.ts +18 -0
  81. package/dist/utils/copy-job-store.js +104 -0
  82. package/dist/utils/copy-job-store.js.map +1 -0
  83. package/dist/utils/copy-job-types.d.ts +133 -0
  84. package/dist/utils/copy-job-types.js +37 -0
  85. package/dist/utils/copy-job-types.js.map +1 -0
  86. package/dist/utils/copy-transfer.d.ts +59 -0
  87. package/dist/utils/copy-transfer.js +86 -0
  88. package/dist/utils/copy-transfer.js.map +1 -0
  89. package/dist/utils/remote-copy.d.ts +14 -0
  90. package/dist/utils/remote-copy.js +115 -0
  91. package/dist/utils/remote-copy.js.map +1 -0
  92. package/dist/utils/remote-executor.d.ts +15 -0
  93. package/dist/utils/remote-executor.js +34 -0
  94. package/dist/utils/remote-executor.js.map +1 -0
  95. package/dist/utils/remote-target.d.ts +22 -0
  96. package/dist/utils/remote-target.js +75 -0
  97. package/dist/utils/remote-target.js.map +1 -0
  98. package/dist/utils/result.d.ts +87 -0
  99. package/dist/utils/result.js +81 -0
  100. package/dist/utils/result.js.map +1 -0
  101. package/dist/utils/shell-quote.d.ts +22 -0
  102. package/dist/utils/shell-quote.js +55 -0
  103. package/dist/utils/shell-quote.js.map +1 -0
  104. package/dist/utils/timeout.d.ts +1 -0
  105. package/dist/utils/timeout.js +18 -0
  106. package/dist/utils/timeout.js.map +1 -0
  107. package/dist/utils/truncate.d.ts +5 -0
  108. package/dist/utils/truncate.js +12 -0
  109. package/dist/utils/truncate.js.map +1 -0
  110. package/dist/version.d.ts +1 -0
  111. package/dist/version.js +7 -0
  112. package/dist/version.js.map +1 -0
  113. package/docs/AGENT_GUIDE.md +161 -0
  114. package/docs/CONFIGURATION.md +130 -0
  115. package/docs/COPY_SPECIALIZED_TEST_CHECKLIST.md +9 -0
  116. package/docs/HOP1_E2E_CHECKLIST.md +128 -0
  117. package/docs/HOP1_E2E_RESULTS.json +647 -0
  118. package/docs/PARADI_ACCESS_PLAN.md +272 -0
  119. package/docs/REMOTE_COPY_JOB_API.md +92 -0
  120. package/docs/TODO.md +66 -0
  121. package/docs/USER_GUIDE.zh-CN.md +93 -0
  122. package/docs/clients/claude-desktop.json.example +12 -0
  123. package/docs/clients/cursor-npx.json.example +12 -0
  124. package/docs/clients/cursor.json.example +12 -0
  125. package/docs/clients/vscode.json.example +12 -0
  126. package/package.json +65 -0
@@ -0,0 +1,133 @@
1
+ import type { FileEndpoint } from "./remote-copy.js";
2
+ import type { CopyProgress, CopySkipReason, CopyVerifyMode } from "./copy-transfer.js";
3
+ /** Job lifecycle visible to Agent / UI. */
4
+ export type CopyJobStatus = "queued" | "running" | "success" | "failed" | "cancelled" | "timeout";
5
+ /** How the job is executed under the hood (not exposed as separate tools). */
6
+ export type CopyExecutorKind = "sftp" | "kubectl" | "scp" | "landing-cp" | "pipeline";
7
+ /**
8
+ * Durable job record (MCP-local store, scheme A).
9
+ * Path suggestion: ~/.agent-compose/remote-copy/jobs/<transferId>.json
10
+ */
11
+ export interface CopyJobRecord {
12
+ transferId: string;
13
+ status: CopyJobStatus;
14
+ source: FileEndpoint;
15
+ dest: FileEndpoint;
16
+ /** Engine options shared with sync path. */
17
+ options: {
18
+ skipIfComplete: boolean;
19
+ verify: CopyVerifyMode;
20
+ progressIntervalMs: number;
21
+ /** Wall-clock budget for the whole job (all hops). */
22
+ totalTimeoutMs: number;
23
+ recursive: boolean;
24
+ };
25
+ /** Multi-hop plan, e.g. pod→accessClient tmp→local. */
26
+ plan: CopyJobHop[];
27
+ progress: CopyJobProgress;
28
+ /** Set when Engine reports verify result. */
29
+ verified?: boolean;
30
+ estimatedSeconds?: number;
31
+ error?: string;
32
+ createdAt: string;
33
+ updatedAt: string;
34
+ startedAt?: string;
35
+ finishedAt?: string;
36
+ }
37
+ export declare function isTerminalCopyJobStatus(status: CopyJobStatus): status is Extract<CopyJobStatus, "success" | "failed" | "cancelled" | "timeout">;
38
+ export interface CopyJobHop {
39
+ index: number;
40
+ kind: CopyExecutorKind;
41
+ source: FileEndpoint;
42
+ dest: FileEndpoint;
43
+ status: CopyJobStatus;
44
+ /** Staging path on accessClient when this hop uses a temp file. */
45
+ stagingPath?: string;
46
+ }
47
+ export interface CopyJobProgress {
48
+ transferredBytes: number;
49
+ totalBytes?: number;
50
+ percent: number;
51
+ speedBps?: number;
52
+ etaSeconds?: number;
53
+ skippedBytes?: number;
54
+ skipped?: boolean;
55
+ skipReason?: CopySkipReason;
56
+ /** Which hop is active (0-based). */
57
+ currentHopIndex?: number;
58
+ }
59
+ export interface RemoteCopyStartRequest {
60
+ source: FileEndpoint;
61
+ dest: FileEndpoint;
62
+ skipIfComplete?: boolean;
63
+ verify?: CopyVerifyMode;
64
+ recursive?: boolean;
65
+ progressIntervalMs?: number;
66
+ /** @deprecated prefer totalTimeoutMs */
67
+ timeoutMs?: number;
68
+ totalTimeoutMs?: number;
69
+ }
70
+ export interface RemoteCopyStartResult {
71
+ transferId: string;
72
+ status: Extract<CopyJobStatus, "queued" | "running">;
73
+ source: {
74
+ path: string;
75
+ size?: number;
76
+ target: string;
77
+ };
78
+ dest: {
79
+ path: string;
80
+ target: string;
81
+ };
82
+ estimatedSeconds?: number;
83
+ /** Where Agent/user can find durable state (MCP-local). */
84
+ jobStorePath?: string;
85
+ /** Human hint: poll remote_copy_status; do not block the chat. */
86
+ message: string;
87
+ createdAt: string;
88
+ }
89
+ export interface RemoteCopyStatusRequest {
90
+ transferId: string;
91
+ }
92
+ export interface RemoteCopyStatusResult {
93
+ transferId: string;
94
+ status: CopyJobStatus;
95
+ progress: CopyJobProgress;
96
+ source: {
97
+ path: string;
98
+ target: string;
99
+ };
100
+ dest: {
101
+ path: string;
102
+ target: string;
103
+ };
104
+ skippedBytes?: number;
105
+ skipped?: boolean;
106
+ skipReason?: CopySkipReason;
107
+ verified?: boolean;
108
+ speedBps?: number;
109
+ etaSeconds?: number;
110
+ error?: string;
111
+ createdAt: string;
112
+ updatedAt: string;
113
+ finishedAt?: string;
114
+ }
115
+ export interface RemoteCopyCancelRequest {
116
+ transferId: string;
117
+ }
118
+ export interface RemoteCopyCancelResult {
119
+ transferId: string;
120
+ status: Extract<CopyJobStatus, "cancelled" | "success" | "failed" | "timeout">;
121
+ message: string;
122
+ }
123
+ /** Map engine progress callback → job progress fields. */
124
+ export declare function engineProgressToJobProgress(p: CopyProgress, currentHopIndex?: number): CopyJobProgress;
125
+ /** Async threshold policy (Agent + optional auto-route later). */
126
+ export declare const DEFAULT_ASYNC_THRESHOLD: {
127
+ readonly sizeBytes: number;
128
+ readonly estimatedSeconds: 120;
129
+ };
130
+ export declare function shouldPreferAsyncCopy(input: {
131
+ sizeBytes?: number;
132
+ estimatedSeconds?: number;
133
+ }): boolean;
@@ -0,0 +1,37 @@
1
+ export function isTerminalCopyJobStatus(status) {
2
+ return (status === "success" ||
3
+ status === "failed" ||
4
+ status === "cancelled" ||
5
+ status === "timeout");
6
+ }
7
+ /** Map engine progress callback → job progress fields. */
8
+ export function engineProgressToJobProgress(p, currentHopIndex) {
9
+ const etaSeconds = p.speedBps && p.speedBps > 0 && p.total > p.transferred
10
+ ? Math.ceil((p.total - p.transferred) / p.speedBps)
11
+ : undefined;
12
+ return {
13
+ transferredBytes: p.transferred,
14
+ totalBytes: p.total,
15
+ percent: p.percent,
16
+ speedBps: p.speedBps,
17
+ etaSeconds,
18
+ skippedBytes: p.skippedBytes,
19
+ currentHopIndex,
20
+ };
21
+ }
22
+ /** Async threshold policy (Agent + optional auto-route later). */
23
+ export const DEFAULT_ASYNC_THRESHOLD = {
24
+ sizeBytes: 256 * 1024 * 1024,
25
+ estimatedSeconds: 120,
26
+ };
27
+ export function shouldPreferAsyncCopy(input) {
28
+ if (input.sizeBytes !== undefined && input.sizeBytes > DEFAULT_ASYNC_THRESHOLD.sizeBytes) {
29
+ return true;
30
+ }
31
+ if (input.estimatedSeconds !== undefined &&
32
+ input.estimatedSeconds > DEFAULT_ASYNC_THRESHOLD.estimatedSeconds) {
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ //# sourceMappingURL=copy-job-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-job-types.js","sourceRoot":"","sources":["../../src/utils/copy-job-types.ts"],"names":[],"mappings":"AAqDA,MAAM,UAAU,uBAAuB,CACrC,MAAqB;IAErB,OAAO,CACL,MAAM,KAAK,SAAS;QACpB,MAAM,KAAK,QAAQ;QACnB,MAAM,KAAK,WAAW;QACtB,MAAM,KAAK,SAAS,CACrB,CAAC;AACJ,CAAC;AAoFD,0DAA0D;AAC1D,MAAM,UAAU,2BAA2B,CACzC,CAAe,EACf,eAAwB;IAExB,MAAM,UAAU,GACd,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,WAAW;QACrD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACnD,CAAC,CAAC,SAAS,CAAC;IAChB,OAAO;QACL,gBAAgB,EAAE,CAAC,CAAC,WAAW;QAC/B,UAAU,EAAE,CAAC,CAAC,KAAK;QACnB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU;QACV,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,SAAS,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI;IAC5B,gBAAgB,EAAE,GAAG;CACb,CAAC;AAEX,MAAM,UAAU,qBAAqB,CAAC,KAGrC;IACC,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,GAAG,uBAAuB,CAAC,SAAS,EAAE,CAAC;QACzF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,KAAK,CAAC,gBAAgB,KAAK,SAAS;QACpC,KAAK,CAAC,gBAAgB,GAAG,uBAAuB,CAAC,gBAAgB,EACjE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,59 @@
1
+ export declare const DEFAULT_PROGRESS_INTERVAL_MS = 5000;
2
+ export declare const DEFAULT_CONNECTION_TIMEOUT_MS = 20000;
3
+ export type CopyVerifyMode = "none" | "size" | "size+sha256";
4
+ export type CopySkipReason = "size-match" | "hash-match";
5
+ export interface CopyProgress {
6
+ transferred: number;
7
+ total: number;
8
+ percent: number;
9
+ speedBps?: number;
10
+ /** Bytes already present at dest and counted as complete when skipIfComplete skipped a rewrite. */
11
+ skippedBytes?: number;
12
+ }
13
+ export interface SftpCopyOptions {
14
+ /** If true and destination already matches source (+ verify gate), skip rewrite. */
15
+ skipIfComplete?: boolean;
16
+ /** Progress callback throttle interval (default 5000). */
17
+ progressIntervalMs?: number;
18
+ /** Hard wall-clock limit for the whole Hop1 transfer. */
19
+ totalTimeoutMs?: number;
20
+ /** SSH connect readyTimeout for Hop1 SFTP (default 20000). */
21
+ connectionTimeoutMs?: number;
22
+ /**
23
+ * Skip gate + post-transfer verification (default size).
24
+ * With skipIfComplete: size = cheap size gate; size+sha256 = content gate; none is rejected.
25
+ */
26
+ verify?: CopyVerifyMode;
27
+ onProgress?: (progress: CopyProgress) => void;
28
+ /** Pre-assigned id; generated if omitted. */
29
+ transferId?: string;
30
+ /** Abort in-flight Hop1 transfer (ends SFTP session only). */
31
+ signal?: AbortSignal;
32
+ }
33
+ export interface SftpTransferResult {
34
+ bytes: number;
35
+ totalBytes: number;
36
+ skippedBytes: number;
37
+ /** True when any bytes were skipped via skipIfComplete. */
38
+ skipped: boolean;
39
+ /** Why skip was allowed (only when skipped). */
40
+ skipReason?: CopySkipReason;
41
+ transferId: string;
42
+ verified: boolean;
43
+ usedFastPath: boolean;
44
+ }
45
+ /**
46
+ * Single rule center for skip + verify.
47
+ * Does NOT auto-upgrade size → size+sha256 (checkpoint-friendly cheap skip).
48
+ */
49
+ export declare function resolveEffectiveVerify(skipIfComplete: boolean, verify?: CopyVerifyMode): CopyVerifyMode;
50
+ export declare function skipReasonForVerify(verify: CopyVerifyMode): CopySkipReason;
51
+ /** Build skip observability fields from skipped byte count + verify mode. */
52
+ export declare function transferSkipFields(skippedBytes: number, verify: CopyVerifyMode): Pick<SftpTransferResult, "skippedBytes" | "skipped" | "skipReason">;
53
+ export declare function createTransferId(): string;
54
+ export declare function throwIfAborted(signal?: AbortSignal): void;
55
+ export declare function createProgressReporter(intervalMs: number, onProgress?: (progress: CopyProgress) => void, skippedBytes?: number): {
56
+ report: (transferred: number, total: number, force?: boolean) => void;
57
+ flush: (transferred: number, total: number) => void;
58
+ };
59
+ export declare function sha256File(filePath: string): Promise<string>;
@@ -0,0 +1,86 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ export const DEFAULT_PROGRESS_INTERVAL_MS = 5_000;
4
+ export const DEFAULT_CONNECTION_TIMEOUT_MS = 20_000;
5
+ /**
6
+ * Single rule center for skip + verify.
7
+ * Does NOT auto-upgrade size → size+sha256 (checkpoint-friendly cheap skip).
8
+ */
9
+ export function resolveEffectiveVerify(skipIfComplete, verify) {
10
+ const v = verify ?? "size";
11
+ if (skipIfComplete && v === "none") {
12
+ throw new Error("skipIfComplete requires verify=size or verify=size+sha256");
13
+ }
14
+ return v;
15
+ }
16
+ export function skipReasonForVerify(verify) {
17
+ return verify === "size+sha256" ? "hash-match" : "size-match";
18
+ }
19
+ /** Build skip observability fields from skipped byte count + verify mode. */
20
+ export function transferSkipFields(skippedBytes, verify) {
21
+ if (skippedBytes > 0) {
22
+ return {
23
+ skippedBytes,
24
+ skipped: true,
25
+ skipReason: skipReasonForVerify(verify),
26
+ };
27
+ }
28
+ return { skippedBytes: 0, skipped: false };
29
+ }
30
+ export function createTransferId() {
31
+ return `copy-${Date.now().toString(36)}-${crypto.randomBytes(3).toString("hex")}`;
32
+ }
33
+ export function throwIfAborted(signal) {
34
+ if (signal?.aborted) {
35
+ throw new Error("Copy aborted");
36
+ }
37
+ }
38
+ export function createProgressReporter(intervalMs, onProgress, skippedBytes = 0) {
39
+ let lastReport = 0;
40
+ let lastBytes = skippedBytes;
41
+ let lastTs = Date.now();
42
+ const emit = (transferred, total) => {
43
+ if (!onProgress) {
44
+ return;
45
+ }
46
+ const now = Date.now();
47
+ const dt = Math.max(1, now - lastTs);
48
+ const speedBps = ((transferred - lastBytes) * 1000) / dt;
49
+ lastBytes = transferred;
50
+ lastTs = now;
51
+ lastReport = now;
52
+ onProgress({
53
+ transferred,
54
+ total,
55
+ percent: total > 0 ? Math.min(100, (transferred / total) * 100) : 0,
56
+ speedBps,
57
+ skippedBytes,
58
+ });
59
+ };
60
+ return {
61
+ report(transferred, total, force = false) {
62
+ if (!onProgress) {
63
+ return;
64
+ }
65
+ const now = Date.now();
66
+ if (!force && now - lastReport < intervalMs) {
67
+ return;
68
+ }
69
+ emit(transferred, total);
70
+ },
71
+ flush(transferred, total) {
72
+ emit(transferred, total);
73
+ },
74
+ };
75
+ }
76
+ export async function sha256File(filePath) {
77
+ const hash = crypto.createHash("sha256");
78
+ await new Promise((resolve, reject) => {
79
+ const stream = fs.createReadStream(filePath);
80
+ stream.on("data", (chunk) => hash.update(chunk));
81
+ stream.on("error", reject);
82
+ stream.on("end", () => resolve());
83
+ });
84
+ return hash.digest("hex");
85
+ }
86
+ //# sourceMappingURL=copy-transfer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-transfer.js","sourceRoot":"","sources":["../../src/utils/copy-transfer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface FileEndpoint {
2
+ target: "landing" | "pod" | "host" | "local" | "context";
3
+ path: string;
4
+ contextKey?: string;
5
+ namespace?: string;
6
+ pod?: string;
7
+ container?: string;
8
+ host?: string;
9
+ user?: string;
10
+ password?: string;
11
+ }
12
+ /** Build a bash script (runs on accessClient) that copies source→dest and prints MCP_COPY_BYTES=<n>. */
13
+ export declare function buildCopyFileCommand(source: FileEndpoint, dest: FileEndpoint, recursive: boolean, defaultUser?: string, defaultPassword?: string): string;
14
+ export declare function parseCopyBytes(output: string): number | undefined;
@@ -0,0 +1,115 @@
1
+ import { bashSingleQuote, buildHostTransportPrefix, buildSshRemoteCommand, } from "./shell-quote.js";
2
+ import { resolveRemotePassword, resolveRemoteUser } from "./remote-target.js";
3
+ function quotePath(path) {
4
+ return bashSingleQuote(path);
5
+ }
6
+ function kubectlCpFromPod(namespace, pod, remotePath, localPath, container) {
7
+ const remote = `${namespace}/${pod}:${remotePath}`;
8
+ const parts = ["kubectl cp", quotePath(remote), quotePath(localPath)];
9
+ if (container) {
10
+ parts.push("-c", quotePath(container));
11
+ }
12
+ return parts.join(" ");
13
+ }
14
+ function kubectlCpToPod(namespace, pod, localPath, remotePath, container) {
15
+ const remote = `${namespace}/${pod}:${remotePath}`;
16
+ const parts = ["kubectl cp", quotePath(localPath), quotePath(remote)];
17
+ if (container) {
18
+ parts.push("-c", quotePath(container));
19
+ }
20
+ return parts.join(" ");
21
+ }
22
+ function scpFromHost(user, host, remotePath, localPath, password) {
23
+ const remote = `${user}@${host}:${remotePath}`;
24
+ return `${buildHostTransportPrefix("scp", password)} -r ${quotePath(remote)} ${quotePath(localPath)}`;
25
+ }
26
+ function scpToHost(user, host, localPath, remotePath, password) {
27
+ const remote = `${user}@${host}:${remotePath}`;
28
+ return `${buildHostTransportPrefix("scp", password)} -r ${quotePath(localPath)} ${quotePath(remote)}`;
29
+ }
30
+ function cpOnLanding(sourcePath, destPath, recursive) {
31
+ const flag = recursive ? "-a" : "";
32
+ return `mkdir -p $(dirname ${quotePath(destPath)}) && cp ${flag} ${quotePath(sourcePath)} ${quotePath(destPath)}`.replace(" ", " ");
33
+ }
34
+ function statBytesCommand(path) {
35
+ return `stat -c%s ${quotePath(path)} 2>/dev/null || wc -c < ${quotePath(path)}`;
36
+ }
37
+ /** Remote host byte-stat via shared buildSshRemoteCommand (same password as scp). */
38
+ function hostStatBytesEcho(user, host, path, password) {
39
+ const ssh = buildSshRemoteCommand(user, host, statBytesCommand(path), password);
40
+ return `echo MCP_COPY_BYTES=$(${ssh})`;
41
+ }
42
+ function hostUserPassword(endpoint, defaultUser, defaultPassword) {
43
+ return {
44
+ user: resolveRemoteUser(endpoint.user, defaultUser),
45
+ password: resolveRemotePassword(endpoint.password, defaultPassword),
46
+ };
47
+ }
48
+ /** Build a bash script (runs on accessClient) that copies source→dest and prints MCP_COPY_BYTES=<n>. */
49
+ export function buildCopyFileCommand(source, dest, recursive, defaultUser, defaultPassword) {
50
+ const src = source.target;
51
+ const dst = dest.target;
52
+ if (src === "landing" && dst === "landing") {
53
+ return `${cpOnLanding(source.path, dest.path, recursive)} && echo MCP_COPY_BYTES=$( ${statBytesCommand(dest.path)} )`;
54
+ }
55
+ if (src === "pod" && dst === "landing") {
56
+ const pre = `mkdir -p $(dirname ${quotePath(dest.path)})`;
57
+ const cp = kubectlCpFromPod(source.namespace, source.pod, source.path, dest.path, source.container);
58
+ return `${pre} && ${cp} && echo MCP_COPY_BYTES=$( ${statBytesCommand(dest.path)} )`;
59
+ }
60
+ if (src === "landing" && dst === "pod") {
61
+ const mkdir = `kubectl exec -n ${quotePath(dest.namespace)} ${quotePath(dest.pod)}${dest.container ? ` -c ${quotePath(dest.container)}` : ""} -- bash -lc ${quotePath(`mkdir -p $(dirname '${dest.path.replace(/'/g, "'\\''")}')`)}`;
62
+ const cp = kubectlCpToPod(dest.namespace, dest.pod, source.path, dest.path, dest.container);
63
+ return `${mkdir} && ${cp} && echo MCP_COPY_BYTES=$(kubectl exec -n ${quotePath(dest.namespace)} ${quotePath(dest.pod)}${dest.container ? ` -c ${quotePath(dest.container)}` : ""} -- bash -lc ${quotePath(`stat -c%s '${dest.path.replace(/'/g, "'\\''")}' 2>/dev/null || wc -c < '${dest.path.replace(/'/g, "'\\''")}'`)} )`;
64
+ }
65
+ if (src === "host" && dst === "landing") {
66
+ const { user, password } = hostUserPassword(source, defaultUser, defaultPassword);
67
+ const pre = `mkdir -p $(dirname ${quotePath(dest.path)})`;
68
+ const cp = scpFromHost(user, source.host, source.path, dest.path, password);
69
+ return `${pre} && ${cp} && echo MCP_COPY_BYTES=$( ${statBytesCommand(dest.path)} )`;
70
+ }
71
+ if (src === "landing" && dst === "host") {
72
+ const { user, password } = hostUserPassword(dest, defaultUser, defaultPassword);
73
+ const cp = scpToHost(user, dest.host, source.path, dest.path, password);
74
+ return `${cp} && ${hostStatBytesEcho(user, dest.host, dest.path, password)}`;
75
+ }
76
+ // Cross-tier copies via landing temp directory
77
+ const tmpVar = "MCP_COPY_TMP";
78
+ const setup = `${tmpVar}=$(mktemp -d) && trap 'rm -rf "$${tmpVar}"' EXIT`;
79
+ const tmpItem = `$${tmpVar}/item`;
80
+ if (src === "pod" && dst === "pod") {
81
+ const pull = kubectlCpFromPod(source.namespace, source.pod, source.path, tmpItem, source.container);
82
+ const push = kubectlCpToPod(dest.namespace, dest.pod, tmpItem, dest.path, dest.container);
83
+ const stat = `echo MCP_COPY_BYTES=$(kubectl exec -n ${quotePath(dest.namespace)} ${quotePath(dest.pod)}${dest.container ? ` -c ${quotePath(dest.container)}` : ""} -- bash -lc ${quotePath(`stat -c%s '${dest.path.replace(/'/g, "'\\''")}' 2>/dev/null || wc -c < '${dest.path.replace(/'/g, "'\\''")}'`)} )`;
84
+ return `${setup} && ${pull} && ${push} && ${stat}`;
85
+ }
86
+ if (src === "pod" && dst === "host") {
87
+ const { user, password } = hostUserPassword(dest, defaultUser, defaultPassword);
88
+ const pull = kubectlCpFromPod(source.namespace, source.pod, source.path, tmpItem, source.container);
89
+ const push = scpToHost(user, dest.host, tmpItem, dest.path, password);
90
+ return `${setup} && ${pull} && ${push} && ${hostStatBytesEcho(user, dest.host, dest.path, password)}`;
91
+ }
92
+ if (src === "host" && dst === "pod") {
93
+ const { user, password } = hostUserPassword(source, defaultUser, defaultPassword);
94
+ const pull = scpFromHost(user, source.host, source.path, tmpItem, password);
95
+ const push = kubectlCpToPod(dest.namespace, dest.pod, tmpItem, dest.path, dest.container);
96
+ const stat = `echo MCP_COPY_BYTES=$(kubectl exec -n ${quotePath(dest.namespace)} ${quotePath(dest.pod)}${dest.container ? ` -c ${quotePath(dest.container)}` : ""} -- bash -lc ${quotePath(`stat -c%s '${dest.path.replace(/'/g, "'\\''")}' 2>/dev/null || wc -c < '${dest.path.replace(/'/g, "'\\''")}'`)} )`;
97
+ return `${setup} && ${pull} && ${push} && ${stat}`;
98
+ }
99
+ if (src === "host" && dst === "host") {
100
+ const srcAuth = hostUserPassword(source, defaultUser, defaultPassword);
101
+ const dstAuth = hostUserPassword(dest, defaultUser, defaultPassword);
102
+ const pull = scpFromHost(srcAuth.user, source.host, source.path, tmpItem, srcAuth.password);
103
+ const push = scpToHost(dstAuth.user, dest.host, tmpItem, dest.path, dstAuth.password);
104
+ return `${setup} && ${pull} && ${push} && ${hostStatBytesEcho(dstAuth.user, dest.host, dest.path, dstAuth.password)}`;
105
+ }
106
+ throw new Error(`Unsupported copy route: ${src} → ${dst}`);
107
+ }
108
+ export function parseCopyBytes(output) {
109
+ const match = output.match(/MCP_COPY_BYTES=(\d+)/);
110
+ if (!match) {
111
+ return undefined;
112
+ }
113
+ return Number.parseInt(match[1], 10);
114
+ }
115
+ //# sourceMappingURL=remote-copy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-copy.js","sourceRoot":"","sources":["../../src/utils/remote-copy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { AppConfig } from "../config.js";
2
+ import type { AccessRuntime } from "../access/index.js";
3
+ import type { KubectlRunner } from "../kubectl/runner.js";
4
+ import type { RemoteTarget } from "../tools/remote-schemas.js";
5
+ /**
6
+ * Runtime routing: landing / pod / context.
7
+ * context path = shellExec on derived Context by contextKey.
8
+ */
9
+ export declare class RemoteExecutor {
10
+ private readonly access;
11
+ private readonly kubectl;
12
+ private readonly config;
13
+ constructor(access: AccessRuntime, kubectl: KubectlRunner, config: AppConfig);
14
+ runOnTarget(target: RemoteTarget, command: string, timeoutMs?: number): Promise<import("../ssh/access-client.js").ExecOutput>;
15
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Runtime routing: landing / pod / context.
3
+ * context path = shellExec on derived Context by contextKey.
4
+ */
5
+ export class RemoteExecutor {
6
+ access;
7
+ kubectl;
8
+ config;
9
+ constructor(access, kubectl, config) {
10
+ this.access = access;
11
+ this.kubectl = kubectl;
12
+ this.config = config;
13
+ }
14
+ async runOnTarget(target, command, timeoutMs) {
15
+ switch (target.target) {
16
+ case "landing":
17
+ return this.access.shellExec(command, {
18
+ timeoutMs: timeoutMs ?? this.config.defaultTimeoutMs,
19
+ });
20
+ case "context":
21
+ return this.access.shellExec(command, {
22
+ contextKey: target.contextKey,
23
+ timeoutMs: timeoutMs ?? this.config.defaultTimeoutMs,
24
+ });
25
+ case "pod":
26
+ return this.kubectl.exec(target.namespace, target.pod, command, target.container, timeoutMs);
27
+ default: {
28
+ const exhaustive = target;
29
+ throw new Error(`Unsupported target: ${String(exhaustive)}`);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ //# sourceMappingURL=remote-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-executor.js","sourceRoot":"","sources":["../../src/utils/remote-executor.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,MAAM,OAAO,cAAc;IAEN;IACA;IACA;IAHnB,YACmB,MAAqB,EACrB,OAAsB,EACtB,MAAiB;QAFjB,WAAM,GAAN,MAAM,CAAe;QACrB,YAAO,GAAP,OAAO,CAAe;QACtB,WAAM,GAAN,MAAM,CAAW;IACjC,CAAC;IAEJ,KAAK,CAAC,WAAW,CACf,MAAoB,EACpB,OAAe,EACf,SAAkB;QAElB,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE;oBACpC,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB;iBACrD,CAAC,CAAC;YACL,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE;oBACpC,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB;iBACrD,CAAC,CAAC;YACL,KAAK,KAAK;gBACR,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CACtB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,GAAG,EACV,OAAO,EACP,MAAM,CAAC,SAAS,EAChB,SAAS,CACV,CAAC;YACJ,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,UAAU,GAAU,MAAM,CAAC;gBACjC,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ export declare function resolveRemotePassword(password: string | undefined, defaultPassword: string | undefined): string | undefined;
2
+ export declare function resolveRemoteUser(user: string | undefined, defaultUser: string | undefined): string;
3
+ export declare function buildReadFileCommand(path: string, maxLines: number, offset?: number): string;
4
+ /** Read file with MCP_META prefix (total lines/bytes) before content. */
5
+ export declare function buildReadFileWithMetaCommand(path: string, maxLines: number, offset?: number): string;
6
+ export interface ReadFileMeta {
7
+ totalLines?: number;
8
+ totalBytes?: number;
9
+ }
10
+ export declare function parseReadFileMeta(output: string): {
11
+ meta: ReadFileMeta;
12
+ content: string;
13
+ };
14
+ export declare function buildTailFileWithMetaCommand(path: string, lines: number, grep?: string): string;
15
+ export declare function buildRemoteReadCommand(path: string, options: {
16
+ mode: "head" | "tail";
17
+ lines: number;
18
+ offset?: number;
19
+ grep?: string;
20
+ }): string;
21
+ export declare function buildTailFileCommand(path: string, lines: number, grep?: string): string;
22
+ export declare function buildPsCommand(pattern?: string): string;
@@ -0,0 +1,75 @@
1
+ export function resolveRemotePassword(password, defaultPassword) {
2
+ // Do not trim — passwords may intentionally include leading/trailing spaces.
3
+ if (password !== undefined && password !== "") {
4
+ return password;
5
+ }
6
+ if (defaultPassword !== undefined && defaultPassword !== "") {
7
+ return defaultPassword;
8
+ }
9
+ return undefined;
10
+ }
11
+ export function resolveRemoteUser(user, defaultUser) {
12
+ const resolved = user?.trim() || defaultUser?.trim();
13
+ if (!resolved) {
14
+ throw new Error('ssh_exec and host-target tools require "user" parameter or TARGET_USER environment variable. Do not assume root.');
15
+ }
16
+ return resolved;
17
+ }
18
+ export function buildReadFileCommand(path, maxLines, offset) {
19
+ const safePath = path.replace(/'/g, "'\\''");
20
+ if (offset && offset > 1) {
21
+ return `sed -n '${offset},${offset + maxLines - 1}p' '${safePath}' 2>/dev/null || head -n ${offset + maxLines - 1} '${safePath}' | tail -n ${maxLines}`;
22
+ }
23
+ return `head -n ${maxLines} '${safePath}'`;
24
+ }
25
+ /** Read file with MCP_META prefix (total lines/bytes) before content. */
26
+ export function buildReadFileWithMetaCommand(path, maxLines, offset) {
27
+ const safePath = path.replace(/'/g, "'\\''");
28
+ const meta = `echo "MCP_META lines=$(wc -l < '${safePath}' 2>/dev/null | tr -d ' ') bytes=$(wc -c < '${safePath}' 2>/dev/null | tr -d ' ')"`;
29
+ const read = buildReadFileCommand(path, maxLines, offset);
30
+ return `${meta} && ${read}`;
31
+ }
32
+ const META_PATTERN = /^MCP_META lines=(\d+) bytes=(\d+)/;
33
+ export function parseReadFileMeta(output) {
34
+ const lines = output.split("\n");
35
+ const first = lines[0] ?? "";
36
+ const match = first.match(META_PATTERN);
37
+ if (!match) {
38
+ return { meta: {}, content: output };
39
+ }
40
+ return {
41
+ meta: {
42
+ totalLines: Number.parseInt(match[1], 10),
43
+ totalBytes: Number.parseInt(match[2], 10),
44
+ },
45
+ content: lines.slice(1).join("\n"),
46
+ };
47
+ }
48
+ export function buildTailFileWithMetaCommand(path, lines, grep) {
49
+ const safePath = path.replace(/'/g, "'\\''");
50
+ const meta = `echo "MCP_META lines=$(wc -l < '${safePath}' 2>/dev/null | tr -d ' ') bytes=$(wc -c < '${safePath}' 2>/dev/null | tr -d ' ')"`;
51
+ const tail = buildTailFileCommand(path, lines, grep);
52
+ return `${meta} && ${tail}`;
53
+ }
54
+ export function buildRemoteReadCommand(path, options) {
55
+ if (options.mode === "tail") {
56
+ return buildTailFileWithMetaCommand(path, options.lines, options.grep);
57
+ }
58
+ return buildReadFileWithMetaCommand(path, options.lines, options.offset);
59
+ }
60
+ export function buildTailFileCommand(path, lines, grep) {
61
+ const safePath = path.replace(/'/g, "'\\''");
62
+ if (grep) {
63
+ const safeGrep = grep.replace(/'/g, "'\\''");
64
+ return `grep -i '${safeGrep}' '${safePath}' | tail -n ${lines}`;
65
+ }
66
+ return `tail -n ${lines} '${safePath}'`;
67
+ }
68
+ export function buildPsCommand(pattern) {
69
+ if (pattern) {
70
+ const safePattern = pattern.replace(/'/g, "'\\''");
71
+ return `ps aux | grep -F '${safePattern}' | grep -v grep || true`;
72
+ }
73
+ return "ps aux";
74
+ }
75
+ //# sourceMappingURL=remote-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-target.js","sourceRoot":"","sources":["../../src/utils/remote-target.ts"],"names":[],"mappings":"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"}