@hai.ai/jacs 0.6.0 → 0.8.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/README.md +336 -52
- package/client.d.ts +96 -0
- package/client.js +560 -0
- package/express.d.ts +69 -0
- package/express.js +130 -0
- package/express.js.map +1 -0
- package/index.d.ts +117 -96
- package/index.js +19 -17
- package/jacs.darwin-arm64.node +0 -0
- package/jacs.darwin-x64.node +0 -0
- package/jacs.linux-arm-gnueabihf.node +0 -0
- package/jacs.linux-arm-musleabihf.node +0 -0
- package/jacs.linux-arm64-gnu.node +0 -0
- package/jacs.linux-x64-gnu.node +0 -0
- package/jacs.linux-x64-musl.node +0 -0
- package/koa.d.ts +59 -0
- package/koa.js +124 -0
- package/koa.js.map +1 -0
- package/langchain.d.ts +97 -0
- package/langchain.js +439 -0
- package/langchain.js.map +1 -0
- package/mcp.d.ts +75 -42
- package/mcp.js +449 -422
- package/mcp.js.map +1 -1
- package/package.json +91 -7
- package/scripts/install-cli.js +125 -0
- package/simple.d.ts +92 -430
- package/simple.js +507 -524
- package/src/a2a.js +2 -2
- package/testing.d.ts +39 -0
- package/testing.js +49 -0
- package/vercel-ai.d.ts +54 -0
- package/vercel-ai.js +162 -0
- package/vercel-ai.js.map +1 -0
- package/mcp.ts +0 -521
package/simple.js
CHANGED
|
@@ -2,45 +2,25 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* JACS Simplified API for TypeScript/JavaScript
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - signMessage(): Sign a message or data
|
|
9
|
-
* - verify(): Verify any signed document
|
|
10
|
-
* - signFile(): Sign a file with optional embedding
|
|
11
|
-
* - updateAgent(): Update the agent document with new data
|
|
12
|
-
* - updateDocument(): Update an existing document with new data
|
|
13
|
-
* - createAgreement(): Create a multi-party agreement
|
|
14
|
-
* - signAgreement(): Sign an existing agreement
|
|
15
|
-
* - checkAgreement(): Check agreement status
|
|
16
|
-
* - trustAgent(): Add an agent to the local trust store
|
|
17
|
-
* - listTrustedAgents(): List all trusted agent IDs
|
|
18
|
-
* - untrustAgent(): Remove an agent from the trust store
|
|
19
|
-
* - isTrusted(): Check if an agent is trusted
|
|
20
|
-
* - getTrustedAgent(): Get a trusted agent's JSON
|
|
21
|
-
* - audit(): Run a read-only security audit and health checks
|
|
22
|
-
*
|
|
23
|
-
* Also re-exports for advanced usage:
|
|
24
|
-
* - JacsAgent: Class for direct agent control
|
|
25
|
-
* - hashString: Standalone SHA-256 hashing
|
|
26
|
-
* - verifyString: Verify with external public key
|
|
27
|
-
* - createConfig: Create agent configuration
|
|
5
|
+
* v0.7.0: Async-first API. All functions that call native JACS operations
|
|
6
|
+
* return Promises by default. Use `*Sync` variants when you need synchronous
|
|
7
|
+
* execution (e.g., CLI scripts, initialization code).
|
|
28
8
|
*
|
|
29
9
|
* @example
|
|
30
10
|
* ```typescript
|
|
31
11
|
* import * as jacs from '@hai.ai/jacs/simple';
|
|
32
12
|
*
|
|
33
|
-
* // Load agent
|
|
34
|
-
* const agent = jacs.load('./jacs.config.json');
|
|
13
|
+
* // Load agent (async, default)
|
|
14
|
+
* const agent = await jacs.load('./jacs.config.json');
|
|
35
15
|
*
|
|
36
16
|
* // Sign a message
|
|
37
|
-
* const signed = jacs.signMessage({ action: 'approve', amount: 100 });
|
|
17
|
+
* const signed = await jacs.signMessage({ action: 'approve', amount: 100 });
|
|
38
18
|
*
|
|
39
19
|
* // Verify it
|
|
40
|
-
* const result = jacs.verify(signed.raw);
|
|
20
|
+
* const result = await jacs.verify(signed.raw);
|
|
41
21
|
* console.log(`Valid: ${result.valid}`);
|
|
42
22
|
*
|
|
43
|
-
* //
|
|
23
|
+
* // Sync variants also available
|
|
44
24
|
* const hash = jacs.hashString('data to hash');
|
|
45
25
|
* ```
|
|
46
26
|
*/
|
|
@@ -78,39 +58,59 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
78
58
|
};
|
|
79
59
|
})();
|
|
80
60
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
81
|
-
exports.MAX_VERIFY_DOCUMENT_BYTES = exports.MAX_VERIFY_URL_LEN = exports.createConfig = exports.
|
|
61
|
+
exports.MAX_VERIFY_DOCUMENT_BYTES = exports.MAX_VERIFY_URL_LEN = exports.createConfig = exports.hashString = exports.JacsAgent = void 0;
|
|
62
|
+
exports.isStrict = isStrict;
|
|
63
|
+
exports.quickstart = quickstart;
|
|
64
|
+
exports.quickstartSync = quickstartSync;
|
|
82
65
|
exports.create = create;
|
|
66
|
+
exports.createSync = createSync;
|
|
83
67
|
exports.load = load;
|
|
68
|
+
exports.loadSync = loadSync;
|
|
84
69
|
exports.verifySelf = verifySelf;
|
|
70
|
+
exports.verifySelfSync = verifySelfSync;
|
|
85
71
|
exports.signMessage = signMessage;
|
|
72
|
+
exports.signMessageSync = signMessageSync;
|
|
86
73
|
exports.updateAgent = updateAgent;
|
|
74
|
+
exports.updateAgentSync = updateAgentSync;
|
|
87
75
|
exports.updateDocument = updateDocument;
|
|
76
|
+
exports.updateDocumentSync = updateDocumentSync;
|
|
88
77
|
exports.signFile = signFile;
|
|
78
|
+
exports.signFileSync = signFileSync;
|
|
89
79
|
exports.verify = verify;
|
|
80
|
+
exports.verifySync = verifySync;
|
|
90
81
|
exports.verifyStandalone = verifyStandalone;
|
|
91
82
|
exports.verifyById = verifyById;
|
|
83
|
+
exports.verifyByIdSync = verifyByIdSync;
|
|
92
84
|
exports.reencryptKey = reencryptKey;
|
|
85
|
+
exports.reencryptKeySync = reencryptKeySync;
|
|
93
86
|
exports.getPublicKey = getPublicKey;
|
|
94
87
|
exports.exportAgent = exportAgent;
|
|
95
88
|
exports.getAgentInfo = getAgentInfo;
|
|
96
89
|
exports.isLoaded = isLoaded;
|
|
90
|
+
exports.debugInfo = debugInfo;
|
|
91
|
+
exports.reset = reset;
|
|
97
92
|
exports.getDnsRecord = getDnsRecord;
|
|
98
93
|
exports.getWellKnownJson = getWellKnownJson;
|
|
94
|
+
exports.getSetupInstructions = getSetupInstructions;
|
|
95
|
+
exports.getSetupInstructionsSync = getSetupInstructionsSync;
|
|
99
96
|
exports.registerWithHai = registerWithHai;
|
|
100
97
|
exports.createAgreement = createAgreement;
|
|
98
|
+
exports.createAgreementSync = createAgreementSync;
|
|
101
99
|
exports.signAgreement = signAgreement;
|
|
100
|
+
exports.signAgreementSync = signAgreementSync;
|
|
102
101
|
exports.checkAgreement = checkAgreement;
|
|
102
|
+
exports.checkAgreementSync = checkAgreementSync;
|
|
103
103
|
exports.trustAgent = trustAgent;
|
|
104
104
|
exports.listTrustedAgents = listTrustedAgents;
|
|
105
105
|
exports.untrustAgent = untrustAgent;
|
|
106
106
|
exports.isTrusted = isTrusted;
|
|
107
107
|
exports.getTrustedAgent = getTrustedAgent;
|
|
108
108
|
exports.audit = audit;
|
|
109
|
+
exports.auditSync = auditSync;
|
|
109
110
|
exports.generateVerifyLink = generateVerifyLink;
|
|
110
111
|
const index_1 = require("./index");
|
|
111
112
|
Object.defineProperty(exports, "JacsAgent", { enumerable: true, get: function () { return index_1.JacsAgent; } });
|
|
112
113
|
Object.defineProperty(exports, "hashString", { enumerable: true, get: function () { return index_1.hashString; } });
|
|
113
|
-
Object.defineProperty(exports, "verifyString", { enumerable: true, get: function () { return index_1.verifyString; } });
|
|
114
114
|
Object.defineProperty(exports, "createConfig", { enumerable: true, get: function () { return index_1.createConfig; } });
|
|
115
115
|
const fs = __importStar(require("fs"));
|
|
116
116
|
const path = __importStar(require("path"));
|
|
@@ -119,6 +119,17 @@ const path = __importStar(require("path"));
|
|
|
119
119
|
// =============================================================================
|
|
120
120
|
let globalAgent = null;
|
|
121
121
|
let agentInfo = null;
|
|
122
|
+
let strictMode = false;
|
|
123
|
+
function resolveStrict(explicit) {
|
|
124
|
+
if (explicit !== undefined) {
|
|
125
|
+
return explicit;
|
|
126
|
+
}
|
|
127
|
+
const envStrict = process.env.JACS_STRICT_MODE;
|
|
128
|
+
return envStrict === 'true' || envStrict === '1';
|
|
129
|
+
}
|
|
130
|
+
function isStrict() {
|
|
131
|
+
return strictMode;
|
|
132
|
+
}
|
|
122
133
|
function resolveConfigRelativePath(configPath, candidate) {
|
|
123
134
|
if (path.isAbsolute(candidate)) {
|
|
124
135
|
return candidate;
|
|
@@ -139,32 +150,19 @@ function normalizeDocumentInput(document) {
|
|
|
139
150
|
}
|
|
140
151
|
return JSON.stringify(document);
|
|
141
152
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
* name: 'my-agent',
|
|
156
|
-
* password: process.env.JACS_PRIVATE_KEY_PASSWORD,
|
|
157
|
-
* algorithm: 'pq2025',
|
|
158
|
-
* });
|
|
159
|
-
* console.log(`Created: ${agent.agentId}`);
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
function create(options) {
|
|
163
|
-
const resolvedPassword = options.password ?? process.env.JACS_PRIVATE_KEY_PASSWORD ?? '';
|
|
164
|
-
if (!resolvedPassword) {
|
|
165
|
-
throw new Error('Missing private key password. Pass options.password or set JACS_PRIVATE_KEY_PASSWORD.');
|
|
166
|
-
}
|
|
167
|
-
const resultJson = (0, index_1.createAgent)(options.name, resolvedPassword, options.algorithm ?? null, options.dataDirectory ?? null, options.keyDirectory ?? null, options.configPath ?? null, options.agentType ?? null, options.description ?? null, options.domain ?? null, options.defaultStorage ?? null);
|
|
153
|
+
function extractAgentInfo(resolvedConfigPath) {
|
|
154
|
+
const config = JSON.parse(fs.readFileSync(resolvedConfigPath, 'utf8'));
|
|
155
|
+
const agentIdVersion = config.jacs_agent_id_and_version || '';
|
|
156
|
+
const [agentId] = agentIdVersion.split(':');
|
|
157
|
+
const keyDir = resolveConfigRelativePath(resolvedConfigPath, config.jacs_key_directory || './jacs_keys');
|
|
158
|
+
return {
|
|
159
|
+
agentId: agentId || '',
|
|
160
|
+
name: config.name || '',
|
|
161
|
+
publicKeyPath: path.join(keyDir, 'jacs.public.pem'),
|
|
162
|
+
configPath: resolvedConfigPath,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function parseCreateResult(resultJson, options) {
|
|
168
166
|
const info = JSON.parse(resultJson);
|
|
169
167
|
return {
|
|
170
168
|
agentId: info.agent_id || '',
|
|
@@ -173,59 +171,281 @@ function create(options) {
|
|
|
173
171
|
configPath: info.config_path || options.configPath || './jacs.config.json',
|
|
174
172
|
};
|
|
175
173
|
}
|
|
174
|
+
function parseSignedResult(result) {
|
|
175
|
+
const doc = JSON.parse(result);
|
|
176
|
+
return {
|
|
177
|
+
raw: result,
|
|
178
|
+
documentId: doc.jacsId || '',
|
|
179
|
+
agentId: doc.jacsSignature?.agentID || '',
|
|
180
|
+
timestamp: doc.jacsSignature?.date || '',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function requireAgent() {
|
|
184
|
+
if (!globalAgent) {
|
|
185
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
186
|
+
}
|
|
187
|
+
return globalAgent;
|
|
188
|
+
}
|
|
189
|
+
function verifyImpl(signedDocument, agent, isSync) {
|
|
190
|
+
const trimmed = signedDocument.trim();
|
|
191
|
+
if (trimmed.length > 0 && !trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
192
|
+
const result = {
|
|
193
|
+
valid: false,
|
|
194
|
+
signerId: '',
|
|
195
|
+
timestamp: '',
|
|
196
|
+
attachments: [],
|
|
197
|
+
errors: [
|
|
198
|
+
`Input does not appear to be a JSON document. If you have a document ID (e.g., 'uuid:version'), use verifyById() instead. Received: '${trimmed.substring(0, 50)}${trimmed.length > 50 ? '...' : ''}'`
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
return isSync ? result : Promise.resolve(result);
|
|
202
|
+
}
|
|
203
|
+
let doc;
|
|
204
|
+
try {
|
|
205
|
+
doc = JSON.parse(signedDocument);
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
const result = {
|
|
209
|
+
valid: false,
|
|
210
|
+
signerId: '',
|
|
211
|
+
timestamp: '',
|
|
212
|
+
attachments: [],
|
|
213
|
+
errors: [`Invalid JSON: ${e}`],
|
|
214
|
+
};
|
|
215
|
+
return isSync ? result : Promise.resolve(result);
|
|
216
|
+
}
|
|
217
|
+
const extractAttachments = () => (doc.jacsFiles || []).map((f) => ({
|
|
218
|
+
filename: f.path || '',
|
|
219
|
+
mimeType: f.mimetype || 'application/octet-stream',
|
|
220
|
+
hash: f.sha256 || '',
|
|
221
|
+
embedded: f.embed || false,
|
|
222
|
+
content: f.contents ? Buffer.from(f.contents, 'base64') : undefined,
|
|
223
|
+
}));
|
|
224
|
+
const makeSuccess = () => ({
|
|
225
|
+
valid: true,
|
|
226
|
+
data: doc.content,
|
|
227
|
+
signerId: doc.jacsSignature?.agentID || '',
|
|
228
|
+
timestamp: doc.jacsSignature?.date || '',
|
|
229
|
+
attachments: extractAttachments(),
|
|
230
|
+
errors: [],
|
|
231
|
+
});
|
|
232
|
+
const makeFailure = (e) => {
|
|
233
|
+
if (strictMode) {
|
|
234
|
+
throw new Error(`Verification failed (strict mode): ${e}`);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
valid: false,
|
|
238
|
+
signerId: doc.jacsSignature?.agentID || '',
|
|
239
|
+
timestamp: doc.jacsSignature?.date || '',
|
|
240
|
+
attachments: [],
|
|
241
|
+
errors: [String(e)],
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
if (isSync) {
|
|
245
|
+
try {
|
|
246
|
+
agent.verifyDocumentSync(signedDocument);
|
|
247
|
+
return makeSuccess();
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
return makeFailure(e);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
return agent.verifyDocument(signedDocument)
|
|
255
|
+
.then(() => makeSuccess())
|
|
256
|
+
.catch((e) => makeFailure(e));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function ensurePassword() {
|
|
260
|
+
let password = process.env.JACS_PRIVATE_KEY_PASSWORD || '';
|
|
261
|
+
if (!password) {
|
|
262
|
+
const crypto = require('crypto');
|
|
263
|
+
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
264
|
+
const lower = 'abcdefghijklmnopqrstuvwxyz';
|
|
265
|
+
const digits = '0123456789';
|
|
266
|
+
const special = '!@#$%^&*()-_=+';
|
|
267
|
+
const all = upper + lower + digits + special;
|
|
268
|
+
password =
|
|
269
|
+
upper[crypto.randomInt(upper.length)] +
|
|
270
|
+
lower[crypto.randomInt(lower.length)] +
|
|
271
|
+
digits[crypto.randomInt(digits.length)] +
|
|
272
|
+
special[crypto.randomInt(special.length)];
|
|
273
|
+
for (let i = 4; i < 32; i++) {
|
|
274
|
+
password += all[crypto.randomInt(all.length)];
|
|
275
|
+
}
|
|
276
|
+
const keysDir = './jacs_keys';
|
|
277
|
+
fs.mkdirSync(keysDir, { recursive: true });
|
|
278
|
+
const pwPath = path.join(keysDir, '.jacs_password');
|
|
279
|
+
fs.writeFileSync(pwPath, password, { mode: 0o600 });
|
|
280
|
+
process.env.JACS_PRIVATE_KEY_PASSWORD = password;
|
|
281
|
+
}
|
|
282
|
+
return password;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Zero-config quickstart: loads or creates a persistent agent.
|
|
286
|
+
* @returns Promise<QuickstartInfo>
|
|
287
|
+
*/
|
|
288
|
+
async function quickstart(options) {
|
|
289
|
+
strictMode = resolveStrict(options?.strict);
|
|
290
|
+
const configPath = options?.configPath || './jacs.config.json';
|
|
291
|
+
if (fs.existsSync(configPath)) {
|
|
292
|
+
const info = await load(configPath);
|
|
293
|
+
return {
|
|
294
|
+
agentId: info.agentId,
|
|
295
|
+
name: info.name || 'jacs-agent',
|
|
296
|
+
version: '',
|
|
297
|
+
algorithm: '',
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const password = ensurePassword();
|
|
301
|
+
const algo = options?.algorithm || 'pq2025';
|
|
302
|
+
const result = await create({
|
|
303
|
+
name: 'jacs-agent',
|
|
304
|
+
password,
|
|
305
|
+
algorithm: algo,
|
|
306
|
+
configPath,
|
|
307
|
+
});
|
|
308
|
+
await load(result.configPath || configPath, { strict: strictMode });
|
|
309
|
+
return {
|
|
310
|
+
agentId: result.agentId,
|
|
311
|
+
name: 'jacs-agent',
|
|
312
|
+
version: '',
|
|
313
|
+
algorithm: algo,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Zero-config quickstart (sync variant, blocks event loop).
|
|
318
|
+
*/
|
|
319
|
+
function quickstartSync(options) {
|
|
320
|
+
strictMode = resolveStrict(options?.strict);
|
|
321
|
+
const configPath = options?.configPath || './jacs.config.json';
|
|
322
|
+
if (fs.existsSync(configPath)) {
|
|
323
|
+
const info = loadSync(configPath);
|
|
324
|
+
return {
|
|
325
|
+
agentId: info.agentId,
|
|
326
|
+
name: info.name || 'jacs-agent',
|
|
327
|
+
version: '',
|
|
328
|
+
algorithm: '',
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
const password = ensurePassword();
|
|
332
|
+
const algo = options?.algorithm || 'pq2025';
|
|
333
|
+
const result = createSync({
|
|
334
|
+
name: 'jacs-agent',
|
|
335
|
+
password,
|
|
336
|
+
algorithm: algo,
|
|
337
|
+
configPath,
|
|
338
|
+
});
|
|
339
|
+
loadSync(result.configPath || configPath, { strict: strictMode });
|
|
340
|
+
return {
|
|
341
|
+
agentId: result.agentId,
|
|
342
|
+
name: 'jacs-agent',
|
|
343
|
+
version: '',
|
|
344
|
+
algorithm: algo,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function resolveCreatePassword(options) {
|
|
348
|
+
const p = options.password ?? process.env.JACS_PRIVATE_KEY_PASSWORD ?? '';
|
|
349
|
+
if (!p) {
|
|
350
|
+
throw new Error('Missing private key password. Pass options.password or set JACS_PRIVATE_KEY_PASSWORD.');
|
|
351
|
+
}
|
|
352
|
+
return p;
|
|
353
|
+
}
|
|
354
|
+
function createNativeArgs(options, password) {
|
|
355
|
+
return [
|
|
356
|
+
options.name,
|
|
357
|
+
password,
|
|
358
|
+
options.algorithm ?? null,
|
|
359
|
+
options.dataDirectory ?? null,
|
|
360
|
+
options.keyDirectory ?? null,
|
|
361
|
+
options.configPath ?? null,
|
|
362
|
+
options.agentType ?? null,
|
|
363
|
+
options.description ?? null,
|
|
364
|
+
options.domain ?? null,
|
|
365
|
+
options.defaultStorage ?? null,
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Creates a new JACS agent with cryptographic keys.
|
|
370
|
+
*/
|
|
371
|
+
async function create(options) {
|
|
372
|
+
const password = resolveCreatePassword(options);
|
|
373
|
+
const resultJson = await (0, index_1.createAgent)(...createNativeArgs(options, password));
|
|
374
|
+
return parseCreateResult(resultJson, options);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Creates a new JACS agent (sync, blocks event loop).
|
|
378
|
+
*/
|
|
379
|
+
function createSync(options) {
|
|
380
|
+
const password = resolveCreatePassword(options);
|
|
381
|
+
const resultJson = (0, index_1.createAgentSync)(...createNativeArgs(options, password));
|
|
382
|
+
return parseCreateResult(resultJson, options);
|
|
383
|
+
}
|
|
176
384
|
/**
|
|
177
385
|
* Loads an existing agent from a configuration file.
|
|
178
|
-
*
|
|
179
|
-
* @param configPath - Path to jacs.config.json (default: "./jacs.config.json")
|
|
180
|
-
* @returns AgentInfo with the loaded agent's details
|
|
181
|
-
*
|
|
182
|
-
* @example
|
|
183
|
-
* ```typescript
|
|
184
|
-
* const agent = jacs.load('./jacs.config.json');
|
|
185
|
-
* console.log(`Loaded: ${agent.agentId}`);
|
|
186
|
-
* ```
|
|
187
386
|
*/
|
|
188
|
-
function load(configPath) {
|
|
387
|
+
async function load(configPath, options) {
|
|
388
|
+
strictMode = resolveStrict(options?.strict);
|
|
189
389
|
const requestedPath = configPath || './jacs.config.json';
|
|
190
390
|
const resolvedConfigPath = path.resolve(requestedPath);
|
|
191
391
|
if (!fs.existsSync(resolvedConfigPath)) {
|
|
192
392
|
throw new Error(`Config file not found: ${requestedPath}\nRun 'jacs create' to create a new agent.`);
|
|
193
393
|
}
|
|
194
|
-
// Create new agent instance
|
|
195
394
|
globalAgent = new index_1.JacsAgent();
|
|
196
|
-
globalAgent.load(resolvedConfigPath);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
395
|
+
await globalAgent.load(resolvedConfigPath);
|
|
396
|
+
agentInfo = extractAgentInfo(resolvedConfigPath);
|
|
397
|
+
return agentInfo;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Loads an existing agent (sync, blocks event loop).
|
|
401
|
+
*/
|
|
402
|
+
function loadSync(configPath, options) {
|
|
403
|
+
strictMode = resolveStrict(options?.strict);
|
|
404
|
+
const requestedPath = configPath || './jacs.config.json';
|
|
405
|
+
const resolvedConfigPath = path.resolve(requestedPath);
|
|
406
|
+
if (!fs.existsSync(resolvedConfigPath)) {
|
|
407
|
+
throw new Error(`Config file not found: ${requestedPath}\nRun 'jacs create' to create a new agent.`);
|
|
408
|
+
}
|
|
409
|
+
globalAgent = new index_1.JacsAgent();
|
|
410
|
+
globalAgent.loadSync(resolvedConfigPath);
|
|
411
|
+
agentInfo = extractAgentInfo(resolvedConfigPath);
|
|
208
412
|
return agentInfo;
|
|
209
413
|
}
|
|
210
414
|
/**
|
|
211
415
|
* Verifies the currently loaded agent's integrity.
|
|
212
|
-
*
|
|
213
|
-
* @returns VerificationResult indicating if the agent is valid
|
|
214
|
-
*
|
|
215
|
-
* @example
|
|
216
|
-
* ```typescript
|
|
217
|
-
* const result = jacs.verifySelf();
|
|
218
|
-
* if (result.valid) {
|
|
219
|
-
* console.log('Agent integrity verified');
|
|
220
|
-
* }
|
|
221
|
-
* ```
|
|
222
416
|
*/
|
|
223
|
-
function verifySelf() {
|
|
224
|
-
|
|
225
|
-
|
|
417
|
+
async function verifySelf() {
|
|
418
|
+
const agent = requireAgent();
|
|
419
|
+
try {
|
|
420
|
+
await agent.verifyAgent();
|
|
421
|
+
return {
|
|
422
|
+
valid: true,
|
|
423
|
+
signerId: agentInfo?.agentId || '',
|
|
424
|
+
timestamp: '',
|
|
425
|
+
attachments: [],
|
|
426
|
+
errors: [],
|
|
427
|
+
};
|
|
226
428
|
}
|
|
429
|
+
catch (e) {
|
|
430
|
+
if (strictMode) {
|
|
431
|
+
throw new Error(`Self-verification failed (strict mode): ${e}`);
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
valid: false,
|
|
435
|
+
signerId: '',
|
|
436
|
+
timestamp: '',
|
|
437
|
+
attachments: [],
|
|
438
|
+
errors: [String(e)],
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Verifies the currently loaded agent's integrity (sync).
|
|
444
|
+
*/
|
|
445
|
+
function verifySelfSync() {
|
|
446
|
+
const agent = requireAgent();
|
|
227
447
|
try {
|
|
228
|
-
|
|
448
|
+
agent.verifyAgentSync();
|
|
229
449
|
return {
|
|
230
450
|
valid: true,
|
|
231
451
|
signerId: agentInfo?.agentId || '',
|
|
@@ -235,6 +455,9 @@ function verifySelf() {
|
|
|
235
455
|
};
|
|
236
456
|
}
|
|
237
457
|
catch (e) {
|
|
458
|
+
if (strictMode) {
|
|
459
|
+
throw new Error(`Self-verification failed (strict mode): ${e}`);
|
|
460
|
+
}
|
|
238
461
|
return {
|
|
239
462
|
valid: false,
|
|
240
463
|
signerId: '',
|
|
@@ -246,261 +469,168 @@ function verifySelf() {
|
|
|
246
469
|
}
|
|
247
470
|
/**
|
|
248
471
|
* Signs arbitrary data as a JACS message.
|
|
249
|
-
*
|
|
250
|
-
* @param data - The data to sign (object, string, or any JSON-serializable value)
|
|
251
|
-
* @returns SignedDocument containing the full signed document
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* ```typescript
|
|
255
|
-
* const signed = jacs.signMessage({ action: 'approve', amount: 100 });
|
|
256
|
-
* console.log(`Document ID: ${signed.documentId}`);
|
|
257
|
-
* ```
|
|
258
472
|
*/
|
|
259
|
-
function signMessage(data) {
|
|
260
|
-
|
|
261
|
-
throw new Error('No agent loaded. Call load() first.');
|
|
262
|
-
}
|
|
263
|
-
// Create document structure
|
|
473
|
+
async function signMessage(data) {
|
|
474
|
+
const agent = requireAgent();
|
|
264
475
|
const docContent = {
|
|
265
476
|
jacsType: 'message',
|
|
266
477
|
jacsLevel: 'raw',
|
|
267
478
|
content: data,
|
|
268
479
|
};
|
|
269
|
-
const result =
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
480
|
+
const result = await agent.createDocument(JSON.stringify(docContent), null, null, true, null, null);
|
|
481
|
+
return parseSignedResult(result);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Signs arbitrary data (sync, blocks event loop).
|
|
485
|
+
*/
|
|
486
|
+
function signMessageSync(data) {
|
|
487
|
+
const agent = requireAgent();
|
|
488
|
+
const docContent = {
|
|
489
|
+
jacsType: 'message',
|
|
490
|
+
jacsLevel: 'raw',
|
|
491
|
+
content: data,
|
|
278
492
|
};
|
|
493
|
+
const result = agent.createDocumentSync(JSON.stringify(docContent), null, null, true, null, null);
|
|
494
|
+
return parseSignedResult(result);
|
|
279
495
|
}
|
|
280
496
|
/**
|
|
281
497
|
* Updates the agent document with new data and re-signs it.
|
|
282
|
-
*
|
|
283
|
-
* This function expects a complete agent document (not partial updates).
|
|
284
|
-
* Use exportAgent() to get the current document, modify it, then pass it here.
|
|
285
|
-
* The function will create a new version, re-sign, and re-hash the document.
|
|
286
|
-
*
|
|
287
|
-
* @param newAgentData - Complete agent document as JSON string or object
|
|
288
|
-
* @returns The updated and re-signed agent document as a JSON string
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```typescript
|
|
292
|
-
* // Get current agent, modify, and update
|
|
293
|
-
* const agentDoc = JSON.parse(jacs.exportAgent());
|
|
294
|
-
* agentDoc.jacsAgentType = 'updated-service';
|
|
295
|
-
* const updated = jacs.updateAgent(agentDoc);
|
|
296
|
-
* console.log('Agent updated with new version');
|
|
297
|
-
* ```
|
|
298
498
|
*/
|
|
299
|
-
function updateAgent(newAgentData) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
499
|
+
async function updateAgent(newAgentData) {
|
|
500
|
+
const agent = requireAgent();
|
|
501
|
+
const dataString = typeof newAgentData === 'string' ? newAgentData : JSON.stringify(newAgentData);
|
|
502
|
+
return agent.updateAgent(dataString);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Updates the agent document (sync, blocks event loop).
|
|
506
|
+
*/
|
|
507
|
+
function updateAgentSync(newAgentData) {
|
|
508
|
+
const agent = requireAgent();
|
|
509
|
+
const dataString = typeof newAgentData === 'string' ? newAgentData : JSON.stringify(newAgentData);
|
|
510
|
+
return agent.updateAgentSync(dataString);
|
|
307
511
|
}
|
|
308
512
|
/**
|
|
309
513
|
* Updates an existing document with new data and re-signs it.
|
|
310
|
-
*
|
|
311
|
-
* Use signMessage() to create a document first, then use this to update it.
|
|
312
|
-
* The function will create a new version, re-sign, and re-hash the document.
|
|
313
|
-
*
|
|
314
|
-
* @param documentId - The document ID (jacsId) to update
|
|
315
|
-
* @param newDocumentData - The updated document as JSON string or object
|
|
316
|
-
* @param attachments - Optional array of file paths to attach
|
|
317
|
-
* @param embed - If true, embed attachment contents
|
|
318
|
-
* @returns SignedDocument with the updated document
|
|
319
|
-
*
|
|
320
|
-
* @example
|
|
321
|
-
* ```typescript
|
|
322
|
-
* // Create a document first
|
|
323
|
-
* const signed = jacs.signMessage({ status: 'pending' });
|
|
324
|
-
*
|
|
325
|
-
* // Later, update it
|
|
326
|
-
* const doc = JSON.parse(signed.raw);
|
|
327
|
-
* doc.content.status = 'approved';
|
|
328
|
-
* const updated = jacs.updateDocument(signed.documentId, doc);
|
|
329
|
-
* console.log('Document updated with new version');
|
|
330
|
-
* ```
|
|
331
514
|
*/
|
|
332
|
-
function updateDocument(documentId, newDocumentData, attachments, embed) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
};
|
|
515
|
+
async function updateDocument(documentId, newDocumentData, attachments, embed) {
|
|
516
|
+
const agent = requireAgent();
|
|
517
|
+
const dataString = typeof newDocumentData === 'string' ? newDocumentData : JSON.stringify(newDocumentData);
|
|
518
|
+
const result = await agent.updateDocument(documentId, dataString, attachments || null, embed ?? null);
|
|
519
|
+
return parseSignedResult(result);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Updates an existing document (sync, blocks event loop).
|
|
523
|
+
*/
|
|
524
|
+
function updateDocumentSync(documentId, newDocumentData, attachments, embed) {
|
|
525
|
+
const agent = requireAgent();
|
|
526
|
+
const dataString = typeof newDocumentData === 'string' ? newDocumentData : JSON.stringify(newDocumentData);
|
|
527
|
+
const result = agent.updateDocumentSync(documentId, dataString, attachments || null, embed ?? null);
|
|
528
|
+
return parseSignedResult(result);
|
|
347
529
|
}
|
|
348
530
|
/**
|
|
349
531
|
* Signs a file with optional content embedding.
|
|
350
|
-
*
|
|
351
|
-
* @param filePath - Path to the file to sign
|
|
352
|
-
* @param embed - If true, embed file content in the document
|
|
353
|
-
* @returns SignedDocument with file attachment
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* ```typescript
|
|
357
|
-
* const signed = jacs.signFile('contract.pdf', true);
|
|
358
|
-
* console.log(`Signed: ${signed.attachments[0].filename}`);
|
|
359
|
-
* ```
|
|
360
532
|
*/
|
|
361
|
-
function signFile(filePath, embed = false) {
|
|
362
|
-
|
|
363
|
-
|
|
533
|
+
async function signFile(filePath, embed = false) {
|
|
534
|
+
const agent = requireAgent();
|
|
535
|
+
if (!fs.existsSync(filePath)) {
|
|
536
|
+
throw new Error(`File not found: ${filePath}`);
|
|
364
537
|
}
|
|
538
|
+
const docContent = {
|
|
539
|
+
jacsType: 'file',
|
|
540
|
+
jacsLevel: 'raw',
|
|
541
|
+
filename: path.basename(filePath),
|
|
542
|
+
};
|
|
543
|
+
const result = await agent.createDocument(JSON.stringify(docContent), null, null, true, filePath, embed);
|
|
544
|
+
return parseSignedResult(result);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Signs a file (sync, blocks event loop).
|
|
548
|
+
*/
|
|
549
|
+
function signFileSync(filePath, embed = false) {
|
|
550
|
+
const agent = requireAgent();
|
|
365
551
|
if (!fs.existsSync(filePath)) {
|
|
366
552
|
throw new Error(`File not found: ${filePath}`);
|
|
367
553
|
}
|
|
368
|
-
// Create document structure
|
|
369
554
|
const docContent = {
|
|
370
555
|
jacsType: 'file',
|
|
371
556
|
jacsLevel: 'raw',
|
|
372
557
|
filename: path.basename(filePath),
|
|
373
558
|
};
|
|
374
|
-
const result =
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
559
|
+
const result = agent.createDocumentSync(JSON.stringify(docContent), null, null, true, filePath, embed);
|
|
560
|
+
return parseSignedResult(result);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Verifies a signed document and extracts its content.
|
|
564
|
+
*/
|
|
565
|
+
async function verify(signedDocument) {
|
|
566
|
+
const agent = requireAgent();
|
|
567
|
+
return verifyImpl(signedDocument, agent, false);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Verifies a signed document (sync, blocks event loop).
|
|
571
|
+
*/
|
|
572
|
+
function verifySync(signedDocument) {
|
|
573
|
+
const agent = requireAgent();
|
|
574
|
+
return verifyImpl(signedDocument, agent, true);
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Verify a signed JACS document without loading an agent.
|
|
578
|
+
*/
|
|
579
|
+
function verifyStandalone(signedDocument, options) {
|
|
580
|
+
const doc = typeof signedDocument === 'string' ? signedDocument : JSON.stringify(signedDocument);
|
|
581
|
+
const r = (0, index_1.verifyDocumentStandalone)(doc, options?.keyResolution ?? undefined, options?.dataDirectory ?? undefined, options?.keyDirectory ?? undefined);
|
|
378
582
|
return {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
583
|
+
valid: r.valid,
|
|
584
|
+
signerId: r.signerId,
|
|
585
|
+
timestamp: '',
|
|
586
|
+
attachments: [],
|
|
587
|
+
errors: [],
|
|
383
588
|
};
|
|
384
589
|
}
|
|
385
590
|
/**
|
|
386
|
-
* Verifies a
|
|
387
|
-
*
|
|
388
|
-
* @param signedDocument - The JSON string of the signed document
|
|
389
|
-
* @returns VerificationResult with the verification status and extracted content
|
|
390
|
-
*
|
|
391
|
-
* @example
|
|
392
|
-
* ```typescript
|
|
393
|
-
* const result = jacs.verify(signedJson);
|
|
394
|
-
* if (result.valid) {
|
|
395
|
-
* console.log(`Signed by: ${result.signerId}`);
|
|
396
|
-
* }
|
|
397
|
-
* ```
|
|
591
|
+
* Verifies a document by its storage ID.
|
|
398
592
|
*/
|
|
399
|
-
function
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
// Detect non-JSON input and provide helpful error
|
|
404
|
-
const trimmed = signedDocument.trim();
|
|
405
|
-
if (trimmed.length > 0 && !trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
593
|
+
async function verifyById(documentId) {
|
|
594
|
+
const agent = requireAgent();
|
|
595
|
+
if (!documentId.includes(':')) {
|
|
406
596
|
return {
|
|
407
597
|
valid: false,
|
|
408
598
|
signerId: '',
|
|
409
599
|
timestamp: '',
|
|
410
600
|
attachments: [],
|
|
411
601
|
errors: [
|
|
412
|
-
`
|
|
602
|
+
`Document ID must be in 'uuid:version' format, got '${documentId}'. Use verify() with the full JSON string instead.`
|
|
413
603
|
],
|
|
414
604
|
};
|
|
415
605
|
}
|
|
416
|
-
let doc;
|
|
417
606
|
try {
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
catch (e) {
|
|
607
|
+
await agent.verifyDocumentById(documentId);
|
|
421
608
|
return {
|
|
422
|
-
valid:
|
|
609
|
+
valid: true,
|
|
423
610
|
signerId: '',
|
|
424
611
|
timestamp: '',
|
|
425
612
|
attachments: [],
|
|
426
|
-
errors: [`Invalid JSON: ${e}`],
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
try {
|
|
430
|
-
globalAgent.verifyDocument(signedDocument);
|
|
431
|
-
// Extract attachments
|
|
432
|
-
const attachments = (doc.jacsFiles || []).map((f) => ({
|
|
433
|
-
filename: f.path || '',
|
|
434
|
-
mimeType: f.mimetype || 'application/octet-stream',
|
|
435
|
-
hash: f.sha256 || '',
|
|
436
|
-
embedded: f.embed || false,
|
|
437
|
-
content: f.contents ? Buffer.from(f.contents, 'base64') : undefined,
|
|
438
|
-
}));
|
|
439
|
-
return {
|
|
440
|
-
valid: true,
|
|
441
|
-
data: doc.content,
|
|
442
|
-
signerId: doc.jacsSignature?.agentID || '',
|
|
443
|
-
timestamp: doc.jacsSignature?.date || '',
|
|
444
|
-
attachments,
|
|
445
613
|
errors: [],
|
|
446
614
|
};
|
|
447
615
|
}
|
|
448
616
|
catch (e) {
|
|
617
|
+
if (strictMode) {
|
|
618
|
+
throw new Error(`Verification failed (strict mode): ${e}`);
|
|
619
|
+
}
|
|
449
620
|
return {
|
|
450
621
|
valid: false,
|
|
451
|
-
signerId:
|
|
452
|
-
timestamp:
|
|
622
|
+
signerId: '',
|
|
623
|
+
timestamp: '',
|
|
453
624
|
attachments: [],
|
|
454
625
|
errors: [String(e)],
|
|
455
626
|
};
|
|
456
627
|
}
|
|
457
628
|
}
|
|
458
629
|
/**
|
|
459
|
-
*
|
|
460
|
-
* Uses caller-supplied key resolution and directories; does not use global agent state.
|
|
461
|
-
*
|
|
462
|
-
* @param signedDocument - Full signed JACS document JSON string
|
|
463
|
-
* @param options - Optional keyResolution, dataDirectory, keyDirectory
|
|
464
|
-
* @returns VerificationResult with valid and signerId
|
|
465
|
-
*
|
|
466
|
-
* @example
|
|
467
|
-
* ```typescript
|
|
468
|
-
* const result = jacs.verifyStandalone(signedJson, { keyResolution: 'local', keyDirectory: './keys' });
|
|
469
|
-
* if (result.valid) console.log(`Signed by: ${result.signerId}`);
|
|
470
|
-
* ```
|
|
471
|
-
*/
|
|
472
|
-
function verifyStandalone(signedDocument, options) {
|
|
473
|
-
const doc = typeof signedDocument === 'string' ? signedDocument : JSON.stringify(signedDocument);
|
|
474
|
-
const r = (0, index_1.verifyDocumentStandalone)(doc, options?.keyResolution ?? undefined, options?.dataDirectory ?? undefined, options?.keyDirectory ?? undefined);
|
|
475
|
-
return {
|
|
476
|
-
valid: r.valid,
|
|
477
|
-
signerId: r.signerId,
|
|
478
|
-
timestamp: '',
|
|
479
|
-
attachments: [],
|
|
480
|
-
errors: [],
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Verifies a document by its storage ID.
|
|
485
|
-
*
|
|
486
|
-
* Use this when you have a document ID (e.g., "uuid:version") rather than
|
|
487
|
-
* the full JSON string. The document will be loaded from storage and verified.
|
|
488
|
-
*
|
|
489
|
-
* @param documentId - The document ID in "uuid:version" format
|
|
490
|
-
* @returns VerificationResult with the verification status
|
|
491
|
-
*
|
|
492
|
-
* @example
|
|
493
|
-
* ```typescript
|
|
494
|
-
* const result = jacs.verifyById('550e8400-e29b-41d4-a716-446655440000:1');
|
|
495
|
-
* if (result.valid) {
|
|
496
|
-
* console.log('Document verified');
|
|
497
|
-
* }
|
|
498
|
-
* ```
|
|
630
|
+
* Verifies a document by its storage ID (sync, blocks event loop).
|
|
499
631
|
*/
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
throw new Error('No agent loaded. Call load() first.');
|
|
503
|
-
}
|
|
632
|
+
function verifyByIdSync(documentId) {
|
|
633
|
+
const agent = requireAgent();
|
|
504
634
|
if (!documentId.includes(':')) {
|
|
505
635
|
return {
|
|
506
636
|
valid: false,
|
|
@@ -513,7 +643,7 @@ function verifyById(documentId) {
|
|
|
513
643
|
};
|
|
514
644
|
}
|
|
515
645
|
try {
|
|
516
|
-
|
|
646
|
+
agent.verifyDocumentByIdSync(documentId);
|
|
517
647
|
return {
|
|
518
648
|
valid: true,
|
|
519
649
|
signerId: '',
|
|
@@ -523,6 +653,9 @@ function verifyById(documentId) {
|
|
|
523
653
|
};
|
|
524
654
|
}
|
|
525
655
|
catch (e) {
|
|
656
|
+
if (strictMode) {
|
|
657
|
+
throw new Error(`Verification failed (strict mode): ${e}`);
|
|
658
|
+
}
|
|
526
659
|
return {
|
|
527
660
|
valid: false,
|
|
528
661
|
signerId: '',
|
|
@@ -534,58 +667,34 @@ function verifyById(documentId) {
|
|
|
534
667
|
}
|
|
535
668
|
/**
|
|
536
669
|
* Re-encrypt the agent's private key with a new password.
|
|
537
|
-
*
|
|
538
|
-
* @param oldPassword - The current password for the private key
|
|
539
|
-
* @param newPassword - The new password to encrypt with (must meet password requirements)
|
|
540
|
-
*
|
|
541
|
-
* @example
|
|
542
|
-
* ```typescript
|
|
543
|
-
* jacs.reencryptKey('old-password-123!', 'new-Str0ng-P@ss!');
|
|
544
|
-
* console.log('Key re-encrypted successfully');
|
|
545
|
-
* ```
|
|
546
670
|
*/
|
|
547
|
-
function reencryptKey(oldPassword, newPassword) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}
|
|
551
|
-
globalAgent.reencryptKey(oldPassword, newPassword);
|
|
671
|
+
async function reencryptKey(oldPassword, newPassword) {
|
|
672
|
+
const agent = requireAgent();
|
|
673
|
+
await agent.reencryptKey(oldPassword, newPassword);
|
|
552
674
|
}
|
|
553
675
|
/**
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
* @returns The public key as a PEM-encoded string
|
|
557
|
-
*
|
|
558
|
-
* @example
|
|
559
|
-
* ```typescript
|
|
560
|
-
* const pem = jacs.getPublicKey();
|
|
561
|
-
* console.log(pem); // Share with others for verification
|
|
562
|
-
* ```
|
|
676
|
+
* Re-encrypt the agent's private key (sync, blocks event loop).
|
|
563
677
|
*/
|
|
678
|
+
function reencryptKeySync(oldPassword, newPassword) {
|
|
679
|
+
const agent = requireAgent();
|
|
680
|
+
agent.reencryptKeySync(oldPassword, newPassword);
|
|
681
|
+
}
|
|
682
|
+
// =============================================================================
|
|
683
|
+
// Pure sync helpers (no NAPI calls, stay sync-only)
|
|
684
|
+
// =============================================================================
|
|
564
685
|
function getPublicKey() {
|
|
565
686
|
if (!agentInfo) {
|
|
566
|
-
throw new Error('No agent loaded. Call load()
|
|
687
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
567
688
|
}
|
|
568
689
|
if (!fs.existsSync(agentInfo.publicKeyPath)) {
|
|
569
690
|
throw new Error(`Public key not found: ${agentInfo.publicKeyPath}`);
|
|
570
691
|
}
|
|
571
692
|
return fs.readFileSync(agentInfo.publicKeyPath, 'utf8');
|
|
572
693
|
}
|
|
573
|
-
/**
|
|
574
|
-
* Export the agent document for sharing.
|
|
575
|
-
*
|
|
576
|
-
* @returns The agent JSON document as a string
|
|
577
|
-
*
|
|
578
|
-
* @example
|
|
579
|
-
* ```typescript
|
|
580
|
-
* const agentDoc = jacs.exportAgent();
|
|
581
|
-
* // Send to another party for trust establishment
|
|
582
|
-
* ```
|
|
583
|
-
*/
|
|
584
694
|
function exportAgent() {
|
|
585
695
|
if (!agentInfo) {
|
|
586
|
-
throw new Error('No agent loaded. Call load()
|
|
696
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
587
697
|
}
|
|
588
|
-
// Read agent file
|
|
589
698
|
const configPath = path.resolve(agentInfo.configPath);
|
|
590
699
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
591
700
|
const dataDir = resolveConfigRelativePath(configPath, config.jacs_data_directory || './jacs_data');
|
|
@@ -596,29 +705,31 @@ function exportAgent() {
|
|
|
596
705
|
}
|
|
597
706
|
return fs.readFileSync(agentPath, 'utf8');
|
|
598
707
|
}
|
|
599
|
-
/**
|
|
600
|
-
* Get information about the currently loaded agent.
|
|
601
|
-
*
|
|
602
|
-
* @returns AgentInfo if an agent is loaded, null otherwise
|
|
603
|
-
*/
|
|
604
708
|
function getAgentInfo() {
|
|
605
709
|
return agentInfo;
|
|
606
710
|
}
|
|
607
|
-
/**
|
|
608
|
-
* Check if an agent is currently loaded.
|
|
609
|
-
*
|
|
610
|
-
* @returns true if an agent is loaded, false otherwise
|
|
611
|
-
*/
|
|
612
711
|
function isLoaded() {
|
|
613
712
|
return globalAgent !== null;
|
|
614
713
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
714
|
+
function debugInfo() {
|
|
715
|
+
if (!globalAgent) {
|
|
716
|
+
return { jacs_version: 'unknown', agent_loaded: false };
|
|
717
|
+
}
|
|
718
|
+
try {
|
|
719
|
+
return JSON.parse(globalAgent.diagnostics());
|
|
720
|
+
}
|
|
721
|
+
catch {
|
|
722
|
+
return { jacs_version: 'unknown', agent_loaded: false };
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function reset() {
|
|
726
|
+
globalAgent = null;
|
|
727
|
+
agentInfo = null;
|
|
728
|
+
strictMode = false;
|
|
729
|
+
}
|
|
619
730
|
function getDnsRecord(domain, ttl = 3600) {
|
|
620
731
|
if (!agentInfo) {
|
|
621
|
-
throw new Error('No agent loaded. Call load()
|
|
732
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
622
733
|
}
|
|
623
734
|
const agentDoc = JSON.parse(exportAgent());
|
|
624
735
|
const jacsId = agentDoc.jacsId || agentDoc.agentId || '';
|
|
@@ -630,13 +741,9 @@ function getDnsRecord(domain, ttl = 3600) {
|
|
|
630
741
|
const txt = `v=hai.ai; jacs_agent_id=${jacsId}; alg=SHA-256; enc=base64; jac_public_key_hash=${publicKeyHash}`;
|
|
631
742
|
return `${owner} ${ttl} IN TXT "${txt}"`;
|
|
632
743
|
}
|
|
633
|
-
/**
|
|
634
|
-
* Returns the well-known JSON object for the loaded agent (e.g. for /.well-known/jacs-pubkey.json).
|
|
635
|
-
* Keys: publicKey, publicKeyHash, algorithm, agentId.
|
|
636
|
-
*/
|
|
637
744
|
function getWellKnownJson() {
|
|
638
745
|
if (!agentInfo) {
|
|
639
|
-
throw new Error('No agent loaded. Call load()
|
|
746
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
640
747
|
}
|
|
641
748
|
const agentDoc = JSON.parse(exportAgent());
|
|
642
749
|
const jacsId = agentDoc.jacsId || agentDoc.agentId || '';
|
|
@@ -657,17 +764,25 @@ function getWellKnownJson() {
|
|
|
657
764
|
agentId: jacsId,
|
|
658
765
|
};
|
|
659
766
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
767
|
+
// =============================================================================
|
|
768
|
+
// Setup Instructions
|
|
769
|
+
// =============================================================================
|
|
770
|
+
async function getSetupInstructions(domain, ttl = 3600) {
|
|
771
|
+
const agent = requireAgent();
|
|
772
|
+
const json = await agent.getSetupInstructions(domain, ttl);
|
|
773
|
+
return JSON.parse(json);
|
|
774
|
+
}
|
|
775
|
+
function getSetupInstructionsSync(domain, ttl = 3600) {
|
|
776
|
+
const agent = requireAgent();
|
|
777
|
+
const json = agent.getSetupInstructionsSync(domain, ttl);
|
|
778
|
+
return JSON.parse(json);
|
|
779
|
+
}
|
|
780
|
+
// =============================================================================
|
|
781
|
+
// HAI Registration
|
|
782
|
+
// =============================================================================
|
|
668
783
|
async function registerWithHai(options) {
|
|
669
784
|
if (!agentInfo) {
|
|
670
|
-
throw new Error('No agent loaded. Call load()
|
|
785
|
+
throw new Error('No agent loaded. Call quickstart() for zero-config setup, or load() for a persistent agent.');
|
|
671
786
|
}
|
|
672
787
|
const apiKey = options?.apiKey ?? process.env.HAI_API_KEY;
|
|
673
788
|
if (!apiKey) {
|
|
@@ -705,205 +820,73 @@ async function registerWithHai(options) {
|
|
|
705
820
|
signatures,
|
|
706
821
|
};
|
|
707
822
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
*
|
|
711
|
-
* @param document - The document to create an agreement on (object or JSON string)
|
|
712
|
-
* @param agentIds - List of agent IDs required to sign
|
|
713
|
-
* @param question - Optional question or purpose of the agreement
|
|
714
|
-
* @param context - Optional additional context for signers
|
|
715
|
-
* @param fieldName - Optional custom field name for the agreement (default: "jacsAgreement")
|
|
716
|
-
* @returns SignedDocument containing the agreement document
|
|
717
|
-
*
|
|
718
|
-
* @example
|
|
719
|
-
* ```typescript
|
|
720
|
-
* const agreement = jacs.createAgreement(
|
|
721
|
-
* { proposal: 'Merge codebase' },
|
|
722
|
-
* ['agent-1-uuid', 'agent-2-uuid'],
|
|
723
|
-
* 'Do you approve this merge?',
|
|
724
|
-
* 'This will combine repositories A and B'
|
|
725
|
-
* );
|
|
726
|
-
* ```
|
|
727
|
-
*/
|
|
728
|
-
function createAgreement(document, agentIds, question, context, fieldName) {
|
|
729
|
-
if (!globalAgent) {
|
|
730
|
-
throw new Error('No agent loaded. Call load() first.');
|
|
731
|
-
}
|
|
823
|
+
async function createAgreement(document, agentIds, question, context, fieldName) {
|
|
824
|
+
const agent = requireAgent();
|
|
732
825
|
const docString = normalizeDocumentInput(document);
|
|
733
|
-
const result =
|
|
734
|
-
|
|
735
|
-
return {
|
|
736
|
-
raw: result,
|
|
737
|
-
documentId: doc.jacsId || '',
|
|
738
|
-
agentId: doc.jacsSignature?.agentID || '',
|
|
739
|
-
timestamp: doc.jacsSignature?.date || '',
|
|
740
|
-
};
|
|
826
|
+
const result = await agent.createAgreement(docString, agentIds, question || null, context || null, fieldName || null);
|
|
827
|
+
return parseSignedResult(result);
|
|
741
828
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
*
|
|
745
|
-
* @param document - The agreement document to sign (object or JSON string)
|
|
746
|
-
* @param fieldName - Optional custom field name for the agreement (default: "jacsAgreement")
|
|
747
|
-
* @returns SignedDocument with this agent's signature added
|
|
748
|
-
*
|
|
749
|
-
* @example
|
|
750
|
-
* ```typescript
|
|
751
|
-
* // Receive agreement from another party
|
|
752
|
-
* const signedByMe = jacs.signAgreement(agreementDoc);
|
|
753
|
-
* // Send back to coordinator or next signer
|
|
754
|
-
* ```
|
|
755
|
-
*/
|
|
756
|
-
function signAgreement(document, fieldName) {
|
|
757
|
-
if (!globalAgent) {
|
|
758
|
-
throw new Error('No agent loaded. Call load() first.');
|
|
759
|
-
}
|
|
829
|
+
function createAgreementSync(document, agentIds, question, context, fieldName) {
|
|
830
|
+
const agent = requireAgent();
|
|
760
831
|
const docString = normalizeDocumentInput(document);
|
|
761
|
-
const result =
|
|
762
|
-
|
|
763
|
-
return {
|
|
764
|
-
raw: result,
|
|
765
|
-
documentId: doc.jacsId || '',
|
|
766
|
-
agentId: doc.jacsSignature?.agentID || '',
|
|
767
|
-
timestamp: doc.jacsSignature?.date || '',
|
|
768
|
-
};
|
|
832
|
+
const result = agent.createAgreementSync(docString, agentIds, question || null, context || null, fieldName || null);
|
|
833
|
+
return parseSignedResult(result);
|
|
769
834
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
* ```typescript
|
|
779
|
-
* const status = jacs.checkAgreement(agreementDoc);
|
|
780
|
-
* if (status.complete) {
|
|
781
|
-
* console.log('All parties have signed!');
|
|
782
|
-
* } else {
|
|
783
|
-
* console.log(`Waiting for: ${status.pending.join(', ')}`);
|
|
784
|
-
* }
|
|
785
|
-
* ```
|
|
786
|
-
*/
|
|
787
|
-
function checkAgreement(document, fieldName) {
|
|
788
|
-
if (!globalAgent) {
|
|
789
|
-
throw new Error('No agent loaded. Call load() first.');
|
|
790
|
-
}
|
|
835
|
+
async function signAgreement(document, fieldName) {
|
|
836
|
+
const agent = requireAgent();
|
|
837
|
+
const docString = normalizeDocumentInput(document);
|
|
838
|
+
const result = await agent.signAgreement(docString, fieldName || null);
|
|
839
|
+
return parseSignedResult(result);
|
|
840
|
+
}
|
|
841
|
+
function signAgreementSync(document, fieldName) {
|
|
842
|
+
const agent = requireAgent();
|
|
791
843
|
const docString = normalizeDocumentInput(document);
|
|
792
|
-
const result =
|
|
844
|
+
const result = agent.signAgreementSync(docString, fieldName || null);
|
|
845
|
+
return parseSignedResult(result);
|
|
846
|
+
}
|
|
847
|
+
async function checkAgreement(document, fieldName) {
|
|
848
|
+
const agent = requireAgent();
|
|
849
|
+
const docString = normalizeDocumentInput(document);
|
|
850
|
+
const result = await agent.checkAgreement(docString, fieldName || null);
|
|
851
|
+
return JSON.parse(result);
|
|
852
|
+
}
|
|
853
|
+
function checkAgreementSync(document, fieldName) {
|
|
854
|
+
const agent = requireAgent();
|
|
855
|
+
const docString = normalizeDocumentInput(document);
|
|
856
|
+
const result = agent.checkAgreementSync(docString, fieldName || null);
|
|
793
857
|
return JSON.parse(result);
|
|
794
858
|
}
|
|
795
859
|
// =============================================================================
|
|
796
|
-
// Trust Store Functions
|
|
860
|
+
// Trust Store Functions (sync-only — fast local file lookups)
|
|
797
861
|
// =============================================================================
|
|
798
|
-
/**
|
|
799
|
-
* Add an agent to the local trust store.
|
|
800
|
-
*
|
|
801
|
-
* The trust store is a local list of agents you trust. When verifying
|
|
802
|
-
* documents from known agents, the trust store provides signer names
|
|
803
|
-
* and allows quick lookups.
|
|
804
|
-
*
|
|
805
|
-
* @param agentJson - The agent's JSON document (from their exportAgent())
|
|
806
|
-
* @returns The trusted agent's ID
|
|
807
|
-
*
|
|
808
|
-
* @example
|
|
809
|
-
* ```typescript
|
|
810
|
-
* const trustedId = jacs.trustAgent(partnerAgentJson);
|
|
811
|
-
* console.log(`Trusted agent: ${trustedId}`);
|
|
812
|
-
* ```
|
|
813
|
-
*/
|
|
814
862
|
function trustAgent(agentJson) {
|
|
815
863
|
return (0, index_1.trustAgent)(agentJson);
|
|
816
864
|
}
|
|
817
|
-
/**
|
|
818
|
-
* List all trusted agent IDs in the local trust store.
|
|
819
|
-
*
|
|
820
|
-
* @returns Array of trusted agent UUIDs
|
|
821
|
-
*
|
|
822
|
-
* @example
|
|
823
|
-
* ```typescript
|
|
824
|
-
* const trustedIds = jacs.listTrustedAgents();
|
|
825
|
-
* console.log(`${trustedIds.length} trusted agents`);
|
|
826
|
-
* ```
|
|
827
|
-
*/
|
|
828
865
|
function listTrustedAgents() {
|
|
829
866
|
return (0, index_1.listTrustedAgents)();
|
|
830
867
|
}
|
|
831
|
-
/**
|
|
832
|
-
* Remove an agent from the local trust store.
|
|
833
|
-
*
|
|
834
|
-
* @param agentId - The agent UUID to remove
|
|
835
|
-
*
|
|
836
|
-
* @example
|
|
837
|
-
* ```typescript
|
|
838
|
-
* jacs.untrustAgent('550e8400-e29b-41d4-a716-446655440000');
|
|
839
|
-
* ```
|
|
840
|
-
*/
|
|
841
868
|
function untrustAgent(agentId) {
|
|
842
869
|
(0, index_1.untrustAgent)(agentId);
|
|
843
870
|
}
|
|
844
|
-
/**
|
|
845
|
-
* Check if an agent is in the local trust store.
|
|
846
|
-
*
|
|
847
|
-
* @param agentId - The agent UUID to check
|
|
848
|
-
* @returns true if the agent is trusted
|
|
849
|
-
*
|
|
850
|
-
* @example
|
|
851
|
-
* ```typescript
|
|
852
|
-
* if (jacs.isTrusted(signerId)) {
|
|
853
|
-
* console.log('Signer is in our trust store');
|
|
854
|
-
* }
|
|
855
|
-
* ```
|
|
856
|
-
*/
|
|
857
871
|
function isTrusted(agentId) {
|
|
858
872
|
return (0, index_1.isTrusted)(agentId);
|
|
859
873
|
}
|
|
860
|
-
/**
|
|
861
|
-
* Get a trusted agent's full JSON document from the trust store.
|
|
862
|
-
*
|
|
863
|
-
* @param agentId - The agent UUID to retrieve
|
|
864
|
-
* @returns The agent's JSON document as a string
|
|
865
|
-
*
|
|
866
|
-
* @example
|
|
867
|
-
* ```typescript
|
|
868
|
-
* const agentDoc = JSON.parse(jacs.getTrustedAgent(agentId));
|
|
869
|
-
* console.log(`Agent name: ${agentDoc.jacsAgentName}`);
|
|
870
|
-
* ```
|
|
871
|
-
*/
|
|
872
874
|
function getTrustedAgent(agentId) {
|
|
873
875
|
return (0, index_1.getTrustedAgent)(agentId);
|
|
874
876
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
*
|
|
882
|
-
* @example
|
|
883
|
-
* ```typescript
|
|
884
|
-
* const result = jacs.audit();
|
|
885
|
-
* console.log(`Risks: ${result.risks.length}, Status: ${result.overall_status}`);
|
|
886
|
-
* ```
|
|
887
|
-
*/
|
|
888
|
-
function audit(options) {
|
|
889
|
-
const json = (0, index_1.audit)(options?.configPath ?? undefined, options?.recentN ?? undefined);
|
|
877
|
+
async function audit(options) {
|
|
878
|
+
const json = await (0, index_1.audit)(options?.configPath ?? undefined, options?.recentN ?? undefined);
|
|
879
|
+
return JSON.parse(json);
|
|
880
|
+
}
|
|
881
|
+
function auditSync(options) {
|
|
882
|
+
const json = (0, index_1.auditSync)(options?.configPath ?? undefined, options?.recentN ?? undefined);
|
|
890
883
|
return JSON.parse(json);
|
|
891
884
|
}
|
|
892
885
|
// =============================================================================
|
|
893
|
-
// Verify link
|
|
886
|
+
// Verify link
|
|
894
887
|
// =============================================================================
|
|
895
|
-
/** Max length for a full verify URL (scheme + host + path + ?s=...). */
|
|
896
888
|
exports.MAX_VERIFY_URL_LEN = 2048;
|
|
897
|
-
/** Max UTF-8 byte length of a document that fits in a verify link. */
|
|
898
889
|
exports.MAX_VERIFY_DOCUMENT_BYTES = 1515;
|
|
899
|
-
/**
|
|
900
|
-
* Build a verification URL for a signed JACS document (e.g. https://hai.ai/jacs/verify?s=...).
|
|
901
|
-
* Uses URL-safe base64. Throws if the URL would exceed MAX_VERIFY_URL_LEN.
|
|
902
|
-
*
|
|
903
|
-
* @param document - Full signed JACS document string (JSON)
|
|
904
|
-
* @param baseUrl - Base URL of the verifier (no trailing slash). Default "https://hai.ai"
|
|
905
|
-
* @returns Full URL: {baseUrl}/jacs/verify?s={base64url(document)}
|
|
906
|
-
*/
|
|
907
890
|
function generateVerifyLink(document, baseUrl = 'https://hai.ai') {
|
|
908
891
|
const base = baseUrl.replace(/\/+$/, '');
|
|
909
892
|
const encoded = Buffer.from(document, 'utf8')
|