@crustdev/armordesk 1.0.4 → 1.0.6
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/dist/agents.d.ts +27 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/chunk-HWPT3W34.mjs +860 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +517 -4
- package/dist/cli.mjs +92 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +437 -0
- package/dist/index.mjs +11 -1
- package/package.json +1 -1
- package/dist/chunk-IKQRJ7XB.mjs +0 -428
package/dist/chunk-IKQRJ7XB.mjs
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
// src/crypto.ts
|
|
2
|
-
import { createHash } from "crypto";
|
|
3
|
-
function canonicalJson(data) {
|
|
4
|
-
if (data === null || data === void 0) {
|
|
5
|
-
return "null";
|
|
6
|
-
}
|
|
7
|
-
if (typeof data === "number" || typeof data === "boolean") {
|
|
8
|
-
return JSON.stringify(data);
|
|
9
|
-
}
|
|
10
|
-
if (typeof data === "string") {
|
|
11
|
-
return JSON.stringify(data);
|
|
12
|
-
}
|
|
13
|
-
if (Array.isArray(data)) {
|
|
14
|
-
return "[" + data.map((item) => canonicalJson(item)).join(",") + "]";
|
|
15
|
-
}
|
|
16
|
-
if (typeof data === "object") {
|
|
17
|
-
const keys = Object.keys(data).sort();
|
|
18
|
-
const entries = keys.map((k) => JSON.stringify(k) + ":" + canonicalJson(data[k]));
|
|
19
|
-
return "{" + entries.join(",") + "}";
|
|
20
|
-
}
|
|
21
|
-
return JSON.stringify(data);
|
|
22
|
-
}
|
|
23
|
-
function hashSha256(data) {
|
|
24
|
-
return createHash("sha256").update(data, "utf8").digest("hex");
|
|
25
|
-
}
|
|
26
|
-
function generatePlanHash(planDict) {
|
|
27
|
-
const canonical = canonicalJson(planDict);
|
|
28
|
-
return hashSha256(canonical);
|
|
29
|
-
}
|
|
30
|
-
function buildMerkleTree(leafItems) {
|
|
31
|
-
const leafHashes = leafItems.length === 0 ? [hashSha256("empty_tree_node")] : leafItems.map((item) => hashSha256(item));
|
|
32
|
-
let currentLevel = [...leafHashes];
|
|
33
|
-
while (currentLevel.length > 1) {
|
|
34
|
-
const nextLevel = [];
|
|
35
|
-
for (let i = 0; i < currentLevel.length; i += 2) {
|
|
36
|
-
const left = currentLevel[i];
|
|
37
|
-
const right = i + 1 < currentLevel.length ? currentLevel[i + 1] : left;
|
|
38
|
-
const combined = hashSha256(left + right);
|
|
39
|
-
nextLevel.push(combined);
|
|
40
|
-
}
|
|
41
|
-
currentLevel = nextLevel;
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
root: currentLevel[0],
|
|
45
|
-
leafHashes
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function verifyMerkleRoot(leafItems, expectedRoot) {
|
|
49
|
-
const { root } = buildMerkleTree(leafItems);
|
|
50
|
-
return root.toLowerCase() === expectedRoot.toLowerCase();
|
|
51
|
-
}
|
|
52
|
-
function verifyProof(request) {
|
|
53
|
-
const computedPlanHash = generatePlanHash(request.allowed_spec);
|
|
54
|
-
const matchesHash = computedPlanHash.toLowerCase() === request.plan_hash.toLowerCase();
|
|
55
|
-
const executionItems = Object.entries(request.actual_execution).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${canonicalJson(v)}`);
|
|
56
|
-
const { root: computedMerkleRoot } = buildMerkleTree(executionItems);
|
|
57
|
-
const matchesMerkle = computedMerkleRoot.toLowerCase() === request.merkle_root.toLowerCase();
|
|
58
|
-
const valid = matchesHash && matchesMerkle;
|
|
59
|
-
return {
|
|
60
|
-
valid,
|
|
61
|
-
computed_plan_hash: computedPlanHash,
|
|
62
|
-
computed_merkle_root: computedMerkleRoot,
|
|
63
|
-
matches_hash: matchesHash,
|
|
64
|
-
matches_merkle: matchesMerkle,
|
|
65
|
-
status: valid ? "CRYPTOGRAPHICALLY_VERIFIED" : "VERIFICATION_FAILED",
|
|
66
|
-
details: valid ? "Plan hash matches allowed specification and Merkle root confirms un-tampered execution proof." : `Discrepancy detected: hashMatch=${matchesHash}, merkleMatch=${matchesMerkle}`
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// src/config.ts
|
|
71
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
|
|
72
|
-
import { homedir } from "os";
|
|
73
|
-
import { join } from "path";
|
|
74
|
-
function getConfigDir() {
|
|
75
|
-
return join(homedir(), ".armordesk");
|
|
76
|
-
}
|
|
77
|
-
function getConfigPath() {
|
|
78
|
-
return join(getConfigDir(), "config.json");
|
|
79
|
-
}
|
|
80
|
-
function loadStoredConfig() {
|
|
81
|
-
try {
|
|
82
|
-
const configPath = getConfigPath();
|
|
83
|
-
if (existsSync(configPath)) {
|
|
84
|
-
const raw = readFileSync(configPath, "utf8");
|
|
85
|
-
return JSON.parse(raw);
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
}
|
|
89
|
-
return {};
|
|
90
|
-
}
|
|
91
|
-
function saveStoredConfig(config) {
|
|
92
|
-
try {
|
|
93
|
-
const dir = getConfigDir();
|
|
94
|
-
if (!existsSync(dir)) {
|
|
95
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
96
|
-
}
|
|
97
|
-
const configPath = getConfigPath();
|
|
98
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
99
|
-
encoding: "utf8",
|
|
100
|
-
mode: 384
|
|
101
|
-
});
|
|
102
|
-
} catch (err) {
|
|
103
|
-
console.warn(`[ArmorDesk] Could not write config to ${getConfigPath()}: ${err.message}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function clearStoredConfig() {
|
|
107
|
-
try {
|
|
108
|
-
const configPath = getConfigPath();
|
|
109
|
-
if (existsSync(configPath)) {
|
|
110
|
-
unlinkSync(configPath);
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
}
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// src/client.ts
|
|
119
|
-
var ArmorDeskClient = class {
|
|
120
|
-
baseUrl;
|
|
121
|
-
apiKey;
|
|
122
|
-
tenantId;
|
|
123
|
-
userName;
|
|
124
|
-
userRole;
|
|
125
|
-
token;
|
|
126
|
-
_fetch;
|
|
127
|
-
crypto = {
|
|
128
|
-
canonicalJson,
|
|
129
|
-
generatePlanHash,
|
|
130
|
-
buildMerkleTree,
|
|
131
|
-
verifyMerkleRoot,
|
|
132
|
-
verifyProof
|
|
133
|
-
};
|
|
134
|
-
constructor(options = {}) {
|
|
135
|
-
const stored = typeof process !== "undefined" ? loadStoredConfig() : {};
|
|
136
|
-
this.baseUrl = (options.baseUrl || typeof process !== "undefined" && process.env?.ARMORDESK_URL || stored.baseUrl || "https://armordesk.crusts.dev").replace(/\/$/, "");
|
|
137
|
-
this.apiKey = options.apiKey || (typeof process !== "undefined" ? process.env?.ARMORIQ_API_KEY || process.env?.ARMORIQ_PRO_API_KEY : void 0) || stored.apiKey;
|
|
138
|
-
this.token = stored.token;
|
|
139
|
-
this.tenantId = options.tenantId || stored.tenant?.id || "acme-corp";
|
|
140
|
-
this.userName = options.userName || stored.user?.name || "Sarah Chen";
|
|
141
|
-
this.userRole = options.userRole || stored.user?.role || "manager";
|
|
142
|
-
this._fetch = options.fetch || globalThis.fetch.bind(globalThis);
|
|
143
|
-
}
|
|
144
|
-
async request(endpoint, options = {}) {
|
|
145
|
-
const url = `${this.baseUrl}${endpoint.startsWith("/") ? endpoint : "/" + endpoint}`;
|
|
146
|
-
const headers = {
|
|
147
|
-
"Content-Type": "application/json",
|
|
148
|
-
"X-ArmorIQ-Tenant": this.tenantId,
|
|
149
|
-
...options.headers
|
|
150
|
-
};
|
|
151
|
-
if (this.token) {
|
|
152
|
-
headers["Authorization"] = `Bearer ${this.token}`;
|
|
153
|
-
} else if (this.apiKey) {
|
|
154
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
155
|
-
headers["X-ArmorIQ-API-Key"] = this.apiKey;
|
|
156
|
-
}
|
|
157
|
-
const res = await this._fetch(url, {
|
|
158
|
-
...options,
|
|
159
|
-
headers
|
|
160
|
-
});
|
|
161
|
-
if (!res.ok) {
|
|
162
|
-
let errorBody = "";
|
|
163
|
-
try {
|
|
164
|
-
errorBody = await res.text();
|
|
165
|
-
} catch {
|
|
166
|
-
}
|
|
167
|
-
throw new Error(
|
|
168
|
-
`ArmorDesk API error [${res.status} ${res.statusText}] at ${endpoint}: ${errorBody || "Unknown error"}`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
return res.json();
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Authenticate with an ArmorDesk account and persist credentials
|
|
175
|
-
*/
|
|
176
|
-
async login(email, password = "admin", tenantId) {
|
|
177
|
-
const res = await this.request("/api/auth/login", {
|
|
178
|
-
method: "POST",
|
|
179
|
-
body: JSON.stringify({
|
|
180
|
-
email,
|
|
181
|
-
password,
|
|
182
|
-
tenant_id: tenantId || this.tenantId
|
|
183
|
-
})
|
|
184
|
-
});
|
|
185
|
-
if (res.user && res.token) {
|
|
186
|
-
this.token = res.token;
|
|
187
|
-
this.userName = res.user.name;
|
|
188
|
-
this.userRole = res.user.role;
|
|
189
|
-
if (res.tenant?.id) {
|
|
190
|
-
this.tenantId = res.tenant.id;
|
|
191
|
-
}
|
|
192
|
-
if (typeof process !== "undefined") {
|
|
193
|
-
saveStoredConfig({
|
|
194
|
-
baseUrl: this.baseUrl,
|
|
195
|
-
token: res.token,
|
|
196
|
-
user: res.user,
|
|
197
|
-
tenant: res.tenant,
|
|
198
|
-
apiKey: this.apiKey,
|
|
199
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return res;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Request OAuth 2.0 Device Code for terminal/browser authentication (RFC 8628)
|
|
207
|
-
*/
|
|
208
|
-
async requestDeviceCode() {
|
|
209
|
-
return this.request("/api/auth/device/code", {
|
|
210
|
-
method: "POST"
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Poll OAuth 2.0 Device Code until authorized by user in browser
|
|
215
|
-
*/
|
|
216
|
-
async pollDeviceCode(deviceCode) {
|
|
217
|
-
return this.request("/api/auth/device/poll", {
|
|
218
|
-
method: "POST",
|
|
219
|
-
body: JSON.stringify({ device_code: deviceCode })
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Check live status and ping telemetry of Governed MCP Gateway
|
|
224
|
-
*/
|
|
225
|
-
async getMCPStatus() {
|
|
226
|
-
return this.request("/api/mcp/ping", {
|
|
227
|
-
method: "POST"
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Comprehensive MCP diagnostic: measures latency, pings gateway, and tests tool readiness
|
|
232
|
-
*/
|
|
233
|
-
async checkMCP(tenantId) {
|
|
234
|
-
const start = Date.now();
|
|
235
|
-
const ping = await this.getMCPStatus();
|
|
236
|
-
const latencyMs = Date.now() - start;
|
|
237
|
-
const config = await this.getMCPConfig(tenantId || this.tenantId);
|
|
238
|
-
const connected = ping.status === "CONNECTED" && config.governed_tools.length > 0;
|
|
239
|
-
return {
|
|
240
|
-
connected,
|
|
241
|
-
latencyMs,
|
|
242
|
-
ping,
|
|
243
|
-
config
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Health and runtime operational telemetry
|
|
248
|
-
*/
|
|
249
|
-
async status() {
|
|
250
|
-
return this.request("/api/status");
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* List managed corporate tenants
|
|
254
|
-
*/
|
|
255
|
-
async getTenants() {
|
|
256
|
-
return this.request("/api/tenants");
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Fetch immutable security and policy enforcement traces
|
|
260
|
-
*/
|
|
261
|
-
async getTraces(tenantId) {
|
|
262
|
-
const tid = tenantId || this.tenantId;
|
|
263
|
-
return this.request(`/api/traces?tenant_id=${encodeURIComponent(tid)}`);
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Fetch approval queue items held by human-in-the-loop policies
|
|
267
|
-
*/
|
|
268
|
-
async getApprovals(tenantId) {
|
|
269
|
-
const tid = tenantId || this.tenantId;
|
|
270
|
-
return this.request(`/api/approvals?tenant_id=${encodeURIComponent(tid)}`);
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Authorize or reject a held action in escrow
|
|
274
|
-
*/
|
|
275
|
-
async resolveApproval(delegationId, action, notes) {
|
|
276
|
-
const payload = {
|
|
277
|
-
action,
|
|
278
|
-
user_role: this.userRole,
|
|
279
|
-
user_name: this.userName,
|
|
280
|
-
notes
|
|
281
|
-
};
|
|
282
|
-
return this.request(`/api/approvals/${encodeURIComponent(delegationId)}/action`, {
|
|
283
|
-
method: "POST",
|
|
284
|
-
body: JSON.stringify(payload)
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* List digital verification receipts with cryptographic proofs
|
|
289
|
-
*/
|
|
290
|
-
async getReceipts(tenantId) {
|
|
291
|
-
const tid = tenantId || this.tenantId;
|
|
292
|
-
return this.request(`/api/receipts?tenant_id=${encodeURIComponent(tid)}`);
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Retrieve a specific receipt by ID
|
|
296
|
-
*/
|
|
297
|
-
async getReceipt(receiptId) {
|
|
298
|
-
return this.request(`/api/receipts/${encodeURIComponent(receiptId)}`);
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Cryptographically verify a digital proof
|
|
302
|
-
*/
|
|
303
|
-
async verifyProof(proof) {
|
|
304
|
-
return this.request("/api/receipts/verify", {
|
|
305
|
-
method: "POST",
|
|
306
|
-
body: JSON.stringify(proof)
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Get MCP Gateway configuration and sample connection specs
|
|
311
|
-
*/
|
|
312
|
-
async getMCPConfig(tenantId) {
|
|
313
|
-
const tid = tenantId || this.tenantId;
|
|
314
|
-
return this.request(`/api/mcp/config?tenant_id=${encodeURIComponent(tid)}`);
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Invoke a tool through the Governed MCP Gateway
|
|
318
|
-
*/
|
|
319
|
-
async callMCPTool(toolName, args, options = {}) {
|
|
320
|
-
const rpcPayload = {
|
|
321
|
-
jsonrpc: "2.0",
|
|
322
|
-
id: Date.now(),
|
|
323
|
-
method: "tools/call",
|
|
324
|
-
params: {
|
|
325
|
-
name: toolName,
|
|
326
|
-
arguments: args,
|
|
327
|
-
context: {
|
|
328
|
-
user_role: options.userRole || this.userRole,
|
|
329
|
-
user_name: options.userName || this.userName,
|
|
330
|
-
delegation_id: options.delegationId
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
return this.request("/api/mcp/gateway", {
|
|
335
|
-
method: "POST",
|
|
336
|
-
body: JSON.stringify(rpcPayload)
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Execute an adversarial FinOps scenario simulation (1 to 5)
|
|
341
|
-
*/
|
|
342
|
-
async runScenario(scenarioId, tenantId) {
|
|
343
|
-
return this.request("/api/scenarios/run", {
|
|
344
|
-
method: "POST",
|
|
345
|
-
body: JSON.stringify({
|
|
346
|
-
scenario_id: scenarioId,
|
|
347
|
-
tenant_id: tenantId || this.tenantId
|
|
348
|
-
})
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Test prompt injection attacks against ArmorDesk CTF Gauntlet
|
|
353
|
-
*/
|
|
354
|
-
async submitCTF(prompt, attackerName, tenantId) {
|
|
355
|
-
return this.request("/api/ctf/submit", {
|
|
356
|
-
method: "POST",
|
|
357
|
-
body: JSON.stringify({
|
|
358
|
-
prompt,
|
|
359
|
-
attacker_name: attackerName || "PenTester",
|
|
360
|
-
tenant_id: tenantId || this.tenantId
|
|
361
|
-
})
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Generate an executive voice briefing via ElevenLabs
|
|
366
|
-
*/
|
|
367
|
-
async getVoiceBriefing(briefingType = "executive", tenantId) {
|
|
368
|
-
return this.request("/api/voice/briefing", {
|
|
369
|
-
method: "POST",
|
|
370
|
-
body: JSON.stringify({
|
|
371
|
-
briefing_type: briefingType,
|
|
372
|
-
tenant_id: tenantId || this.tenantId
|
|
373
|
-
})
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
// src/mcp.ts
|
|
379
|
-
function generateClaudeDesktopConfig(options = {}) {
|
|
380
|
-
const url = options.gatewayUrl || "https://armordesk.crusts.dev/api/mcp/gateway";
|
|
381
|
-
const tenant = options.tenantId || "acme-corp";
|
|
382
|
-
const apiKey = options.apiKey || "ak_live_acme_corp";
|
|
383
|
-
return {
|
|
384
|
-
mcpServers: {
|
|
385
|
-
armordesk_gateway: {
|
|
386
|
-
command: "npx",
|
|
387
|
-
args: ["-y", "@modelcontextprotocol/server-fetch", url],
|
|
388
|
-
env: {
|
|
389
|
-
ARMORIQ_TENANT: tenant,
|
|
390
|
-
ARMORIQ_API_KEY: apiKey
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
function generateCursorConfig(options = {}) {
|
|
397
|
-
const url = options.gatewayUrl || "https://armordesk.crusts.dev/api/mcp/gateway";
|
|
398
|
-
const tenant = options.tenantId || "acme-corp";
|
|
399
|
-
const apiKey = options.apiKey || "ak_live_acme_corp";
|
|
400
|
-
return {
|
|
401
|
-
mcpServers: {
|
|
402
|
-
armordesk: {
|
|
403
|
-
url,
|
|
404
|
-
headers: {
|
|
405
|
-
"X-ArmorIQ-Tenant": tenant,
|
|
406
|
-
Authorization: `Bearer ${apiKey}`
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export {
|
|
414
|
-
canonicalJson,
|
|
415
|
-
hashSha256,
|
|
416
|
-
generatePlanHash,
|
|
417
|
-
buildMerkleTree,
|
|
418
|
-
verifyMerkleRoot,
|
|
419
|
-
verifyProof,
|
|
420
|
-
getConfigDir,
|
|
421
|
-
getConfigPath,
|
|
422
|
-
loadStoredConfig,
|
|
423
|
-
saveStoredConfig,
|
|
424
|
-
clearStoredConfig,
|
|
425
|
-
ArmorDeskClient,
|
|
426
|
-
generateClaudeDesktopConfig,
|
|
427
|
-
generateCursorConfig
|
|
428
|
-
};
|