secretless-ai 0.8.2 → 0.9.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.
- package/dist/broker/aim-client.d.ts +39 -0
- package/dist/broker/aim-client.d.ts.map +1 -0
- package/dist/broker/aim-client.js +165 -0
- package/dist/broker/aim-client.js.map +1 -0
- package/dist/broker/audit.d.ts +35 -0
- package/dist/broker/audit.d.ts.map +1 -0
- package/dist/broker/audit.js +166 -0
- package/dist/broker/audit.js.map +1 -0
- package/dist/broker/daemon.d.ts +48 -0
- package/dist/broker/daemon.d.ts.map +1 -0
- package/dist/broker/daemon.js +230 -0
- package/dist/broker/daemon.js.map +1 -0
- package/dist/broker/index.d.ts +9 -0
- package/dist/broker/index.d.ts.map +1 -0
- package/dist/broker/index.js +23 -0
- package/dist/broker/index.js.map +1 -0
- package/dist/broker/policy.d.ts +60 -0
- package/dist/broker/policy.d.ts.map +1 -0
- package/dist/broker/policy.js +295 -0
- package/dist/broker/policy.js.map +1 -0
- package/dist/broker/rate-limiter.d.ts +36 -0
- package/dist/broker/rate-limiter.d.ts.map +1 -0
- package/dist/broker/rate-limiter.js +92 -0
- package/dist/broker/rate-limiter.js.map +1 -0
- package/dist/broker/resolver.d.ts +42 -0
- package/dist/broker/resolver.d.ts.map +1 -0
- package/dist/broker/resolver.js +75 -0
- package/dist/broker/resolver.js.map +1 -0
- package/dist/broker/server.d.ts +58 -0
- package/dist/broker/server.d.ts.map +1 -0
- package/dist/broker/server.js +304 -0
- package/dist/broker/server.js.map +1 -0
- package/dist/broker/types.d.ts +117 -0
- package/dist/broker/types.d.ts.map +1 -0
- package/dist/broker/types.js +9 -0
- package/dist/broker/types.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -1
- package/dist/env.d.ts +36 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +50 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AIM integration client — verifies agent identity via AIM REST API.
|
|
3
|
+
*
|
|
4
|
+
* Checks agent capabilities, trust scores, and verification status against
|
|
5
|
+
* the AIM server. Results are cached for 60 seconds to reduce API calls.
|
|
6
|
+
* Fails gracefully if AIM is unavailable (returns undefined, not errors).
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentIdentity } from './types';
|
|
9
|
+
export declare class AimClient {
|
|
10
|
+
private readonly baseUrl;
|
|
11
|
+
private readonly cache;
|
|
12
|
+
private readonly cacheTtlMs;
|
|
13
|
+
constructor(baseUrl: string, options?: {
|
|
14
|
+
cacheTtlMs?: number;
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Fetch agent identity from AIM.
|
|
18
|
+
* Returns cached result if available and not expired.
|
|
19
|
+
* Returns undefined if AIM is unreachable or the agent is not found.
|
|
20
|
+
*/
|
|
21
|
+
getAgentIdentity(agentId: string): Promise<AgentIdentity | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* Check if the AIM server is reachable.
|
|
24
|
+
*/
|
|
25
|
+
isAvailable(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Clear the identity cache.
|
|
28
|
+
*/
|
|
29
|
+
clearCache(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Evict a specific agent from the cache.
|
|
32
|
+
*/
|
|
33
|
+
evict(agentId: string): void;
|
|
34
|
+
/** Get the number of cached entries (for testing/status). */
|
|
35
|
+
get cacheSize(): number;
|
|
36
|
+
/** Simple HTTP GET returning parsed JSON body, or undefined on error. */
|
|
37
|
+
private httpGet;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=aim-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aim-client.d.ts","sourceRoot":"","sources":["../../src/broker/aim-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAc7C,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAM9D;;;;OAIG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAkC3E;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAUrC;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,6DAA6D;IAC7D,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,yEAAyE;IACzE,OAAO,CAAC,OAAO;CA+BhB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AIM integration client — verifies agent identity via AIM REST API.
|
|
4
|
+
*
|
|
5
|
+
* Checks agent capabilities, trust scores, and verification status against
|
|
6
|
+
* the AIM server. Results are cached for 60 seconds to reduce API calls.
|
|
7
|
+
* Fails gracefully if AIM is unavailable (returns undefined, not errors).
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.AimClient = void 0;
|
|
44
|
+
const http = __importStar(require("http"));
|
|
45
|
+
const https = __importStar(require("https"));
|
|
46
|
+
/** Default cache TTL in milliseconds (60 seconds). */
|
|
47
|
+
const DEFAULT_CACHE_TTL_MS = 60000;
|
|
48
|
+
/** Request timeout in milliseconds. */
|
|
49
|
+
const REQUEST_TIMEOUT_MS = 5000;
|
|
50
|
+
class AimClient {
|
|
51
|
+
constructor(baseUrl, options) {
|
|
52
|
+
this.cache = new Map();
|
|
53
|
+
// Remove trailing slash
|
|
54
|
+
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
55
|
+
this.cacheTtlMs = options?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Fetch agent identity from AIM.
|
|
59
|
+
* Returns cached result if available and not expired.
|
|
60
|
+
* Returns undefined if AIM is unreachable or the agent is not found.
|
|
61
|
+
*/
|
|
62
|
+
async getAgentIdentity(agentId) {
|
|
63
|
+
// Check cache first
|
|
64
|
+
const cached = this.cache.get(agentId);
|
|
65
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
66
|
+
return cached.identity;
|
|
67
|
+
}
|
|
68
|
+
// Evict expired entry
|
|
69
|
+
if (cached) {
|
|
70
|
+
this.cache.delete(agentId);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const url = `${this.baseUrl}/api/v1/agents/${encodeURIComponent(agentId)}`;
|
|
74
|
+
const response = await this.httpGet(url);
|
|
75
|
+
if (!response)
|
|
76
|
+
return undefined;
|
|
77
|
+
const identity = parseAgentResponse(response);
|
|
78
|
+
if (!identity)
|
|
79
|
+
return undefined;
|
|
80
|
+
// Cache the result
|
|
81
|
+
this.cache.set(agentId, {
|
|
82
|
+
identity,
|
|
83
|
+
expiresAt: Date.now() + this.cacheTtlMs,
|
|
84
|
+
});
|
|
85
|
+
return identity;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// AIM unavailable — fail gracefully
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if the AIM server is reachable.
|
|
94
|
+
*/
|
|
95
|
+
async isAvailable() {
|
|
96
|
+
try {
|
|
97
|
+
const url = `${this.baseUrl}/health`;
|
|
98
|
+
const response = await this.httpGet(url);
|
|
99
|
+
return response !== undefined;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Clear the identity cache.
|
|
107
|
+
*/
|
|
108
|
+
clearCache() {
|
|
109
|
+
this.cache.clear();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Evict a specific agent from the cache.
|
|
113
|
+
*/
|
|
114
|
+
evict(agentId) {
|
|
115
|
+
this.cache.delete(agentId);
|
|
116
|
+
}
|
|
117
|
+
/** Get the number of cached entries (for testing/status). */
|
|
118
|
+
get cacheSize() {
|
|
119
|
+
return this.cache.size;
|
|
120
|
+
}
|
|
121
|
+
/** Simple HTTP GET returning parsed JSON body, or undefined on error. */
|
|
122
|
+
httpGet(url) {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
const parsedUrl = new URL(url);
|
|
125
|
+
const transport = parsedUrl.protocol === 'https:' ? https : http;
|
|
126
|
+
const req = transport.get(url, { timeout: REQUEST_TIMEOUT_MS }, (res) => {
|
|
127
|
+
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
|
128
|
+
res.resume(); // Drain response
|
|
129
|
+
resolve(undefined);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const chunks = [];
|
|
133
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
134
|
+
res.on('end', () => {
|
|
135
|
+
try {
|
|
136
|
+
const body = Buffer.concat(chunks).toString('utf-8');
|
|
137
|
+
resolve(JSON.parse(body));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
resolve(undefined);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
req.on('error', () => resolve(undefined));
|
|
145
|
+
req.on('timeout', () => {
|
|
146
|
+
req.destroy();
|
|
147
|
+
resolve(undefined);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.AimClient = AimClient;
|
|
153
|
+
/** Parse an AIM API agent response into an AgentIdentity. */
|
|
154
|
+
function parseAgentResponse(data) {
|
|
155
|
+
const agentId = typeof data.agentId === 'string' ? data.agentId : undefined;
|
|
156
|
+
if (!agentId)
|
|
157
|
+
return undefined;
|
|
158
|
+
const trustScore = typeof data.trustScore === 'number' ? data.trustScore : 0;
|
|
159
|
+
const capabilities = Array.isArray(data.capabilities)
|
|
160
|
+
? data.capabilities.filter((c) => typeof c === 'string')
|
|
161
|
+
: [];
|
|
162
|
+
const verified = typeof data.verified === 'boolean' ? data.verified : false;
|
|
163
|
+
return { agentId, trustScore, capabilities, verified };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=aim-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aim-client.js","sourceRoot":"","sources":["../../src/broker/aim-client.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,2CAA6B;AAC7B,6CAA+B;AAS/B,sDAAsD;AACtD,MAAM,oBAAoB,GAAG,KAAM,CAAC;AAEpC,uCAAuC;AACvC,MAAM,kBAAkB,GAAG,IAAK,CAAC;AAEjC,MAAa,SAAS;IAKpB,YAAY,OAAe,EAAE,OAAiC;QAH7C,UAAK,GAA4B,IAAI,GAAG,EAAE,CAAC;QAI1D,wBAAwB;QACxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,oBAAoB,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,gBAAgB,CAAC,OAAe;QACpC,oBAAoB;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC5C,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,CAAC;QAED,sBAAsB;QACtB,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,kBAAkB,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3E,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAEzC,IAAI,CAAC,QAAQ;gBAAE,OAAO,SAAS,CAAC;YAEhC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,QAAQ;gBAAE,OAAO,SAAS,CAAC;YAEhC,mBAAmB;YACnB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE;gBACtB,QAAQ;gBACR,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU;aACxC,CAAC,CAAC;YAEH,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;YACpC,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,SAAS,CAAC;YACrC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,QAAQ,KAAK,SAAS,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe;QACnB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,6DAA6D;IAC7D,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,yEAAyE;IACjE,OAAO,CAAC,GAAW;QACzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAEjE,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;gBACtE,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;oBACrE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,iBAAiB;oBAC/B,OAAO,CAAC,SAAS,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;gBACtD,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;wBACrD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC5B,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,CAAC,SAAS,CAAC,CAAC;oBACrB,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;YAC1C,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACrB,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAlHD,8BAkHC;AAED,6DAA6D;AAC7D,SAAS,kBAAkB,CAAC,IAA6B;IACvD,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC;QACnD,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QACrE,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,QAAQ,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;IAE5E,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit logger — append-only JSONL audit trail for broker operations.
|
|
3
|
+
*
|
|
4
|
+
* Writes structured audit entries to ~/.secretless-ai/broker-audit.log in
|
|
5
|
+
* JSON Lines format. Supports automatic log rotation at 50MB with 5 retained
|
|
6
|
+
* files. NEVER logs credential values.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuditEntry } from './types';
|
|
9
|
+
export declare class AuditLogger {
|
|
10
|
+
private readonly logPath;
|
|
11
|
+
private fd;
|
|
12
|
+
constructor(logPath?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Write an audit entry to the log file.
|
|
15
|
+
* Creates the log directory and file if they do not exist.
|
|
16
|
+
*/
|
|
17
|
+
log(entry: AuditEntry): void;
|
|
18
|
+
/**
|
|
19
|
+
* Create an audit entry from individual fields.
|
|
20
|
+
* Convenience method that populates the timestamp automatically.
|
|
21
|
+
*/
|
|
22
|
+
logEvent(eventType: string, agentId: string, credentialName: string, policyId: string, result: 'allowed' | 'denied', reason: string, latencyMs: number): void;
|
|
23
|
+
/**
|
|
24
|
+
* Read recent audit entries (last N lines).
|
|
25
|
+
* Returns entries in chronological order (oldest first).
|
|
26
|
+
*/
|
|
27
|
+
readRecent(count: number): AuditEntry[];
|
|
28
|
+
/**
|
|
29
|
+
* Close the audit logger and release file handles.
|
|
30
|
+
*/
|
|
31
|
+
close(): void;
|
|
32
|
+
/** Rotate log file if it exceeds the size limit. */
|
|
33
|
+
private rotateIfNeeded;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/broker/audit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAU1C,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,EAAE,CAAuB;gBAErB,OAAO,CAAC,EAAE,MAAM;IAI5B;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAa5B;;;OAGG;IACH,QAAQ,CACN,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,QAAQ,EAC5B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,IAAI;IAaP;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE;IAoBvC;;OAEG;IACH,KAAK,IAAI,IAAI;IAWb,oDAAoD;IACpD,OAAO,CAAC,cAAc;CAmCvB"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Audit logger — append-only JSONL audit trail for broker operations.
|
|
4
|
+
*
|
|
5
|
+
* Writes structured audit entries to ~/.secretless-ai/broker-audit.log in
|
|
6
|
+
* JSON Lines format. Supports automatic log rotation at 50MB with 5 retained
|
|
7
|
+
* files. NEVER logs credential values.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.AuditLogger = void 0;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
47
|
+
const DEFAULT_AUDIT_LOG = path.join(os.homedir(), '.secretless-ai', 'broker-audit.log');
|
|
48
|
+
/** Maximum log file size before rotation (50 MB). */
|
|
49
|
+
const MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
50
|
+
/** Number of rotated log files to retain. */
|
|
51
|
+
const MAX_RETAINED = 5;
|
|
52
|
+
class AuditLogger {
|
|
53
|
+
constructor(logPath) {
|
|
54
|
+
this.fd = null;
|
|
55
|
+
this.logPath = logPath ?? DEFAULT_AUDIT_LOG;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Write an audit entry to the log file.
|
|
59
|
+
* Creates the log directory and file if they do not exist.
|
|
60
|
+
*/
|
|
61
|
+
log(entry) {
|
|
62
|
+
const dir = path.dirname(this.logPath);
|
|
63
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
64
|
+
// Check rotation before writing
|
|
65
|
+
this.rotateIfNeeded();
|
|
66
|
+
const line = JSON.stringify(entry) + '\n';
|
|
67
|
+
// Append-only write with restrictive permissions
|
|
68
|
+
fs.appendFileSync(this.logPath, line, { mode: 0o600 });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create an audit entry from individual fields.
|
|
72
|
+
* Convenience method that populates the timestamp automatically.
|
|
73
|
+
*/
|
|
74
|
+
logEvent(eventType, agentId, credentialName, policyId, result, reason, latencyMs) {
|
|
75
|
+
this.log({
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
eventType,
|
|
78
|
+
agentId,
|
|
79
|
+
credentialName,
|
|
80
|
+
policyId,
|
|
81
|
+
result,
|
|
82
|
+
reason,
|
|
83
|
+
latencyMs,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Read recent audit entries (last N lines).
|
|
88
|
+
* Returns entries in chronological order (oldest first).
|
|
89
|
+
*/
|
|
90
|
+
readRecent(count) {
|
|
91
|
+
if (!fs.existsSync(this.logPath))
|
|
92
|
+
return [];
|
|
93
|
+
try {
|
|
94
|
+
const content = fs.readFileSync(this.logPath, 'utf-8');
|
|
95
|
+
const lines = content.trim().split('\n').filter(l => l.length > 0);
|
|
96
|
+
const recent = lines.slice(-count);
|
|
97
|
+
return recent.map(line => {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(line);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}).filter((e) => e !== null);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Close the audit logger and release file handles.
|
|
112
|
+
*/
|
|
113
|
+
close() {
|
|
114
|
+
if (this.fd !== null) {
|
|
115
|
+
try {
|
|
116
|
+
fs.closeSync(this.fd);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Ignore close errors
|
|
120
|
+
}
|
|
121
|
+
this.fd = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** Rotate log file if it exceeds the size limit. */
|
|
125
|
+
rotateIfNeeded() {
|
|
126
|
+
if (!fs.existsSync(this.logPath))
|
|
127
|
+
return;
|
|
128
|
+
try {
|
|
129
|
+
const stat = fs.statSync(this.logPath);
|
|
130
|
+
if (stat.size < MAX_FILE_SIZE)
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Rotate: .4 -> .5 (deleted), .3 -> .4, .2 -> .3, .1 -> .2, current -> .1
|
|
137
|
+
for (let i = MAX_RETAINED; i >= 1; i--) {
|
|
138
|
+
const older = `${this.logPath}.${i}`;
|
|
139
|
+
const newer = i === 1 ? this.logPath : `${this.logPath}.${i - 1}`;
|
|
140
|
+
if (i === MAX_RETAINED) {
|
|
141
|
+
// Delete the oldest
|
|
142
|
+
try {
|
|
143
|
+
fs.unlinkSync(older);
|
|
144
|
+
}
|
|
145
|
+
catch { /* ignore */ }
|
|
146
|
+
}
|
|
147
|
+
if (fs.existsSync(newer)) {
|
|
148
|
+
try {
|
|
149
|
+
fs.renameSync(newer, older);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// If rename fails (cross-device), copy and truncate
|
|
153
|
+
try {
|
|
154
|
+
fs.copyFileSync(newer, older);
|
|
155
|
+
fs.writeFileSync(newer, '', { mode: 0o600 });
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Give up on this rotation step
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
exports.AuditLogger = AuditLogger;
|
|
166
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/broker/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Broker daemon lifecycle — start, stop, status, and signal handling.
|
|
3
|
+
*
|
|
4
|
+
* Manages the broker process as a foreground daemon with PID file tracking.
|
|
5
|
+
* Handles graceful shutdown on SIGTERM/SIGINT and cleans up resources.
|
|
6
|
+
*/
|
|
7
|
+
import type { BrokerStatus } from './types';
|
|
8
|
+
import { BrokerServer } from './server';
|
|
9
|
+
declare const DEFAULT_PID_FILE: string;
|
|
10
|
+
declare const DEFAULT_SOCKET_PATH: string;
|
|
11
|
+
declare const DEFAULT_HTTP_PORT = 19421;
|
|
12
|
+
declare const DEFAULT_POLICY_FILE: string;
|
|
13
|
+
declare const DEFAULT_AUDIT_LOG: string;
|
|
14
|
+
export interface DaemonOptions {
|
|
15
|
+
/** Override socket path. */
|
|
16
|
+
socketPath?: string;
|
|
17
|
+
/** Override HTTP port. */
|
|
18
|
+
httpPort?: number;
|
|
19
|
+
/** AIM server URL. */
|
|
20
|
+
aimUrl?: string;
|
|
21
|
+
/** Policy file path. */
|
|
22
|
+
policyFile?: string;
|
|
23
|
+
/** Audit log path. */
|
|
24
|
+
auditLog?: string;
|
|
25
|
+
/** PID file path. */
|
|
26
|
+
pidFile?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Start the broker daemon in the foreground.
|
|
30
|
+
* Writes a PID file and installs signal handlers for graceful shutdown.
|
|
31
|
+
*/
|
|
32
|
+
export declare function startDaemon(options?: DaemonOptions): Promise<BrokerServer>;
|
|
33
|
+
/**
|
|
34
|
+
* Stop a running broker daemon by sending SIGTERM.
|
|
35
|
+
* Returns true if the daemon was stopped, false if it was not running.
|
|
36
|
+
*/
|
|
37
|
+
export declare function stopDaemon(pidFile?: string): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Get the status of the broker daemon.
|
|
40
|
+
* Returns null if the daemon is not running.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getDaemonStatus(pidFile?: string): BrokerStatus | null;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a broker daemon is currently running.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isDaemonRunning(pidFile?: string): boolean;
|
|
47
|
+
export { DEFAULT_PID_FILE, DEFAULT_SOCKET_PATH, DEFAULT_HTTP_PORT, DEFAULT_POLICY_FILE, DEFAULT_AUDIT_LOG, };
|
|
48
|
+
//# sourceMappingURL=daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/broker/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxC,QAAA,MAAM,gBAAgB,QAA0C,CAAC;AACjE,QAAA,MAAM,mBAAmB,QAA2C,CAAC;AACrE,QAAA,MAAM,iBAAiB,QAAQ,CAAC;AAChC,QAAA,MAAM,mBAAmB,QAAoD,CAAC;AAC9E,QAAA,MAAM,iBAAiB,QAAgD,CAAC;AAExE,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAmEhF;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAgBpD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA+BrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAWzD;AAoCD,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,CAAC"}
|