aws-runtime-bridge 1.0.1 → 1.0.2
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.md +2 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -37
- package/dist/index.js +33 -27
- package/dist/middleware/auth.d.ts +4 -2
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +36 -6
- package/dist/routes/runtime-binding.d.ts +3 -0
- package/dist/routes/runtime-binding.d.ts.map +1 -0
- package/dist/routes/runtime-binding.js +147 -0
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +2 -7
- package/dist/routes/terminal.test.js +0 -4
- package/dist/services/auto-register.d.ts +6 -0
- package/dist/services/auto-register.d.ts.map +1 -1
- package/dist/services/auto-register.js +123 -43
- package/dist/services/aws-client-agent-mcp.d.ts +1 -1
- package/dist/services/aws-client-agent-mcp.d.ts.map +1 -1
- package/dist/services/aws-client-agent-mcp.js +81 -49
- package/dist/services/runtime-binding.d.ts +32 -0
- package/dist/services/runtime-binding.d.ts.map +1 -0
- package/dist/services/runtime-binding.js +131 -0
- package/package.json +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getRuntimeHomeDir } from "../config.js";
|
|
5
|
+
import { createLogger } from "../utils/logger.js";
|
|
6
|
+
const log = createLogger("runtime-binding");
|
|
7
|
+
const TOKEN_HASH_ALGORITHM = "sha256";
|
|
8
|
+
const pairingCode = generatePairingCode();
|
|
9
|
+
function generatePairingCode() {
|
|
10
|
+
const value = crypto.randomInt(0, 1_000_000);
|
|
11
|
+
return String(value)
|
|
12
|
+
.padStart(6, "0")
|
|
13
|
+
.replace(/(\d{3})(\d{3})/, "$1-$2");
|
|
14
|
+
}
|
|
15
|
+
function bindingDir() {
|
|
16
|
+
return path.join(getRuntimeHomeDir(), ".agentswork", "runtime-bridge");
|
|
17
|
+
}
|
|
18
|
+
function bindingFile() {
|
|
19
|
+
return path.join(bindingDir(), "binding.json");
|
|
20
|
+
}
|
|
21
|
+
function hashToken(token) {
|
|
22
|
+
return crypto
|
|
23
|
+
.createHash(TOKEN_HASH_ALGORITHM)
|
|
24
|
+
.update(token, "utf8")
|
|
25
|
+
.digest("hex");
|
|
26
|
+
}
|
|
27
|
+
function safeEqual(a, b) {
|
|
28
|
+
const left = Buffer.from(a, "hex");
|
|
29
|
+
const right = Buffer.from(b, "hex");
|
|
30
|
+
if (left.length !== right.length) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return crypto.timingSafeEqual(left, right);
|
|
34
|
+
}
|
|
35
|
+
function normalizeToken(token) {
|
|
36
|
+
return String(token || "").trim();
|
|
37
|
+
}
|
|
38
|
+
export function getRuntimePairingCode() {
|
|
39
|
+
return pairingCode;
|
|
40
|
+
}
|
|
41
|
+
export function getRuntimeBindingFilePath() {
|
|
42
|
+
return bindingFile();
|
|
43
|
+
}
|
|
44
|
+
export function loadRuntimeBinding() {
|
|
45
|
+
try {
|
|
46
|
+
const raw = fs.readFileSync(bindingFile(), "utf8");
|
|
47
|
+
const parsed = JSON.parse(raw);
|
|
48
|
+
if (parsed?.status === "paired" && parsed.tokenHash) {
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Missing or invalid binding file means the bridge is unpaired.
|
|
54
|
+
}
|
|
55
|
+
return { status: "unpaired" };
|
|
56
|
+
}
|
|
57
|
+
export function getRuntimeBindingPublicState() {
|
|
58
|
+
const state = loadRuntimeBinding();
|
|
59
|
+
const { tokenHash: _tokenHash, accessToken: _accessToken, ...publicState } = state;
|
|
60
|
+
return {
|
|
61
|
+
...publicState,
|
|
62
|
+
paired: state.status === "paired",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function hasRuntimeBinding() {
|
|
66
|
+
const state = loadRuntimeBinding();
|
|
67
|
+
return state.status === "paired" && Boolean(state.tokenHash);
|
|
68
|
+
}
|
|
69
|
+
export function validateRuntimeBindingToken(token) {
|
|
70
|
+
const candidate = normalizeToken(token);
|
|
71
|
+
if (!candidate) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const state = loadRuntimeBinding();
|
|
75
|
+
if (state.status !== "paired" || !state.tokenHash) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return safeEqual(hashToken(candidate), state.tokenHash);
|
|
79
|
+
}
|
|
80
|
+
export function getRuntimeAccessToken() {
|
|
81
|
+
const state = loadRuntimeBinding();
|
|
82
|
+
if (state.status !== "paired") {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
return normalizeToken(state.accessToken) || undefined;
|
|
86
|
+
}
|
|
87
|
+
export function validateRuntimePairingCode(code) {
|
|
88
|
+
return String(code || "").trim() === pairingCode;
|
|
89
|
+
}
|
|
90
|
+
export function saveRuntimeBinding(input) {
|
|
91
|
+
const accessToken = normalizeToken(input.accessToken);
|
|
92
|
+
if (accessToken.length < 16) {
|
|
93
|
+
throw new Error("accessToken must be at least 16 characters");
|
|
94
|
+
}
|
|
95
|
+
const previous = loadRuntimeBinding();
|
|
96
|
+
const now = new Date().toISOString();
|
|
97
|
+
const next = {
|
|
98
|
+
status: "paired",
|
|
99
|
+
instanceId: input.instanceId
|
|
100
|
+
? String(input.instanceId)
|
|
101
|
+
: previous.instanceId,
|
|
102
|
+
schedulerBaseUrl: input.schedulerBaseUrl
|
|
103
|
+
? String(input.schedulerBaseUrl)
|
|
104
|
+
: previous.schedulerBaseUrl,
|
|
105
|
+
accessToken,
|
|
106
|
+
tokenHash: hashToken(accessToken),
|
|
107
|
+
createdAt: previous.createdAt || now,
|
|
108
|
+
updatedAt: now,
|
|
109
|
+
};
|
|
110
|
+
fs.mkdirSync(bindingDir(), { recursive: true });
|
|
111
|
+
fs.writeFileSync(bindingFile(), `${JSON.stringify(next, null, 2)}\n`, {
|
|
112
|
+
encoding: "utf8",
|
|
113
|
+
mode: 0o600,
|
|
114
|
+
});
|
|
115
|
+
log.info(`[runtime-bridge] runtime binding saved: ${bindingFile()}`);
|
|
116
|
+
return next;
|
|
117
|
+
}
|
|
118
|
+
export function clearRuntimeBinding() {
|
|
119
|
+
const current = loadRuntimeBinding();
|
|
120
|
+
const revokedState = {
|
|
121
|
+
...current,
|
|
122
|
+
status: "unpaired",
|
|
123
|
+
accessToken: undefined,
|
|
124
|
+
tokenHash: undefined,
|
|
125
|
+
revokedAt: new Date().toISOString(),
|
|
126
|
+
updatedAt: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
fs.mkdirSync(bindingDir(), { recursive: true });
|
|
129
|
+
fs.writeFileSync(bindingFile(), `${JSON.stringify(revokedState, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
130
|
+
log.info("[runtime-bridge] runtime binding cleared");
|
|
131
|
+
}
|