@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/simple.js CHANGED
@@ -2,45 +2,25 @@
2
2
  /**
3
3
  * JACS Simplified API for TypeScript/JavaScript
4
4
  *
5
- * A streamlined interface for the most common JACS operations:
6
- * - load(): Load an existing agent from config
7
- * - verifySelf(): Verify the loaded agent's integrity
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
- * // Use standalone hash function
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.verifyString = exports.hashString = exports.JacsAgent = void 0;
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
- * Creates a new JACS agent with cryptographic keys.
144
- *
145
- * This is a fully programmatic API that does not require interactive input.
146
- * The password must be provided directly or via the JACS_PRIVATE_KEY_PASSWORD
147
- * environment variable.
148
- *
149
- * @param options - Agent creation options
150
- * @returns AgentInfo containing the agent ID, name, and file paths
151
- *
152
- * @example
153
- * ```typescript
154
- * const agent = jacs.create({
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
- // Read config to get agent info
198
- const config = JSON.parse(fs.readFileSync(resolvedConfigPath, 'utf8'));
199
- const agentIdVersion = config.jacs_agent_id_and_version || '';
200
- const [agentId, version] = agentIdVersion.split(':');
201
- const keyDir = resolveConfigRelativePath(resolvedConfigPath, config.jacs_key_directory || './jacs_keys');
202
- agentInfo = {
203
- agentId: agentId || '',
204
- name: config.name || '',
205
- publicKeyPath: path.join(keyDir, 'jacs.public.pem'),
206
- configPath: resolvedConfigPath,
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
- if (!globalAgent) {
225
- throw new Error('No agent loaded. Call load() first.');
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
- globalAgent.verifyAgent();
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
- if (!globalAgent) {
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 = globalAgent.createDocument(JSON.stringify(docContent), null, null, true, // no_save
270
- null, null);
271
- // Parse result
272
- const doc = JSON.parse(result);
273
- return {
274
- raw: result,
275
- documentId: doc.jacsId || '',
276
- agentId: doc.jacsSignature?.agentID || '',
277
- timestamp: doc.jacsSignature?.date || '',
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
- if (!globalAgent) {
301
- throw new Error('No agent loaded. Call load() first.');
302
- }
303
- const dataString = typeof newAgentData === 'string'
304
- ? newAgentData
305
- : JSON.stringify(newAgentData);
306
- return globalAgent.updateAgent(dataString);
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
- if (!globalAgent) {
334
- throw new Error('No agent loaded. Call load() first.');
335
- }
336
- const dataString = typeof newDocumentData === 'string'
337
- ? newDocumentData
338
- : JSON.stringify(newDocumentData);
339
- const result = globalAgent.updateDocument(documentId, dataString, attachments || null, embed ?? null);
340
- const doc = JSON.parse(result);
341
- return {
342
- raw: result,
343
- documentId: doc.jacsId || '',
344
- agentId: doc.jacsSignature?.agentID || '',
345
- timestamp: doc.jacsSignature?.date || '',
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
- if (!globalAgent) {
363
- throw new Error('No agent loaded. Call load() first.');
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 = globalAgent.createDocument(JSON.stringify(docContent), null, null, true, // no_save
375
- filePath, embed);
376
- // Parse result
377
- const doc = JSON.parse(result);
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
- raw: result,
380
- documentId: doc.jacsId || '',
381
- agentId: doc.jacsSignature?.agentID || '',
382
- timestamp: doc.jacsSignature?.date || '',
583
+ valid: r.valid,
584
+ signerId: r.signerId,
585
+ timestamp: '',
586
+ attachments: [],
587
+ errors: [],
383
588
  };
384
589
  }
385
590
  /**
386
- * Verifies a signed document and extracts its content.
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 verify(signedDocument) {
400
- if (!globalAgent) {
401
- throw new Error('No agent loaded. Call load() first.');
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
- `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 ? '...' : ''}'`
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
- doc = JSON.parse(signedDocument);
419
- }
420
- catch (e) {
607
+ await agent.verifyDocumentById(documentId);
421
608
  return {
422
- valid: false,
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: doc.jacsSignature?.agentID || '',
452
- timestamp: doc.jacsSignature?.date || '',
622
+ signerId: '',
623
+ timestamp: '',
453
624
  attachments: [],
454
625
  errors: [String(e)],
455
626
  };
456
627
  }
457
628
  }
458
629
  /**
459
- * Verify a signed JACS document without loading an agent.
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 verifyById(documentId) {
501
- if (!globalAgent) {
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
- globalAgent.verifyDocumentById(documentId);
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
- if (!globalAgent) {
549
- throw new Error('No agent loaded. Call load() first.');
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
- * Get the loaded agent's public key in PEM format.
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() first.');
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() first.');
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
- * Returns the DNS TXT record line for the loaded agent (for DNS-based discovery).
617
- * Format: _v1.agent.jacs.{domain}. TTL IN TXT "v=hai.ai; jacs_agent_id=...; alg=SHA-256; enc=base64; jac_public_key_hash=..."
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() first.');
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() first.');
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
- * Register the loaded agent with HAI.ai.
662
- * Requires a loaded agent (uses exportAgent() for the payload).
663
- * Calls POST {haiUrl}/api/v1/agents/register with Bearer token and agent JSON.
664
- *
665
- * @param options - apiKey (or HAI_API_KEY env), haiUrl (default "https://hai.ai"), preview
666
- * @returns HaiRegistrationResult with agentId, jacsId, dnsVerified, signatures
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() first.');
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
- * Creates a multi-party agreement that requires signatures from multiple agents.
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 = globalAgent.createAgreement(docString, agentIds, question || null, context || null, fieldName || null);
734
- const doc = JSON.parse(result);
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
- * Signs an existing multi-party agreement.
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 = globalAgent.signAgreement(docString, fieldName || null);
762
- const doc = JSON.parse(result);
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
- * Checks the status of a multi-party agreement.
772
- *
773
- * @param document - The agreement document to check (object or JSON string)
774
- * @param fieldName - Optional custom field name for the agreement (default: "jacsAgreement")
775
- * @returns AgreementStatus with completion status and signer details
776
- *
777
- * @example
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 = globalAgent.checkAgreement(docString, fieldName || null);
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
- * Run a read-only security audit and health checks.
877
- * Returns an object with risks, health_checks, summary, and related fields.
878
- *
879
- * @param options - Optional config path and recent document count
880
- * @returns Audit result object (risks, health_checks, summary, overall_status)
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 (HAI / public verification URLs)
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')