@khirby/plugin-pokelo 1.0.1 → 1.1.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/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
-
import type {
|
|
2
|
+
import type { KnowledgeContextLike, PokeloFetchOpts } from '../../../packages/plugin-host/src';
|
|
3
3
|
import { PokeloSettingsService } from './pokelo-settings.service';
|
|
4
4
|
|
|
5
5
|
const SNIPPET_LIMIT_TOTAL = 8;
|
|
@@ -15,7 +15,7 @@ type McpToolResult = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
@Injectable()
|
|
18
|
-
export class PokeloContextService implements
|
|
18
|
+
export class PokeloContextService implements KnowledgeContextLike {
|
|
19
19
|
private readonly logger = new Logger(PokeloContextService.name);
|
|
20
20
|
|
|
21
21
|
constructor(private readonly settings: PokeloSettingsService) {}
|
package/src/pokelo-crypto.ts
CHANGED
|
@@ -1,60 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function getKey(): Buffer {
|
|
8
|
-
const raw = process.env.POKELO_SECRETS_KEY?.trim();
|
|
9
|
-
if (!raw) {
|
|
10
|
-
throw new Error('POKELO_SECRETS_KEY is not set');
|
|
11
|
-
}
|
|
12
|
-
let buf: Buffer;
|
|
13
|
-
if (/^[0-9a-fA-F]{64}$/.test(raw)) {
|
|
14
|
-
buf = Buffer.from(raw, 'hex');
|
|
15
|
-
} else {
|
|
16
|
-
buf = Buffer.from(raw, 'base64');
|
|
17
|
-
}
|
|
18
|
-
if (buf.length !== 32) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
"POKELO_SECRETS_KEY must be 32 bytes as hex (64 chars) or base64 — generate with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"",
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
return buf;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function isPokeloSecretsKeyConfigured(): boolean {
|
|
27
|
-
try {
|
|
28
|
-
getKey();
|
|
29
|
-
return true;
|
|
30
|
-
} catch {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Encrypts plaintext using AES-256-GCM.
|
|
37
|
-
* Returns a base64 string: iv(12) + ciphertext + tag(16)
|
|
38
|
-
*/
|
|
39
|
-
export function encrypt(plaintext: string): string {
|
|
40
|
-
const key = getKey();
|
|
41
|
-
const iv = randomBytes(IV_BYTES);
|
|
42
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
43
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
44
|
-
const tag = cipher.getAuthTag();
|
|
45
|
-
return Buffer.concat([iv, encrypted, tag]).toString('base64');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Decrypts a base64 blob produced by `encrypt`.
|
|
50
|
-
*/
|
|
51
|
-
export function decrypt(ciphertext: string): string {
|
|
52
|
-
const key = getKey();
|
|
53
|
-
const buf = Buffer.from(ciphertext, 'base64');
|
|
54
|
-
const iv = buf.subarray(0, IV_BYTES);
|
|
55
|
-
const tag = buf.subarray(buf.length - TAG_BYTES);
|
|
56
|
-
const encrypted = buf.subarray(IV_BYTES, buf.length - TAG_BYTES);
|
|
57
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
58
|
-
decipher.setAuthTag(tag);
|
|
59
|
-
return decipher.update(encrypted) + decipher.final('utf8');
|
|
60
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
decrypt,
|
|
3
|
+
encrypt,
|
|
4
|
+
isInstanceSecretsKeyConfigured as isPokeloSecretsKeyConfigured,
|
|
5
|
+
} from '../../../packages/plugin-host/src/instance-secrets';
|
|
@@ -99,7 +99,7 @@ export class PokeloSettingsService {
|
|
|
99
99
|
let encryptedToken: string | undefined = undefined;
|
|
100
100
|
if (dto.token !== undefined && dto.token.trim()) {
|
|
101
101
|
if (!isPokeloSecretsKeyConfigured()) {
|
|
102
|
-
throw AppException.badRequest('
|
|
102
|
+
throw AppException.badRequest('KHIRBY_SECRETS_KEY is not configured');
|
|
103
103
|
}
|
|
104
104
|
encryptedToken = encrypt(dto.token.trim());
|
|
105
105
|
}
|
package/src/pokelo.module.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Global, Module } from '@nestjs/common';
|
|
2
|
-
import { POKELO_CONTEXT_SERVICE } from '../../../packages/plugin-host/src';
|
|
2
|
+
import { KNOWLEDGE_CONTEXT, POKELO_CONTEXT_SERVICE } from '../../../packages/plugin-host/src';
|
|
3
3
|
import { PokeloSettingsService } from './pokelo-settings.service';
|
|
4
4
|
import { PokeloContextService } from './pokelo-context.service';
|
|
5
5
|
import { PokeloSettingsController } from './pokelo-settings.controller';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* @Global so
|
|
9
|
-
* POKELO_CONTEXT_SERVICE
|
|
8
|
+
* @Global so sibling plugins can @Optional()-inject KNOWLEDGE_CONTEXT (ADR-0047).
|
|
9
|
+
* POKELO_CONTEXT_SERVICE stays as a second provide until published consumers bump.
|
|
10
10
|
*/
|
|
11
11
|
@Global()
|
|
12
12
|
@Module({
|
|
@@ -14,8 +14,9 @@ import { PokeloSettingsController } from './pokelo-settings.controller';
|
|
|
14
14
|
providers: [
|
|
15
15
|
PokeloSettingsService,
|
|
16
16
|
PokeloContextService,
|
|
17
|
+
{ provide: KNOWLEDGE_CONTEXT, useExisting: PokeloContextService },
|
|
17
18
|
{ provide: POKELO_CONTEXT_SERVICE, useExisting: PokeloContextService },
|
|
18
19
|
],
|
|
19
|
-
exports: [POKELO_CONTEXT_SERVICE],
|
|
20
|
+
exports: [KNOWLEDGE_CONTEXT, POKELO_CONTEXT_SERVICE],
|
|
20
21
|
})
|
|
21
22
|
export class PokeloModule {}
|
package/src/pokelo.spec.ts
CHANGED
|
@@ -7,10 +7,16 @@ describe('pokelo-crypto', () => {
|
|
|
7
7
|
const HEX_KEY = 'c'.repeat(64);
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
|
+
delete process.env.KHIRBY_SECRETS_KEY;
|
|
11
|
+
delete process.env.MAIL_SECRETS_KEY;
|
|
12
|
+
delete process.env.AI_COMPOSE_SECRETS_KEY;
|
|
10
13
|
process.env.POKELO_SECRETS_KEY = HEX_KEY;
|
|
11
14
|
});
|
|
12
15
|
|
|
13
16
|
afterEach(() => {
|
|
17
|
+
delete process.env.KHIRBY_SECRETS_KEY;
|
|
18
|
+
delete process.env.MAIL_SECRETS_KEY;
|
|
19
|
+
delete process.env.AI_COMPOSE_SECRETS_KEY;
|
|
14
20
|
delete process.env.POKELO_SECRETS_KEY;
|
|
15
21
|
});
|
|
16
22
|
|
|
@@ -26,9 +32,15 @@ describe('pokelo-crypto', () => {
|
|
|
26
32
|
expect(isPokeloSecretsKeyConfigured()).toBe(false);
|
|
27
33
|
});
|
|
28
34
|
|
|
35
|
+
it('isPokeloSecretsKeyConfigured returns true when KHIRBY_SECRETS_KEY is set', () => {
|
|
36
|
+
delete process.env.POKELO_SECRETS_KEY;
|
|
37
|
+
process.env.KHIRBY_SECRETS_KEY = HEX_KEY;
|
|
38
|
+
expect(isPokeloSecretsKeyConfigured()).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
29
41
|
it('throws on missing key at encrypt time', () => {
|
|
30
42
|
delete process.env.POKELO_SECRETS_KEY;
|
|
31
|
-
expect(() => encrypt('anything')).toThrow('
|
|
43
|
+
expect(() => encrypt('anything')).toThrow('KHIRBY_SECRETS_KEY is not set');
|
|
32
44
|
});
|
|
33
45
|
});
|
|
34
46
|
|