@haiai/haiai 0.1.2 → 0.1.4
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 +1 -1
- package/dist/cjs/client.js +260 -106
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.js +9 -6
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/signing.js +58 -37
- package/dist/cjs/signing.js.map +1 -1
- package/dist/cjs/verify.js +4 -7
- package/dist/cjs/verify.js.map +1 -1
- package/dist/esm/client.js +260 -106
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.js +9 -6
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/signing.js +58 -37
- package/dist/esm/signing.js.map +1 -1
- package/dist/esm/verify.js +4 -7
- package/dist/esm/verify.js.map +1 -1
- package/dist/types/client.d.ts +8 -2
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/errors.d.ts +5 -3
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/signing.d.ts +15 -7
- package/dist/types/signing.d.ts.map +1 -1
- package/dist/types/verify.d.ts.map +1 -1
- package/npm/@haiai/cli-darwin-arm64/package.json +1 -1
- package/npm/@haiai/cli-darwin-x64/package.json +1 -1
- package/npm/@haiai/cli-linux-arm64/package.json +1 -1
- package/npm/@haiai/cli-linux-x64/package.json +1 -1
- package/npm/@haiai/cli-win32-x64/package.json +1 -1
- package/package.json +7 -7
- package/src/client.ts +304 -120
- package/src/errors.ts +10 -6
- package/src/signing.ts +84 -36
- package/src/verify.ts +6 -6
- package/tests/client-path-escaping.test.ts +30 -0
- package/tests/client-register.test.ts +18 -1
- package/tests/crypto-contract.test.ts +74 -0
- package/tests/error-contract.test.ts +54 -0
- package/tests/security-contract.test.ts +124 -0
- package/tests/security.test.ts +3 -3
- package/tests/signing.test.ts +1 -1
package/README.md
CHANGED
package/dist/cjs/client.js
CHANGED
|
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.HaiClient = void 0;
|
|
37
37
|
const errors_js_1 = require("./errors.js");
|
|
38
|
+
const node_crypto_1 = require("node:crypto");
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
38
40
|
const signing_js_1 = require("./signing.js");
|
|
39
41
|
const config_js_1 = require("./config.js");
|
|
40
42
|
const jacs_1 = require("@hai.ai/jacs");
|
|
@@ -55,6 +57,75 @@ function normalizeKeyText(raw, blockType) {
|
|
|
55
57
|
}
|
|
56
58
|
return armorKeyData(raw, blockType);
|
|
57
59
|
}
|
|
60
|
+
const credentialWorkspaceDirs = new Set();
|
|
61
|
+
let credentialWorkspaceCleanupRegistered = false;
|
|
62
|
+
function registerCredentialWorkspace(dir) {
|
|
63
|
+
credentialWorkspaceDirs.add(dir);
|
|
64
|
+
if (credentialWorkspaceCleanupRegistered) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
credentialWorkspaceCleanupRegistered = true;
|
|
68
|
+
process.once('exit', () => {
|
|
69
|
+
for (const workspaceDir of credentialWorkspaceDirs) {
|
|
70
|
+
try {
|
|
71
|
+
(0, node_fs_1.rmSync)(workspaceDir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Best-effort temp workspace cleanup.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
credentialWorkspaceDirs.clear();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function createCredentialSigner(privateKeyPem, privateKeyPassphrase) {
|
|
81
|
+
const privateKey = privateKeyPassphrase
|
|
82
|
+
? (0, node_crypto_1.createPrivateKey)({ key: privateKeyPem, format: 'pem', passphrase: privateKeyPassphrase })
|
|
83
|
+
: (0, node_crypto_1.createPrivateKey)({ key: privateKeyPem, format: 'pem' });
|
|
84
|
+
const keyType = privateKey.asymmetricKeyType;
|
|
85
|
+
if (keyType !== 'ed25519' && keyType !== 'rsa' && keyType !== 'rsa-pss') {
|
|
86
|
+
throw new errors_js_1.AuthenticationError(`fromCredentials does not support ${keyType ?? 'this'} private key type in this runtime. ` +
|
|
87
|
+
'Use a config-backed client for other JACS key types.');
|
|
88
|
+
}
|
|
89
|
+
const publicKeyPem = (0, node_crypto_1.createPublicKey)(privateKey)
|
|
90
|
+
.export({ format: 'pem', type: 'spki' })
|
|
91
|
+
.toString()
|
|
92
|
+
.trim();
|
|
93
|
+
return {
|
|
94
|
+
algorithm: keyType === 'ed25519' ? 'ring-Ed25519' : 'RSA-PSS',
|
|
95
|
+
privateKeyPem: privateKeyPem.trim(),
|
|
96
|
+
publicKeyPem,
|
|
97
|
+
signStringSync(message) {
|
|
98
|
+
const data = Buffer.from(message, 'utf-8');
|
|
99
|
+
const signature = keyType === 'ed25519'
|
|
100
|
+
? (0, node_crypto_1.sign)(null, data, privateKey)
|
|
101
|
+
: (0, node_crypto_1.sign)('sha256', data, {
|
|
102
|
+
key: privateKey,
|
|
103
|
+
padding: node_crypto_1.constants.RSA_PKCS1_PSS_PADDING,
|
|
104
|
+
saltLength: node_crypto_1.constants.RSA_PSS_SALTLEN_DIGEST,
|
|
105
|
+
});
|
|
106
|
+
return signature.toString('base64');
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function withPrivateKeyPassphrase(passphrase, run) {
|
|
111
|
+
const envKey = 'JACS_PRIVATE_KEY_PASSWORD';
|
|
112
|
+
const hadOriginal = Object.prototype.hasOwnProperty.call(process.env, envKey);
|
|
113
|
+
const original = process.env[envKey];
|
|
114
|
+
if (passphrase) {
|
|
115
|
+
process.env[envKey] = passphrase;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
return await run();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
if (hadOriginal) {
|
|
122
|
+
process.env[envKey] = original;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
delete process.env[envKey];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
58
129
|
/**
|
|
59
130
|
* HAI platform client.
|
|
60
131
|
*
|
|
@@ -70,8 +141,11 @@ function normalizeKeyText(raw, blockType) {
|
|
|
70
141
|
*/
|
|
71
142
|
class HaiClient {
|
|
72
143
|
config;
|
|
144
|
+
configPath = null;
|
|
73
145
|
/** JACS native agent for all cryptographic operations. */
|
|
74
146
|
agent;
|
|
147
|
+
/** Explicit credential signer used when the runtime cannot import a PEM into JACS directly. */
|
|
148
|
+
credentialSigner = null;
|
|
75
149
|
baseUrl;
|
|
76
150
|
timeout;
|
|
77
151
|
maxRetries;
|
|
@@ -111,33 +185,64 @@ class HaiClient {
|
|
|
111
185
|
const resolvedConfigPath = resolve(configPath);
|
|
112
186
|
client.agent = new jacs_1.JacsAgent();
|
|
113
187
|
await client.agent.load(resolvedConfigPath);
|
|
188
|
+
client.configPath = resolvedConfigPath;
|
|
114
189
|
return client;
|
|
115
190
|
}
|
|
116
191
|
/**
|
|
117
192
|
* Create a HaiClient directly from a JACS ID and PEM-encoded private key.
|
|
118
193
|
* Useful for testing or programmatic setup without config files.
|
|
119
194
|
*
|
|
120
|
-
* Creates a temporary JACS
|
|
121
|
-
*
|
|
195
|
+
* Creates a temporary JACS-shaped workspace for compatibility with
|
|
196
|
+
* config-based flows. When this runtime cannot import PEM material into
|
|
197
|
+
* JACS directly, signing uses the supplied credentials and verification
|
|
198
|
+
* still delegates to JACS.
|
|
122
199
|
*/
|
|
123
200
|
static async fromCredentials(jacsId, privateKeyPem, options) {
|
|
124
201
|
const client = new HaiClient(options);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
client.
|
|
202
|
+
const signer = createCredentialSigner(privateKeyPem, options?.privateKeyPassphrase);
|
|
203
|
+
// Store the caller's PEM material for exportKeys/rotateKeys compatibility.
|
|
204
|
+
client._privateKeyPem = signer.privateKeyPem;
|
|
205
|
+
client.privateKeyPem = signer.privateKeyPem;
|
|
206
|
+
client._publicKeyPem = signer.publicKeyPem;
|
|
128
207
|
client._privateKeyPassphrase = options?.privateKeyPassphrase;
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
208
|
+
client.credentialSigner = signer;
|
|
209
|
+
// Materialize a minimal JACS-shaped workspace so file-based flows (for
|
|
210
|
+
// example rotateKeys) have a stable key directory, even though this
|
|
211
|
+
// runtime still lacks a direct "load PEM credentials into JacsAgent"
|
|
212
|
+
// bootstrap path.
|
|
213
|
+
const { mkdir, mkdtemp, writeFile } = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
214
|
+
const { join } = await Promise.resolve().then(() => __importStar(require('node:path')));
|
|
215
|
+
const { tmpdir } = await Promise.resolve().then(() => __importStar(require('node:os')));
|
|
216
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'haiai-creds-'));
|
|
217
|
+
registerCredentialWorkspace(tempDir);
|
|
218
|
+
const keyDir = join(tempDir, 'keys');
|
|
219
|
+
const dataDir = join(tempDir, 'data');
|
|
220
|
+
await mkdir(keyDir, { recursive: true });
|
|
221
|
+
await mkdir(dataDir, { recursive: true });
|
|
222
|
+
await writeFile(join(keyDir, 'agent_private_key.pem'), `${signer.privateKeyPem}\n`, { mode: 0o600 });
|
|
223
|
+
await writeFile(join(keyDir, 'agent_public_key.pem'), `${signer.publicKeyPem}\n`, { mode: 0o644 });
|
|
224
|
+
const configPath = join(tempDir, 'jacs.config.json');
|
|
225
|
+
const configJson = {
|
|
136
226
|
jacsAgentName: jacsId,
|
|
137
|
-
jacsAgentVersion:
|
|
138
|
-
jacsKeyDir: '',
|
|
227
|
+
jacsAgentVersion: '1.0.0',
|
|
228
|
+
jacsKeyDir: './keys',
|
|
229
|
+
jacsPrivateKeyPath: './keys/agent_private_key.pem',
|
|
139
230
|
jacsId,
|
|
231
|
+
jacs_data_directory: './data',
|
|
232
|
+
jacs_key_directory: './keys',
|
|
233
|
+
jacs_agent_private_key_filename: 'agent_private_key.pem',
|
|
234
|
+
jacs_agent_public_key_filename: 'agent_public_key.pem',
|
|
235
|
+
jacs_agent_key_algorithm: signer.algorithm,
|
|
236
|
+
jacs_default_storage: 'fs',
|
|
140
237
|
};
|
|
238
|
+
await writeFile(configPath, JSON.stringify(configJson, null, 2) + '\n');
|
|
239
|
+
client.config = await (0, config_js_1.loadConfig)(configPath);
|
|
240
|
+
client.configPath = configPath;
|
|
241
|
+
// Keep a lightweight JACS agent around for canonicalization and explicit
|
|
242
|
+
// verifyStringSync checks. Signing is handled by the credentialSigner above
|
|
243
|
+
// so fromCredentials actually uses the caller's key material.
|
|
244
|
+
client.agent = new jacs_1.JacsAgent();
|
|
245
|
+
client.agent.ephemeralSync(signer.algorithm);
|
|
141
246
|
return client;
|
|
142
247
|
}
|
|
143
248
|
/** The agent's JACS ID. */
|
|
@@ -186,6 +291,9 @@ class HaiClient {
|
|
|
186
291
|
clearAgentKeyCache() {
|
|
187
292
|
this.keyCache.clear();
|
|
188
293
|
}
|
|
294
|
+
activeSigner() {
|
|
295
|
+
return this.credentialSigner ?? this.agent;
|
|
296
|
+
}
|
|
189
297
|
// ---------------------------------------------------------------------------
|
|
190
298
|
// Auth helpers
|
|
191
299
|
// ---------------------------------------------------------------------------
|
|
@@ -201,10 +309,16 @@ class HaiClient {
|
|
|
201
309
|
}
|
|
202
310
|
/** Sign a UTF-8 message with the agent's private key via JACS. Returns base64. */
|
|
203
311
|
signMessage(message) {
|
|
204
|
-
return this.
|
|
312
|
+
return this.activeSigner().signStringSync(message);
|
|
205
313
|
}
|
|
206
314
|
/** Build the JACS Authorization header value string. */
|
|
207
315
|
buildAuthHeader() {
|
|
316
|
+
if (this.credentialSigner) {
|
|
317
|
+
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
318
|
+
const message = `${this.jacsId}:${timestamp}`;
|
|
319
|
+
const signature = this.credentialSigner.signStringSync(message);
|
|
320
|
+
return `JACS ${this.jacsId}:${timestamp}:${signature}`;
|
|
321
|
+
}
|
|
208
322
|
// Prefer JACS binding delegation
|
|
209
323
|
if ('buildAuthHeaderSync' in this.agent && typeof this.agent.buildAuthHeaderSync === 'function') {
|
|
210
324
|
return this.agent.buildAuthHeaderSync();
|
|
@@ -336,7 +450,7 @@ class HaiClient {
|
|
|
336
450
|
}
|
|
337
451
|
// Sign canonical JSON via JACS
|
|
338
452
|
const canonical = (0, signing_js_1.canonicalJson)(agentDoc);
|
|
339
|
-
const signature = this.
|
|
453
|
+
const signature = this.activeSigner().signStringSync(canonical);
|
|
340
454
|
agentDoc.jacsSignature.signature = signature;
|
|
341
455
|
agentJson = JSON.stringify(agentDoc);
|
|
342
456
|
}
|
|
@@ -408,7 +522,7 @@ class HaiClient {
|
|
|
408
522
|
// Build old-key auth header BEFORE rotation (chain of trust)
|
|
409
523
|
const oldAuthTimestamp = Math.floor(Date.now() / 1000).toString();
|
|
410
524
|
const oldAuthMessage = `${jacsId}:${oldVersion}:${oldAuthTimestamp}`;
|
|
411
|
-
const oldAuthSig = this.
|
|
525
|
+
const oldAuthSig = this.activeSigner().signStringSync(oldAuthMessage);
|
|
412
526
|
const oldAgent = this.agent;
|
|
413
527
|
// Find existing private key file
|
|
414
528
|
const candidates = [
|
|
@@ -452,18 +566,29 @@ class HaiClient {
|
|
|
452
566
|
const newVersion = randomUUID();
|
|
453
567
|
const generatedKeyDir = await mkdtemp(join(tmpdir(), 'haiai-rotate-'));
|
|
454
568
|
let newPublicKeyPem;
|
|
569
|
+
let generatedSignerAgent = null;
|
|
455
570
|
try {
|
|
456
571
|
const resultJson = (0, jacs_1.createAgentSync)(this.config.jacsAgentName, passphrase, 'pq2025', null, // data dir
|
|
457
|
-
generatedKeyDir,
|
|
458
|
-
null, // agent type
|
|
572
|
+
generatedKeyDir, join(generatedKeyDir, 'jacs.config.json'), null, // agent type
|
|
459
573
|
this.config.description
|
|
460
574
|
?? 'Agent registered via Node SDK', null, // domain
|
|
461
575
|
null);
|
|
462
576
|
const result = JSON.parse(resultJson);
|
|
577
|
+
const generatedConfigPath = result.config_path;
|
|
463
578
|
const newPubKeyPath = result.public_key_path || join(keyDir, 'jacs.public.pem');
|
|
464
579
|
const newPrivKeyPath = result.private_key_path || join(keyDir, 'jacs.private.pem.enc');
|
|
465
580
|
const newPublicKeyRaw = await readF(newPubKeyPath);
|
|
466
581
|
newPublicKeyPem = normalizeKeyText(newPublicKeyRaw, 'PUBLIC KEY');
|
|
582
|
+
if (generatedConfigPath) {
|
|
583
|
+
const nextAgent = new jacs_1.JacsAgent();
|
|
584
|
+
try {
|
|
585
|
+
await withPrivateKeyPassphrase(passphrase || undefined, () => nextAgent.load(generatedConfigPath));
|
|
586
|
+
generatedSignerAgent = nextAgent;
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
generatedSignerAgent = null;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
467
592
|
if (newPrivKeyPath !== privKeyPath) {
|
|
468
593
|
await copyFile(newPrivKeyPath, privKeyPath);
|
|
469
594
|
}
|
|
@@ -473,8 +598,9 @@ class HaiClient {
|
|
|
473
598
|
else {
|
|
474
599
|
await writeFile(pubKeyPath, `${newPublicKeyPem}\n`);
|
|
475
600
|
}
|
|
476
|
-
this._privateKeyPem = (await readF(privKeyPath)).
|
|
601
|
+
this._privateKeyPem = (await readF(privKeyPath, 'utf-8')).trim();
|
|
477
602
|
this.privateKeyPem = this._privateKeyPem;
|
|
603
|
+
this._publicKeyPem = newPublicKeyPem.trim();
|
|
478
604
|
}
|
|
479
605
|
catch (err) {
|
|
480
606
|
// Rollback: restore archived keys
|
|
@@ -504,19 +630,42 @@ class HaiClient {
|
|
|
504
630
|
},
|
|
505
631
|
};
|
|
506
632
|
// Reload the agent with new keys for signing
|
|
507
|
-
const configPath = resolve(process.env.JACS_CONFIG_PATH ?? './jacs.config.json');
|
|
633
|
+
const configPath = resolve(process.env.JACS_CONFIG_PATH ?? this.configPath ?? './jacs.config.json');
|
|
508
634
|
const reloadedAgent = new jacs_1.JacsAgent();
|
|
635
|
+
let reloadedWithNewKeys = false;
|
|
509
636
|
try {
|
|
510
|
-
await reloadedAgent.load(configPath);
|
|
637
|
+
await withPrivateKeyPassphrase(passphrase || undefined, () => reloadedAgent.load(configPath));
|
|
511
638
|
this.agent = reloadedAgent;
|
|
639
|
+
reloadedWithNewKeys = true;
|
|
512
640
|
}
|
|
513
641
|
catch {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
642
|
+
if (generatedSignerAgent) {
|
|
643
|
+
this.agent = generatedSignerAgent;
|
|
644
|
+
reloadedWithNewKeys = true;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
// Fall back to the currently loaded in-memory agent when we cannot
|
|
648
|
+
// hydrate the new key material in this process.
|
|
649
|
+
this.agent = oldAgent;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
let rotatedSigner = this.agent;
|
|
653
|
+
if (this.credentialSigner) {
|
|
654
|
+
try {
|
|
655
|
+
this.credentialSigner = createCredentialSigner(this._privateKeyPem, passphrase || undefined);
|
|
656
|
+
rotatedSigner = this.credentialSigner;
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
// Some runtimes can generate post-quantum keys before they can
|
|
660
|
+
// re-import them through the local PEM compatibility path. In that
|
|
661
|
+
// case we keep the best available in-memory signer and preserve the
|
|
662
|
+
// local rotation result rather than failing the entire operation.
|
|
663
|
+
this.credentialSigner = null;
|
|
664
|
+
rotatedSigner = this.agent;
|
|
665
|
+
}
|
|
517
666
|
}
|
|
518
667
|
const canonical = (0, signing_js_1.canonicalJson)(agentDoc);
|
|
519
|
-
const signature =
|
|
668
|
+
const signature = rotatedSigner.signStringSync(canonical);
|
|
520
669
|
agentDoc.jacsSignature.signature = signature;
|
|
521
670
|
const signedAgentJson = JSON.stringify(agentDoc, null, 2);
|
|
522
671
|
// 4. Compute new public key hash via JACS
|
|
@@ -755,7 +904,7 @@ class HaiClient {
|
|
|
755
904
|
},
|
|
756
905
|
};
|
|
757
906
|
// Sign the response as a JACS document via JACS
|
|
758
|
-
const signed = (0, signing_js_1.signResponse)(body, this.
|
|
907
|
+
const signed = (0, signing_js_1.signResponse)(body, this.activeSigner(), this.jacsId, this.agent);
|
|
759
908
|
const response = await this.fetchWithRetry(url, {
|
|
760
909
|
method: 'POST',
|
|
761
910
|
headers: this.buildAuthHeaders(),
|
|
@@ -1012,7 +1161,7 @@ class HaiClient {
|
|
|
1012
1161
|
* @returns Registration result
|
|
1013
1162
|
*/
|
|
1014
1163
|
async registerNewAgent(agentName, options) {
|
|
1015
|
-
const { mkdtemp, readFile: readF } = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
1164
|
+
const { mkdtemp, readFile: readF, rm } = await Promise.resolve().then(() => __importStar(require('node:fs/promises')));
|
|
1016
1165
|
const { join } = await Promise.resolve().then(() => __importStar(require('node:path')));
|
|
1017
1166
|
const { tmpdir } = await Promise.resolve().then(() => __importStar(require('node:os')));
|
|
1018
1167
|
// Generate a new JACS agent with keys via JACS core
|
|
@@ -1020,88 +1169,93 @@ class HaiClient {
|
|
|
1020
1169
|
const keyDir = join(tempDir, 'keys');
|
|
1021
1170
|
const dataDir = join(tempDir, 'data');
|
|
1022
1171
|
const passphrase = process.env.JACS_PRIVATE_KEY_PASSWORD ?? 'register-temp';
|
|
1023
|
-
const resultJson = (0, jacs_1.createAgentSync)(agentName, passphrase, 'pq2025', dataDir, keyDir, join(tempDir, 'jacs.config.json'), null, options.description ?? 'Agent registered via Node SDK', options.domain ?? null, null);
|
|
1024
|
-
const createResult = JSON.parse(resultJson);
|
|
1025
|
-
const pubKeyPath = createResult.public_key_path || join(keyDir, 'jacs.public.pem');
|
|
1026
|
-
const publicKeyPem = normalizeKeyText(await readF(pubKeyPath), 'PUBLIC KEY');
|
|
1027
|
-
// Load the new agent for signing
|
|
1028
|
-
const tempAgent = new jacs_1.JacsAgent();
|
|
1029
|
-
const tempConfigPath = createResult.config_path || join(tempDir, 'jacs.config.json');
|
|
1030
|
-
const { resolve } = await Promise.resolve().then(() => __importStar(require('node:path')));
|
|
1031
|
-
let signingAgent = tempAgent;
|
|
1032
1172
|
try {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
});
|
|
1075
|
-
const data = await response.json();
|
|
1076
|
-
if (!options.quiet) {
|
|
1077
|
-
console.log(`\nAgent created and submitted for registration!`);
|
|
1078
|
-
console.log(` -> Check your email (${options.ownerEmail}) for a verification link`);
|
|
1079
|
-
console.log(` -> Click the link and log into hai.ai to complete registration`);
|
|
1080
|
-
console.log(` -> After verification, claim a @hai.ai username with:`);
|
|
1081
|
-
console.log(` client.claimUsername('${data.agent_id || ''}', 'my-agent')`);
|
|
1082
|
-
console.log(` -> Save your config and private key to a secure, access-controlled location`);
|
|
1173
|
+
const resultJson = (0, jacs_1.createAgentSync)(agentName, passphrase, 'pq2025', dataDir, keyDir, join(tempDir, 'jacs.config.json'), null, options.description ?? 'Agent registered via Node SDK', options.domain ?? null, null);
|
|
1174
|
+
const createResult = JSON.parse(resultJson);
|
|
1175
|
+
const pubKeyPath = createResult.public_key_path || join(keyDir, 'jacs.public.pem');
|
|
1176
|
+
const publicKeyPem = normalizeKeyText(await readF(pubKeyPath), 'PUBLIC KEY');
|
|
1177
|
+
// Load the new agent for signing
|
|
1178
|
+
const tempAgent = new jacs_1.JacsAgent();
|
|
1179
|
+
const tempConfigPath = createResult.config_path || join(tempDir, 'jacs.config.json');
|
|
1180
|
+
const { resolve } = await Promise.resolve().then(() => __importStar(require('node:path')));
|
|
1181
|
+
let signingAgent = tempAgent;
|
|
1182
|
+
try {
|
|
1183
|
+
await withPrivateKeyPassphrase(passphrase || undefined, () => tempAgent.load(resolve(tempConfigPath)));
|
|
1184
|
+
}
|
|
1185
|
+
catch {
|
|
1186
|
+
tempAgent.ephemeralSync('pq2025');
|
|
1187
|
+
signingAgent = tempAgent;
|
|
1188
|
+
}
|
|
1189
|
+
// Build minimal JACS agent document
|
|
1190
|
+
const agentDoc = {
|
|
1191
|
+
jacsId: agentName,
|
|
1192
|
+
jacsVersion: '1.0.0',
|
|
1193
|
+
jacsSignature: {
|
|
1194
|
+
agentID: agentName,
|
|
1195
|
+
date: new Date().toISOString(),
|
|
1196
|
+
},
|
|
1197
|
+
jacsPublicKey: publicKeyPem,
|
|
1198
|
+
name: agentName,
|
|
1199
|
+
description: options.description ?? 'Agent registered via Node SDK',
|
|
1200
|
+
capabilities: ['mediation'],
|
|
1201
|
+
version: '1.0.0',
|
|
1202
|
+
};
|
|
1203
|
+
// Sign canonical JSON via JACS
|
|
1204
|
+
const canonical = (0, signing_js_1.canonicalJson)(agentDoc);
|
|
1205
|
+
const signature = signingAgent.signStringSync(canonical);
|
|
1206
|
+
agentDoc.jacsSignature.signature = signature;
|
|
1207
|
+
const url = this.makeUrl('/api/v1/agents/register');
|
|
1208
|
+
const publicKeyB64 = Buffer.from(publicKeyPem, 'utf-8').toString('base64');
|
|
1209
|
+
const body = {
|
|
1210
|
+
agent_json: JSON.stringify(agentDoc),
|
|
1211
|
+
public_key: publicKeyB64,
|
|
1212
|
+
owner_email: options.ownerEmail,
|
|
1213
|
+
};
|
|
1083
1214
|
if (options.domain) {
|
|
1084
|
-
|
|
1085
|
-
console.log(`\n--- DNS Setup Instructions ---`);
|
|
1086
|
-
console.log(`Add this TXT record to your domain '${options.domain}':`);
|
|
1087
|
-
console.log(` Name: _jacs.${options.domain}`);
|
|
1088
|
-
console.log(` Type: TXT`);
|
|
1089
|
-
console.log(` Value: sha256:${pubKeyHash}`);
|
|
1090
|
-
console.log(`DNS verification enables the pro tier.\n`);
|
|
1215
|
+
body.domain = options.domain;
|
|
1091
1216
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1217
|
+
if (options.description) {
|
|
1218
|
+
body.description = options.description;
|
|
1219
|
+
}
|
|
1220
|
+
const response = await this.fetchWithRetry(url, {
|
|
1221
|
+
method: 'POST',
|
|
1222
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1223
|
+
body: JSON.stringify(body),
|
|
1224
|
+
});
|
|
1225
|
+
const data = await response.json();
|
|
1226
|
+
if (!options.quiet) {
|
|
1227
|
+
console.log(`\nAgent created and submitted for registration!`);
|
|
1228
|
+
console.log(` -> Check your email (${options.ownerEmail}) for a verification link`);
|
|
1229
|
+
console.log(` -> Click the link and log into hai.ai to complete registration`);
|
|
1230
|
+
console.log(` -> After verification, claim a @hai.ai username with:`);
|
|
1231
|
+
console.log(` client.claimUsername('${data.agent_id || ''}', 'my-agent')`);
|
|
1232
|
+
console.log(` -> Save your config and private key to a secure, access-controlled location`);
|
|
1233
|
+
if (options.domain) {
|
|
1234
|
+
const pubKeyHash = (0, jacs_1.hashString)(publicKeyPem);
|
|
1235
|
+
console.log(`\n--- DNS Setup Instructions ---`);
|
|
1236
|
+
console.log(`Add this TXT record to your domain '${options.domain}':`);
|
|
1237
|
+
console.log(` Name: _jacs.${options.domain}`);
|
|
1238
|
+
console.log(` Type: TXT`);
|
|
1239
|
+
console.log(` Value: sha256:${pubKeyHash}`);
|
|
1240
|
+
console.log(`DNS verification enables the pro tier.\n`);
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
console.log();
|
|
1244
|
+
}
|
|
1094
1245
|
}
|
|
1246
|
+
return {
|
|
1247
|
+
success: true,
|
|
1248
|
+
agentId: data.agent_id || data.agentId || '',
|
|
1249
|
+
jacsId: data.jacs_id || data.jacsId || agentDoc.jacsId || '',
|
|
1250
|
+
haiSignature: data.hai_signature || data.haiSignature || '',
|
|
1251
|
+
registrationId: data.registration_id || data.registrationId || '',
|
|
1252
|
+
registeredAt: data.registered_at || data.registeredAt || '',
|
|
1253
|
+
rawResponse: data,
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
finally {
|
|
1257
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => { });
|
|
1095
1258
|
}
|
|
1096
|
-
return {
|
|
1097
|
-
success: true,
|
|
1098
|
-
agentId: data.agent_id || data.agentId || '',
|
|
1099
|
-
jacsId: data.jacs_id || data.jacsId || agentDoc.jacsId || '',
|
|
1100
|
-
haiSignature: data.hai_signature || data.haiSignature || '',
|
|
1101
|
-
registrationId: data.registration_id || data.registrationId || '',
|
|
1102
|
-
registeredAt: data.registered_at || data.registeredAt || '',
|
|
1103
|
-
rawResponse: data,
|
|
1104
|
-
};
|
|
1105
1259
|
}
|
|
1106
1260
|
// ---------------------------------------------------------------------------
|
|
1107
1261
|
// testConnection()
|
|
@@ -1431,7 +1585,7 @@ class HaiClient {
|
|
|
1431
1585
|
* @returns Signed JACS document envelope
|
|
1432
1586
|
*/
|
|
1433
1587
|
signBenchmarkResult(benchmarkResult) {
|
|
1434
|
-
return (0, signing_js_1.signResponse)(benchmarkResult, this.
|
|
1588
|
+
return (0, signing_js_1.signResponse)(benchmarkResult, this.activeSigner(), this.jacsId, this.agent);
|
|
1435
1589
|
}
|
|
1436
1590
|
// ---------------------------------------------------------------------------
|
|
1437
1591
|
// benchmark() -- legacy suite-based
|